| author | |
| committer | |
| log | d7049fc8e0709619b8aa6766b37abeae946703b2 |
| tree | fd575717d5ca9e585b7d94e2028f80614dbf6c2d |
| parent | 8b9434871ea437840d25f073b945466359f402f9 |
| parent | 9ada7638a5dbb535ba37223a14478691dd60cf6a |
| signature |
Rework AST memory layout for better memory usage and performance42 files changed, 20800 insertions(+), 16573 deletions(-)
CMakeLists.txt+2-1| ... | @@ -370,7 +370,6 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -370,7 +370,6 @@ set(ZIG_STAGE2_SOURCES |
| 370 | "${CMAKE_SOURCE_DIR}/lib/std/heap.zig" | 370 | "${CMAKE_SOURCE_DIR}/lib/std/heap.zig" |
| 371 | "${CMAKE_SOURCE_DIR}/lib/std/heap/arena_allocator.zig" | 371 | "${CMAKE_SOURCE_DIR}/lib/std/heap/arena_allocator.zig" |
| 372 | "${CMAKE_SOURCE_DIR}/lib/std/io.zig" | 372 | "${CMAKE_SOURCE_DIR}/lib/std/io.zig" |
| 373 | "${CMAKE_SOURCE_DIR}/lib/std/io/auto_indenting_stream.zig" | ||
| 374 | "${CMAKE_SOURCE_DIR}/lib/std/io/buffered_atomic_file.zig" | 373 | "${CMAKE_SOURCE_DIR}/lib/std/io/buffered_atomic_file.zig" |
| 375 | "${CMAKE_SOURCE_DIR}/lib/std/io/buffered_writer.zig" | 374 | "${CMAKE_SOURCE_DIR}/lib/std/io/buffered_writer.zig" |
| 376 | "${CMAKE_SOURCE_DIR}/lib/std/io/change_detection_stream.zig" | 375 | "${CMAKE_SOURCE_DIR}/lib/std/io/change_detection_stream.zig" |
| ... | @@ -408,6 +407,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -408,6 +407,7 @@ set(ZIG_STAGE2_SOURCES |
| 408 | "${CMAKE_SOURCE_DIR}/lib/std/meta.zig" | 407 | "${CMAKE_SOURCE_DIR}/lib/std/meta.zig" |
| 409 | "${CMAKE_SOURCE_DIR}/lib/std/meta/trailer_flags.zig" | 408 | "${CMAKE_SOURCE_DIR}/lib/std/meta/trailer_flags.zig" |
| 410 | "${CMAKE_SOURCE_DIR}/lib/std/meta/trait.zig" | 409 | "${CMAKE_SOURCE_DIR}/lib/std/meta/trait.zig" |
| 410 | "${CMAKE_SOURCE_DIR}/lib/std/multi_array_list.zig" | ||
| 411 | "${CMAKE_SOURCE_DIR}/lib/std/os.zig" | 411 | "${CMAKE_SOURCE_DIR}/lib/std/os.zig" |
| 412 | "${CMAKE_SOURCE_DIR}/lib/std/os/bits.zig" | 412 | "${CMAKE_SOURCE_DIR}/lib/std/os/bits.zig" |
| 413 | "${CMAKE_SOURCE_DIR}/lib/std/os/bits/linux.zig" | 413 | "${CMAKE_SOURCE_DIR}/lib/std/os/bits/linux.zig" |
| ... | @@ -573,6 +573,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -573,6 +573,7 @@ set(ZIG_STAGE2_SOURCES |
| 573 | "${CMAKE_SOURCE_DIR}/src/target.zig" | 573 | "${CMAKE_SOURCE_DIR}/src/target.zig" |
| 574 | "${CMAKE_SOURCE_DIR}/src/tracy.zig" | 574 | "${CMAKE_SOURCE_DIR}/src/tracy.zig" |
| 575 | "${CMAKE_SOURCE_DIR}/src/translate_c.zig" | 575 | "${CMAKE_SOURCE_DIR}/src/translate_c.zig" |
| 576 | "${CMAKE_SOURCE_DIR}/src/translate_c/ast.zig" | ||
| 576 | "${CMAKE_SOURCE_DIR}/src/type.zig" | 577 | "${CMAKE_SOURCE_DIR}/src/type.zig" |
| 577 | "${CMAKE_SOURCE_DIR}/src/value.zig" | 578 | "${CMAKE_SOURCE_DIR}/src/value.zig" |
| 578 | "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig" | 579 | "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig" |
README.md+1-1| ... | @@ -5,7 +5,7 @@ A general-purpose programming language and toolchain for maintaining | ... | @@ -5,7 +5,7 @@ A general-purpose programming language and toolchain for maintaining |
| 5 | 5 | ||
| 6 | ## Resources | 6 | ## Resources |
| 7 | 7 | ||
| 8 | * [Introduction](https://ziglang.org/#Introduction) | 8 | * [Introduction](https://ziglang.org/learn/#introduction) |
| 9 | * [Download & Documentation](https://ziglang.org/download) | 9 | * [Download & Documentation](https://ziglang.org/download) |
| 10 | * [Chapter 0 - Getting Started | ZigLearn.org](https://ziglearn.org/) | 10 | * [Chapter 0 - Getting Started | ZigLearn.org](https://ziglearn.org/) |
| 11 | * [Community](https://github.com/ziglang/zig/wiki/Community) | 11 | * [Community](https://github.com/ziglang/zig/wiki/Community) |
build.zig+2| ... | @@ -77,10 +77,12 @@ pub fn build(b: *Builder) !void { | ... | @@ -77,10 +77,12 @@ pub fn build(b: *Builder) !void { |
| 77 | 77 | ||
| 78 | const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); | 78 | const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); |
| 79 | const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse enable_llvm; | 79 | const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse enable_llvm; |
| 80 | const strip = b.option(bool, "strip", "Omit debug information") orelse false; | ||
| 80 | 81 | ||
| 81 | const main_file = if (is_stage1) "src/stage1.zig" else "src/main.zig"; | 82 | const main_file = if (is_stage1) "src/stage1.zig" else "src/main.zig"; |
| 82 | 83 | ||
| 83 | var exe = b.addExecutable("zig", main_file); | 84 | var exe = b.addExecutable("zig", main_file); |
| 85 | exe.strip = strip; | ||
| 84 | exe.install(); | 86 | exe.install(); |
| 85 | exe.setBuildMode(mode); | 87 | exe.setBuildMode(mode); |
| 86 | exe.setTarget(target); | 88 | exe.setTarget(target); |
doc/docgen.zig+134-121| ... | @@ -781,106 +781,119 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: anytype, source_token: | ... | @@ -781,106 +781,119 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: anytype, source_token: |
| 781 | next_tok_is_fn = false; | 781 | next_tok_is_fn = false; |
| 782 | 782 | ||
| 783 | const token = tokenizer.next(); | 783 | const token = tokenizer.next(); |
| 784 | if (mem.indexOf(u8, src[index..token.loc.start], "//")) |comment_start_off| { | ||
| 785 | // render one comment | ||
| 786 | const comment_start = index + comment_start_off; | ||
| 787 | const comment_end_off = mem.indexOf(u8, src[comment_start .. token.loc.start], "\n"); | ||
| 788 | const comment_end = if (comment_end_off) |o| comment_start + o else token.loc.start; | ||
| 789 | |||
| 790 | try writeEscaped(out, src[index..comment_start]); | ||
| 791 | try out.writeAll("<span class=\"tok-comment\">"); | ||
| 792 | try writeEscaped(out, src[comment_start .. comment_end]); | ||
| 793 | try out.writeAll("</span>"); | ||
| 794 | index = comment_end; | ||
| 795 | tokenizer.index = index; | ||
| 796 | continue; | ||
| 797 | } | ||
| 798 | |||
| 784 | try writeEscaped(out, src[index..token.loc.start]); | 799 | try writeEscaped(out, src[index..token.loc.start]); |
| 785 | switch (token.id) { | 800 | switch (token.tag) { |
| 786 | .Eof => break, | 801 | .eof => break, |
| 787 | 802 | ||
| 788 | .Keyword_align, | 803 | .keyword_align, |
| 789 | .Keyword_and, | 804 | .keyword_and, |
| 790 | .Keyword_asm, | 805 | .keyword_asm, |
| 791 | .Keyword_async, | 806 | .keyword_async, |
| 792 | .Keyword_await, | 807 | .keyword_await, |
| 793 | .Keyword_break, | 808 | .keyword_break, |
| 794 | .Keyword_catch, | 809 | .keyword_catch, |
| 795 | .Keyword_comptime, | 810 | .keyword_comptime, |
| 796 | .Keyword_const, | 811 | .keyword_const, |
| 797 | .Keyword_continue, | 812 | .keyword_continue, |
| 798 | .Keyword_defer, | 813 | .keyword_defer, |
| 799 | .Keyword_else, | 814 | .keyword_else, |
| 800 | .Keyword_enum, | 815 | .keyword_enum, |
| 801 | .Keyword_errdefer, | 816 | .keyword_errdefer, |
| 802 | .Keyword_error, | 817 | .keyword_error, |
| 803 | .Keyword_export, | 818 | .keyword_export, |
| 804 | .Keyword_extern, | 819 | .keyword_extern, |
| 805 | .Keyword_for, | 820 | .keyword_for, |
| 806 | .Keyword_if, | 821 | .keyword_if, |
| 807 | .Keyword_inline, | 822 | .keyword_inline, |
| 808 | .Keyword_noalias, | 823 | .keyword_noalias, |
| 809 | .Keyword_noinline, | 824 | .keyword_noinline, |
| 810 | .Keyword_nosuspend, | 825 | .keyword_nosuspend, |
| 811 | .Keyword_opaque, | 826 | .keyword_opaque, |
| 812 | .Keyword_or, | 827 | .keyword_or, |
| 813 | .Keyword_orelse, | 828 | .keyword_orelse, |
| 814 | .Keyword_packed, | 829 | .keyword_packed, |
| 815 | .Keyword_anyframe, | 830 | .keyword_anyframe, |
| 816 | .Keyword_pub, | 831 | .keyword_pub, |
| 817 | .Keyword_resume, | 832 | .keyword_resume, |
| 818 | .Keyword_return, | 833 | .keyword_return, |
| 819 | .Keyword_linksection, | 834 | .keyword_linksection, |
| 820 | .Keyword_callconv, | 835 | .keyword_callconv, |
| 821 | .Keyword_struct, | 836 | .keyword_struct, |
| 822 | .Keyword_suspend, | 837 | .keyword_suspend, |
| 823 | .Keyword_switch, | 838 | .keyword_switch, |
| 824 | .Keyword_test, | 839 | .keyword_test, |
| 825 | .Keyword_threadlocal, | 840 | .keyword_threadlocal, |
| 826 | .Keyword_try, | 841 | .keyword_try, |
| 827 | .Keyword_union, | 842 | .keyword_union, |
| 828 | .Keyword_unreachable, | 843 | .keyword_unreachable, |
| 829 | .Keyword_usingnamespace, | 844 | .keyword_usingnamespace, |
| 830 | .Keyword_var, | 845 | .keyword_var, |
| 831 | .Keyword_volatile, | 846 | .keyword_volatile, |
| 832 | .Keyword_allowzero, | 847 | .keyword_allowzero, |
| 833 | .Keyword_while, | 848 | .keyword_while, |
| 834 | .Keyword_anytype, | 849 | .keyword_anytype, |
| 835 | => { | 850 | => { |
| 836 | try out.writeAll("<span class=\"tok-kw\">"); | 851 | try out.writeAll("<span class=\"tok-kw\">"); |
| 837 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | 852 | try writeEscaped(out, src[token.loc.start..token.loc.end]); |
| 838 | try out.writeAll("</span>"); | 853 | try out.writeAll("</span>"); |
| 839 | }, | 854 | }, |
| 840 | 855 | ||
| 841 | .Keyword_fn => { | 856 | .keyword_fn => { |
| 842 | try out.writeAll("<span class=\"tok-kw\">"); | 857 | try out.writeAll("<span class=\"tok-kw\">"); |
| 843 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | 858 | try writeEscaped(out, src[token.loc.start..token.loc.end]); |
| 844 | try out.writeAll("</span>"); | 859 | try out.writeAll("</span>"); |
| 845 | next_tok_is_fn = true; | 860 | next_tok_is_fn = true; |
| 846 | }, | 861 | }, |
| 847 | 862 | ||
| 848 | .Keyword_undefined, | 863 | .keyword_undefined, |
| 849 | .Keyword_null, | 864 | .keyword_null, |
| 850 | .Keyword_true, | 865 | .keyword_true, |
| 851 | .Keyword_false, | 866 | .keyword_false, |
| 852 | => { | 867 | => { |
| 853 | try out.writeAll("<span class=\"tok-null\">"); | 868 | try out.writeAll("<span class=\"tok-null\">"); |
| 854 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | 869 | try writeEscaped(out, src[token.loc.start..token.loc.end]); |
| 855 | try out.writeAll("</span>"); | 870 | try out.writeAll("</span>"); |
| 856 | }, | 871 | }, |
| 857 | 872 | ||
| 858 | .StringLiteral, | 873 | .string_literal, |
| 859 | .MultilineStringLiteralLine, | 874 | .multiline_string_literal_line, |
| 860 | .CharLiteral, | 875 | .char_literal, |
| 861 | => { | 876 | => { |
| 862 | try out.writeAll("<span class=\"tok-str\">"); | 877 | try out.writeAll("<span class=\"tok-str\">"); |
| 863 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | 878 | try writeEscaped(out, src[token.loc.start..token.loc.end]); |
| 864 | try out.writeAll("</span>"); | 879 | try out.writeAll("</span>"); |
| 865 | }, | 880 | }, |
| 866 | 881 | ||
| 867 | .Builtin => { | 882 | .builtin => { |
| 868 | try out.writeAll("<span class=\"tok-builtin\">"); | 883 | try out.writeAll("<span class=\"tok-builtin\">"); |
| 869 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | 884 | try writeEscaped(out, src[token.loc.start..token.loc.end]); |
| 870 | try out.writeAll("</span>"); | 885 | try out.writeAll("</span>"); |
| 871 | }, | 886 | }, |
| 872 | 887 | ||
| 873 | .LineComment, | 888 | .doc_comment, |
| 874 | .DocComment, | 889 | .container_doc_comment, |
| 875 | .ContainerDocComment, | ||
| 876 | .ShebangLine, | ||
| 877 | => { | 890 | => { |
| 878 | try out.writeAll("<span class=\"tok-comment\">"); | 891 | try out.writeAll("<span class=\"tok-comment\">"); |
| 879 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | 892 | try writeEscaped(out, src[token.loc.start..token.loc.end]); |
| 880 | try out.writeAll("</span>"); | 893 | try out.writeAll("</span>"); |
| 881 | }, | 894 | }, |
| 882 | 895 | ||
| 883 | .Identifier => { | 896 | .identifier => { |
| 884 | if (prev_tok_was_fn) { | 897 | if (prev_tok_was_fn) { |
| 885 | try out.writeAll("<span class=\"tok-fn\">"); | 898 | try out.writeAll("<span class=\"tok-fn\">"); |
| 886 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | 899 | try writeEscaped(out, src[token.loc.start..token.loc.end]); |
| ... | @@ -908,71 +921,71 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: anytype, source_token: | ... | @@ -908,71 +921,71 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: anytype, source_token: |
| 908 | } | 921 | } |
| 909 | }, | 922 | }, |
| 910 | 923 | ||
| 911 | .IntegerLiteral, | 924 | .integer_literal, |
| 912 | .FloatLiteral, | 925 | .float_literal, |
| 913 | => { | 926 | => { |
| 914 | try out.writeAll("<span class=\"tok-number\">"); | 927 | try out.writeAll("<span class=\"tok-number\">"); |
| 915 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | 928 | try writeEscaped(out, src[token.loc.start..token.loc.end]); |
| 916 | try out.writeAll("</span>"); | 929 | try out.writeAll("</span>"); |
| 917 | }, | 930 | }, |
| 918 | 931 | ||
| 919 | .Bang, | 932 | .bang, |
| 920 | .Pipe, | 933 | .pipe, |
| 921 | .PipePipe, | 934 | .pipe_pipe, |
| 922 | .PipeEqual, | 935 | .pipe_equal, |
| 923 | .Equal, | 936 | .equal, |
| 924 | .EqualEqual, | 937 | .equal_equal, |
| 925 | .EqualAngleBracketRight, | 938 | .equal_angle_bracket_right, |
| 926 | .BangEqual, | 939 | .bang_equal, |
| 927 | .LParen, | 940 | .l_paren, |
| 928 | .RParen, | 941 | .r_paren, |
| 929 | .Semicolon, | 942 | .semicolon, |
| 930 | .Percent, | 943 | .percent, |
| 931 | .PercentEqual, | 944 | .percent_equal, |
| 932 | .LBrace, | 945 | .l_brace, |
| 933 | .RBrace, | 946 | .r_brace, |
| 934 | .LBracket, | 947 | .l_bracket, |
| 935 | .RBracket, | 948 | .r_bracket, |
| 936 | .Period, | 949 | .period, |
| 937 | .PeriodAsterisk, | 950 | .period_asterisk, |
| 938 | .Ellipsis2, | 951 | .ellipsis2, |
| 939 | .Ellipsis3, | 952 | .ellipsis3, |
| 940 | .Caret, | 953 | .caret, |
| 941 | .CaretEqual, | 954 | .caret_equal, |
| 942 | .Plus, | 955 | .plus, |
| 943 | .PlusPlus, | 956 | .plus_plus, |
| 944 | .PlusEqual, | 957 | .plus_equal, |
| 945 | .PlusPercent, | 958 | .plus_percent, |
| 946 | .PlusPercentEqual, | 959 | .plus_percent_equal, |
| 947 | .Minus, | 960 | .minus, |
| 948 | .MinusEqual, | 961 | .minus_equal, |
| 949 | .MinusPercent, | 962 | .minus_percent, |
| 950 | .MinusPercentEqual, | 963 | .minus_percent_equal, |
| 951 | .Asterisk, | 964 | .asterisk, |
| 952 | .AsteriskEqual, | 965 | .asterisk_equal, |
| 953 | .AsteriskAsterisk, | 966 | .asterisk_asterisk, |
| 954 | .AsteriskPercent, | 967 | .asterisk_percent, |
| 955 | .AsteriskPercentEqual, | 968 | .asterisk_percent_equal, |
| 956 | .Arrow, | 969 | .arrow, |
| 957 | .Colon, | 970 | .colon, |
| 958 | .Slash, | 971 | .slash, |
| 959 | .SlashEqual, | 972 | .slash_equal, |
| 960 | .Comma, | 973 | .comma, |
| 961 | .Ampersand, | 974 | .ampersand, |
| 962 | .AmpersandEqual, | 975 | .ampersand_equal, |
| 963 | .QuestionMark, | 976 | .question_mark, |
| 964 | .AngleBracketLeft, | 977 | .angle_bracket_left, |
| 965 | .AngleBracketLeftEqual, | 978 | .angle_bracket_left_equal, |
| 966 | .AngleBracketAngleBracketLeft, | 979 | .angle_bracket_angle_bracket_left, |
| 967 | .AngleBracketAngleBracketLeftEqual, | 980 | .angle_bracket_angle_bracket_left_equal, |
| 968 | .AngleBracketRight, | 981 | .angle_bracket_right, |
| 969 | .AngleBracketRightEqual, | 982 | .angle_bracket_right_equal, |
| 970 | .AngleBracketAngleBracketRight, | 983 | .angle_bracket_angle_bracket_right, |
| 971 | .AngleBracketAngleBracketRightEqual, | 984 | .angle_bracket_angle_bracket_right_equal, |
| 972 | .Tilde, | 985 | .tilde, |
| 973 | => try writeEscaped(out, src[token.loc.start..token.loc.end]), | 986 | => try writeEscaped(out, src[token.loc.start..token.loc.end]), |
| 974 | 987 | ||
| 975 | .Invalid, .Invalid_ampersands, .Invalid_periodasterisks => return parseError( | 988 | .invalid, .invalid_ampersands, .invalid_periodasterisks => return parseError( |
| 976 | docgen_tokenizer, | 989 | docgen_tokenizer, |
| 977 | source_token, | 990 | source_token, |
| 978 | "syntax error", | 991 | "syntax error", |
lib/std/heap/general_purpose_allocator.zig+27-1| ... | @@ -98,7 +98,7 @@ | ... | @@ -98,7 +98,7 @@ |
| 98 | //! in a `std.HashMap` using the backing allocator. | 98 | //! in a `std.HashMap` using the backing allocator. |
| 99 | 99 | ||
| 100 | const std = @import("std"); | 100 | const std = @import("std"); |
| 101 | const log = std.log.scoped(.std); | 101 | const log = std.log.scoped(.gpa); |
| 102 | const math = std.math; | 102 | const math = std.math; |
| 103 | const assert = std.debug.assert; | 103 | const assert = std.debug.assert; |
| 104 | const mem = std.mem; | 104 | const mem = std.mem; |
| ... | @@ -162,6 +162,9 @@ pub const Config = struct { | ... | @@ -162,6 +162,9 @@ pub const Config = struct { |
| 162 | /// logged error messages with stack trace details. The downside is that every allocation | 162 | /// logged error messages with stack trace details. The downside is that every allocation |
| 163 | /// will be leaked! | 163 | /// will be leaked! |
| 164 | never_unmap: bool = false, | 164 | never_unmap: bool = false, |
| 165 | |||
| 166 | /// Enables emitting info messages with the size and address of every allocation. | ||
| 167 | verbose_log: bool = false, | ||
| 165 | }; | 168 | }; |
| 166 | 169 | ||
| 167 | pub fn GeneralPurposeAllocator(comptime config: Config) type { | 170 | pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| ... | @@ -454,10 +457,19 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { | ... | @@ -454,10 +457,19 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 454 | const result_len = try self.backing_allocator.resizeFn(self.backing_allocator, old_mem, old_align, new_size, len_align, ret_addr); | 457 | const result_len = try self.backing_allocator.resizeFn(self.backing_allocator, old_mem, old_align, new_size, len_align, ret_addr); |
| 455 | 458 | ||
| 456 | if (result_len == 0) { | 459 | if (result_len == 0) { |
| 460 | if (config.verbose_log) { | ||
| 461 | log.info("large free {d} bytes at {*}", .{ old_mem.len, old_mem.ptr }); | ||
| 462 | } | ||
| 463 | |||
| 457 | self.large_allocations.removeAssertDiscard(@ptrToInt(old_mem.ptr)); | 464 | self.large_allocations.removeAssertDiscard(@ptrToInt(old_mem.ptr)); |
| 458 | return 0; | 465 | return 0; |
| 459 | } | 466 | } |
| 460 | 467 | ||
| 468 | if (config.verbose_log) { | ||
| 469 | log.info("large resize {d} bytes at {*} to {d}", .{ | ||
| 470 | old_mem.len, old_mem.ptr, new_size, | ||
| 471 | }); | ||
| 472 | } | ||
| 461 | entry.value.bytes = old_mem.ptr[0..result_len]; | 473 | entry.value.bytes = old_mem.ptr[0..result_len]; |
| 462 | collectStackTrace(ret_addr, &entry.value.stack_addresses); | 474 | collectStackTrace(ret_addr, &entry.value.stack_addresses); |
| 463 | return result_len; | 475 | return result_len; |
| ... | @@ -568,6 +580,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { | ... | @@ -568,6 +580,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 568 | } else { | 580 | } else { |
| 569 | @memset(old_mem.ptr, undefined, old_mem.len); | 581 | @memset(old_mem.ptr, undefined, old_mem.len); |
| 570 | } | 582 | } |
| 583 | if (config.verbose_log) { | ||
| 584 | log.info("small free {d} bytes at {*}", .{ old_mem.len, old_mem.ptr }); | ||
| 585 | } | ||
| 571 | return @as(usize, 0); | 586 | return @as(usize, 0); |
| 572 | } | 587 | } |
| 573 | const new_aligned_size = math.max(new_size, old_align); | 588 | const new_aligned_size = math.max(new_size, old_align); |
| ... | @@ -576,6 +591,11 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { | ... | @@ -576,6 +591,11 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 576 | if (old_mem.len > new_size) { | 591 | if (old_mem.len > new_size) { |
| 577 | @memset(old_mem.ptr + new_size, undefined, old_mem.len - new_size); | 592 | @memset(old_mem.ptr + new_size, undefined, old_mem.len - new_size); |
| 578 | } | 593 | } |
| 594 | if (config.verbose_log) { | ||
| 595 | log.info("small resize {d} bytes at {*} to {d}", .{ | ||
| 596 | old_mem.len, old_mem.ptr, new_size, | ||
| 597 | }); | ||
| 598 | } | ||
| 579 | return new_size; | 599 | return new_size; |
| 580 | } | 600 | } |
| 581 | return error.OutOfMemory; | 601 | return error.OutOfMemory; |
| ... | @@ -623,6 +643,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { | ... | @@ -623,6 +643,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 623 | gop.entry.value.bytes = slice; | 643 | gop.entry.value.bytes = slice; |
| 624 | collectStackTrace(ret_addr, &gop.entry.value.stack_addresses); | 644 | collectStackTrace(ret_addr, &gop.entry.value.stack_addresses); |
| 625 | 645 | ||
| 646 | if (config.verbose_log) { | ||
| 647 | log.info("large alloc {d} bytes at {*}", .{ slice.len, slice.ptr }); | ||
| 648 | } | ||
| 626 | return slice; | 649 | return slice; |
| 627 | } | 650 | } |
| 628 | 651 | ||
| ... | @@ -632,6 +655,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { | ... | @@ -632,6 +655,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 632 | 655 | ||
| 633 | const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size); | 656 | const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size); |
| 634 | const ptr = try self.allocSlot(new_size_class, ret_addr); | 657 | const ptr = try self.allocSlot(new_size_class, ret_addr); |
| 658 | if (config.verbose_log) { | ||
| 659 | log.info("small alloc {d} bytes at {*}", .{ len, ptr }); | ||
| 660 | } | ||
| 635 | return ptr[0..len]; | 661 | return ptr[0..len]; |
| 636 | } | 662 | } |
| 637 | 663 |
lib/std/io.zig-3| ... | @@ -142,9 +142,6 @@ pub const bitReader = @import("io/bit_reader.zig").bitReader; | ... | @@ -142,9 +142,6 @@ pub const bitReader = @import("io/bit_reader.zig").bitReader; |
| 142 | pub const BitWriter = @import("io/bit_writer.zig").BitWriter; | 142 | pub const BitWriter = @import("io/bit_writer.zig").BitWriter; |
| 143 | pub const bitWriter = @import("io/bit_writer.zig").bitWriter; | 143 | pub const bitWriter = @import("io/bit_writer.zig").bitWriter; |
| 144 | 144 | ||
| 145 | pub const AutoIndentingStream = @import("io/auto_indenting_stream.zig").AutoIndentingStream; | ||
| 146 | pub const autoIndentingStream = @import("io/auto_indenting_stream.zig").autoIndentingStream; | ||
| 147 | |||
| 148 | pub const ChangeDetectionStream = @import("io/change_detection_stream.zig").ChangeDetectionStream; | 145 | pub const ChangeDetectionStream = @import("io/change_detection_stream.zig").ChangeDetectionStream; |
| 149 | pub const changeDetectionStream = @import("io/change_detection_stream.zig").changeDetectionStream; | 146 | pub const changeDetectionStream = @import("io/change_detection_stream.zig").changeDetectionStream; |
| 150 | 147 |
lib/std/io/auto_indenting_stream.zig deleted-154| ... | @@ -1,154 +0,0 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | |||
| 7 | const std = @import("../std.zig"); | ||
| 8 | const io = std.io; | ||
| 9 | const mem = std.mem; | ||
| 10 | const assert = std.debug.assert; | ||
| 11 | |||
| 12 | /// Automatically inserts indentation of written data by keeping | ||
| 13 | /// track of the current indentation level | ||
| 14 | pub fn AutoIndentingStream(comptime UnderlyingWriter: type) type { | ||
| 15 | return struct { | ||
| 16 | const Self = @This(); | ||
| 17 | pub const Error = UnderlyingWriter.Error; | ||
| 18 | pub const Writer = io.Writer(*Self, Error, write); | ||
| 19 | |||
| 20 | underlying_writer: UnderlyingWriter, | ||
| 21 | |||
| 22 | indent_count: usize = 0, | ||
| 23 | indent_delta: usize, | ||
| 24 | current_line_empty: bool = true, | ||
| 25 | indent_one_shot_count: usize = 0, // automatically popped when applied | ||
| 26 | applied_indent: usize = 0, // the most recently applied indent | ||
| 27 | indent_next_line: usize = 0, // not used until the next line | ||
| 28 | |||
| 29 | pub fn writer(self: *Self) Writer { | ||
| 30 | return .{ .context = self }; | ||
| 31 | } | ||
| 32 | |||
| 33 | pub fn write(self: *Self, bytes: []const u8) Error!usize { | ||
| 34 | if (bytes.len == 0) | ||
| 35 | return @as(usize, 0); | ||
| 36 | |||
| 37 | try self.applyIndent(); | ||
| 38 | return self.writeNoIndent(bytes); | ||
| 39 | } | ||
| 40 | |||
| 41 | // Change the indent delta without changing the final indentation level | ||
| 42 | pub fn setIndentDelta(self: *Self, indent_delta: usize) void { | ||
| 43 | if (self.indent_delta == indent_delta) { | ||
| 44 | return; | ||
| 45 | } else if (self.indent_delta > indent_delta) { | ||
| 46 | assert(self.indent_delta % indent_delta == 0); | ||
| 47 | self.indent_count = self.indent_count * (self.indent_delta / indent_delta); | ||
| 48 | } else { | ||
| 49 | // assert that the current indentation (in spaces) in a multiple of the new delta | ||
| 50 | assert((self.indent_count * self.indent_delta) % indent_delta == 0); | ||
| 51 | self.indent_count = self.indent_count / (indent_delta / self.indent_delta); | ||
| 52 | } | ||
| 53 | self.indent_delta = indent_delta; | ||
| 54 | } | ||
| 55 | |||
| 56 | fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize { | ||
| 57 | if (bytes.len == 0) | ||
| 58 | return @as(usize, 0); | ||
| 59 | |||
| 60 | try self.underlying_writer.writeAll(bytes); | ||
| 61 | if (bytes[bytes.len - 1] == '\n') | ||
| 62 | self.resetLine(); | ||
| 63 | return bytes.len; | ||
| 64 | } | ||
| 65 | |||
| 66 | pub fn insertNewline(self: *Self) Error!void { | ||
| 67 | _ = try self.writeNoIndent("\n"); | ||
| 68 | } | ||
| 69 | |||
| 70 | fn resetLine(self: *Self) void { | ||
| 71 | self.current_line_empty = true; | ||
| 72 | self.indent_next_line = 0; | ||
| 73 | } | ||
| 74 | |||
| 75 | /// Insert a newline unless the current line is blank | ||
| 76 | pub fn maybeInsertNewline(self: *Self) Error!void { | ||
| 77 | if (!self.current_line_empty) | ||
| 78 | try self.insertNewline(); | ||
| 79 | } | ||
| 80 | |||
| 81 | /// Push default indentation | ||
| 82 | pub fn pushIndent(self: *Self) void { | ||
| 83 | // Doesn't actually write any indentation. | ||
| 84 | // Just primes the stream to be able to write the correct indentation if it needs to. | ||
| 85 | self.indent_count += 1; | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Push an indent that is automatically popped after being applied | ||
| 89 | pub fn pushIndentOneShot(self: *Self) void { | ||
| 90 | self.indent_one_shot_count += 1; | ||
| 91 | self.pushIndent(); | ||
| 92 | } | ||
| 93 | |||
| 94 | /// Turns all one-shot indents into regular indents | ||
| 95 | /// Returns number of indents that must now be manually popped | ||
| 96 | pub fn lockOneShotIndent(self: *Self) usize { | ||
| 97 | var locked_count = self.indent_one_shot_count; | ||
| 98 | self.indent_one_shot_count = 0; | ||
| 99 | return locked_count; | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Push an indent that should not take effect until the next line | ||
| 103 | pub fn pushIndentNextLine(self: *Self) void { | ||
| 104 | self.indent_next_line += 1; | ||
| 105 | self.pushIndent(); | ||
| 106 | } | ||
| 107 | |||
| 108 | pub fn popIndent(self: *Self) void { | ||
| 109 | assert(self.indent_count != 0); | ||
| 110 | self.indent_count -= 1; | ||
| 111 | |||
| 112 | if (self.indent_next_line > 0) | ||
| 113 | self.indent_next_line -= 1; | ||
| 114 | } | ||
| 115 | |||
| 116 | /// Writes ' ' bytes if the current line is empty | ||
| 117 | fn applyIndent(self: *Self) Error!void { | ||
| 118 | const current_indent = self.currentIndent(); | ||
| 119 | if (self.current_line_empty and current_indent > 0) { | ||
| 120 | try self.underlying_writer.writeByteNTimes(' ', current_indent); | ||
| 121 | self.applied_indent = current_indent; | ||
| 122 | } | ||
| 123 | |||
| 124 | self.indent_count -= self.indent_one_shot_count; | ||
| 125 | self.indent_one_shot_count = 0; | ||
| 126 | self.current_line_empty = false; | ||
| 127 | } | ||
| 128 | |||
| 129 | /// Checks to see if the most recent indentation exceeds the currently pushed indents | ||
| 130 | pub fn isLineOverIndented(self: *Self) bool { | ||
| 131 | if (self.current_line_empty) return false; | ||
| 132 | return self.applied_indent > self.currentIndent(); | ||
| 133 | } | ||
| 134 | |||
| 135 | fn currentIndent(self: *Self) usize { | ||
| 136 | var indent_current: usize = 0; | ||
| 137 | if (self.indent_count > 0) { | ||
| 138 | const indent_count = self.indent_count - self.indent_next_line; | ||
| 139 | indent_current = indent_count * self.indent_delta; | ||
| 140 | } | ||
| 141 | return indent_current; | ||
| 142 | } | ||
| 143 | }; | ||
| 144 | } | ||
| 145 | |||
| 146 | pub fn autoIndentingStream( | ||
| 147 | indent_delta: usize, | ||
| 148 | underlying_writer: anytype, | ||
| 149 | ) AutoIndentingStream(@TypeOf(underlying_writer)) { | ||
| 150 | return AutoIndentingStream(@TypeOf(underlying_writer)){ | ||
| 151 | .underlying_writer = underlying_writer, | ||
| 152 | .indent_delta = indent_delta, | ||
| 153 | }; | ||
| 154 | } | ||
lib/std/multi_array_list.zig created+446| ... | @@ -0,0 +1,446 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("std.zig"); | ||
| 7 | const assert = std.debug.assert; | ||
| 8 | const meta = std.meta; | ||
| 9 | const mem = std.mem; | ||
| 10 | const Allocator = mem.Allocator; | ||
| 11 | |||
| 12 | pub fn MultiArrayList(comptime S: type) type { | ||
| 13 | return struct { | ||
| 14 | bytes: [*]align(@alignOf(S)) u8 = undefined, | ||
| 15 | len: usize = 0, | ||
| 16 | capacity: usize = 0, | ||
| 17 | |||
| 18 | pub const Elem = S; | ||
| 19 | |||
| 20 | pub const Field = meta.FieldEnum(S); | ||
| 21 | |||
| 22 | pub const Slice = struct { | ||
| 23 | /// This array is indexed by the field index which can be obtained | ||
| 24 | /// by using @enumToInt() on the Field enum | ||
| 25 | ptrs: [fields.len][*]u8, | ||
| 26 | len: usize, | ||
| 27 | capacity: usize, | ||
| 28 | |||
| 29 | pub fn items(self: Slice, comptime field: Field) []FieldType(field) { | ||
| 30 | const byte_ptr = self.ptrs[@enumToInt(field)]; | ||
| 31 | const F = FieldType(field); | ||
| 32 | const casted_ptr = @ptrCast([*]F, @alignCast(@alignOf(F), byte_ptr)); | ||
| 33 | return casted_ptr[0..self.len]; | ||
| 34 | } | ||
| 35 | |||
| 36 | pub fn toMultiArrayList(self: Slice) Self { | ||
| 37 | if (self.ptrs.len == 0) { | ||
| 38 | return .{}; | ||
| 39 | } | ||
| 40 | const unaligned_ptr = self.ptrs[sizes.fields[0]]; | ||
| 41 | const aligned_ptr = @alignCast(@alignOf(S), unaligned_ptr); | ||
| 42 | const casted_ptr = @ptrCast([*]align(@alignOf(S)) u8, aligned_ptr); | ||
| 43 | return .{ | ||
| 44 | .bytes = casted_ptr, | ||
| 45 | .len = self.len, | ||
| 46 | .capacity = self.capacity, | ||
| 47 | }; | ||
| 48 | } | ||
| 49 | |||
| 50 | pub fn deinit(self: *Slice, gpa: *Allocator) void { | ||
| 51 | var other = self.toMultiArrayList(); | ||
| 52 | other.deinit(gpa); | ||
| 53 | self.* = undefined; | ||
| 54 | } | ||
| 55 | }; | ||
| 56 | |||
| 57 | const Self = @This(); | ||
| 58 | |||
| 59 | const fields = meta.fields(S); | ||
| 60 | /// `sizes.bytes` is an array of @sizeOf each S field. Sorted by alignment, descending. | ||
| 61 | /// `sizes.fields` is an array mapping from `sizes.bytes` array index to field index. | ||
| 62 | const sizes = blk: { | ||
| 63 | const Data = struct { | ||
| 64 | size: usize, | ||
| 65 | size_index: usize, | ||
| 66 | alignment: usize, | ||
| 67 | }; | ||
| 68 | var data: [fields.len]Data = undefined; | ||
| 69 | for (fields) |field_info, i| { | ||
| 70 | data[i] = .{ | ||
| 71 | .size = @sizeOf(field_info.field_type), | ||
| 72 | .size_index = i, | ||
| 73 | .alignment = field_info.alignment, | ||
| 74 | }; | ||
| 75 | } | ||
| 76 | const Sort = struct { | ||
| 77 | fn lessThan(trash: *i32, lhs: Data, rhs: Data) bool { | ||
| 78 | return lhs.alignment >= rhs.alignment; | ||
| 79 | } | ||
| 80 | }; | ||
| 81 | var trash: i32 = undefined; // workaround for stage1 compiler bug | ||
| 82 | std.sort.sort(Data, &data, &trash, Sort.lessThan); | ||
| 83 | var sizes_bytes: [fields.len]usize = undefined; | ||
| 84 | var field_indexes: [fields.len]usize = undefined; | ||
| 85 | for (data) |elem, i| { | ||
| 86 | sizes_bytes[i] = elem.size; | ||
| 87 | field_indexes[i] = elem.size_index; | ||
| 88 | } | ||
| 89 | break :blk .{ | ||
| 90 | .bytes = sizes_bytes, | ||
| 91 | .fields = field_indexes, | ||
| 92 | }; | ||
| 93 | }; | ||
| 94 | |||
| 95 | /// Release all allocated memory. | ||
| 96 | pub fn deinit(self: *Self, gpa: *Allocator) void { | ||
| 97 | gpa.free(self.allocatedBytes()); | ||
| 98 | self.* = undefined; | ||
| 99 | } | ||
| 100 | |||
| 101 | /// The caller owns the returned memory. Empties this MultiArrayList. | ||
| 102 | pub fn toOwnedSlice(self: *Self) Slice { | ||
| 103 | const result = self.slice(); | ||
| 104 | self.* = .{}; | ||
| 105 | return result; | ||
| 106 | } | ||
| 107 | |||
| 108 | pub fn slice(self: Self) Slice { | ||
| 109 | var result: Slice = .{ | ||
| 110 | .ptrs = undefined, | ||
| 111 | .len = self.len, | ||
| 112 | .capacity = self.capacity, | ||
| 113 | }; | ||
| 114 | var ptr: [*]u8 = self.bytes; | ||
| 115 | for (sizes.bytes) |field_size, i| { | ||
| 116 | result.ptrs[sizes.fields[i]] = ptr; | ||
| 117 | ptr += field_size * self.capacity; | ||
| 118 | } | ||
| 119 | return result; | ||
| 120 | } | ||
| 121 | |||
| 122 | pub fn items(self: Self, comptime field: Field) []FieldType(field) { | ||
| 123 | return self.slice().items(field); | ||
| 124 | } | ||
| 125 | |||
| 126 | /// Overwrite one array element with new data. | ||
| 127 | pub fn set(self: *Self, index: usize, elem: S) void { | ||
| 128 | const slices = self.slice(); | ||
| 129 | inline for (fields) |field_info, i| { | ||
| 130 | slices.items(@intToEnum(Field, i))[index] = @field(elem, field_info.name); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | /// Obtain all the data for one array element. | ||
| 135 | pub fn get(self: *Self, index: usize) S { | ||
| 136 | const slices = self.slice(); | ||
| 137 | var result: S = undefined; | ||
| 138 | inline for (fields) |field_info, i| { | ||
| 139 | @field(elem, field_info.name) = slices.items(@intToEnum(Field, i))[index]; | ||
| 140 | } | ||
| 141 | return result; | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Extend the list by 1 element. Allocates more memory as necessary. | ||
| 145 | pub fn append(self: *Self, gpa: *Allocator, elem: S) !void { | ||
| 146 | try self.ensureCapacity(gpa, self.len + 1); | ||
| 147 | self.appendAssumeCapacity(elem); | ||
| 148 | } | ||
| 149 | |||
| 150 | /// Extend the list by 1 element, but asserting `self.capacity` | ||
| 151 | /// is sufficient to hold an additional item. | ||
| 152 | pub fn appendAssumeCapacity(self: *Self, elem: S) void { | ||
| 153 | assert(self.len < self.capacity); | ||
| 154 | self.len += 1; | ||
| 155 | self.set(self.len - 1, elem); | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Adjust the list's length to `new_len`. | ||
| 159 | /// Does not initialize added items, if any. | ||
| 160 | pub fn resize(self: *Self, gpa: *Allocator, new_len: usize) !void { | ||
| 161 | try self.ensureCapacity(gpa, new_len); | ||
| 162 | self.len = new_len; | ||
| 163 | } | ||
| 164 | |||
| 165 | /// Attempt to reduce allocated capacity to `new_len`. | ||
| 166 | /// If `new_len` is greater than zero, this may fail to reduce the capacity, | ||
| 167 | /// but the data remains intact and the length is updated to new_len. | ||
| 168 | pub fn shrinkAndFree(self: *Self, gpa: *Allocator, new_len: usize) void { | ||
| 169 | if (new_len == 0) { | ||
| 170 | gpa.free(self.allocatedBytes()); | ||
| 171 | self.* = .{}; | ||
| 172 | return; | ||
| 173 | } | ||
| 174 | assert(new_len <= self.capacity); | ||
| 175 | assert(new_len <= self.len); | ||
| 176 | |||
| 177 | const other_bytes = gpa.allocAdvanced( | ||
| 178 | u8, | ||
| 179 | @alignOf(S), | ||
| 180 | capacityInBytes(new_len), | ||
| 181 | .exact, | ||
| 182 | ) catch { | ||
| 183 | const self_slice = self.slice(); | ||
| 184 | inline for (fields) |field_info, i| { | ||
| 185 | const field = @intToEnum(Field, i); | ||
| 186 | const dest_slice = self_slice.items(field)[new_len..]; | ||
| 187 | const byte_count = dest_slice.len * @sizeOf(field_info.field_type); | ||
| 188 | // We use memset here for more efficient codegen in safety-checked, | ||
| 189 | // valgrind-enabled builds. Otherwise the valgrind client request | ||
| 190 | // will be repeated for every element. | ||
| 191 | @memset(@ptrCast([*]u8, dest_slice.ptr), undefined, byte_count); | ||
| 192 | } | ||
| 193 | self.len = new_len; | ||
| 194 | return; | ||
| 195 | }; | ||
| 196 | var other = Self{ | ||
| 197 | .bytes = other_bytes.ptr, | ||
| 198 | .capacity = new_len, | ||
| 199 | .len = new_len, | ||
| 200 | }; | ||
| 201 | self.len = new_len; | ||
| 202 | const self_slice = self.slice(); | ||
| 203 | const other_slice = other.slice(); | ||
| 204 | inline for (fields) |field_info, i| { | ||
| 205 | const field = @intToEnum(Field, i); | ||
| 206 | // TODO we should be able to use std.mem.copy here but it causes a | ||
| 207 | // test failure on aarch64 with -OReleaseFast | ||
| 208 | const src_slice = mem.sliceAsBytes(self_slice.items(field)); | ||
| 209 | const dst_slice = mem.sliceAsBytes(other_slice.items(field)); | ||
| 210 | @memcpy(dst_slice.ptr, src_slice.ptr, src_slice.len); | ||
| 211 | } | ||
| 212 | gpa.free(self.allocatedBytes()); | ||
| 213 | self.* = other; | ||
| 214 | } | ||
| 215 | |||
| 216 | /// Reduce length to `new_len`. | ||
| 217 | /// Invalidates pointers to elements `items[new_len..]`. | ||
| 218 | /// Keeps capacity the same. | ||
| 219 | pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void { | ||
| 220 | self.len = new_len; | ||
| 221 | } | ||
| 222 | |||
| 223 | /// Modify the array so that it can hold at least `new_capacity` items. | ||
| 224 | /// Implements super-linear growth to achieve amortized O(1) append operations. | ||
| 225 | /// Invalidates pointers if additional memory is needed. | ||
| 226 | pub fn ensureCapacity(self: *Self, gpa: *Allocator, new_capacity: usize) !void { | ||
| 227 | var better_capacity = self.capacity; | ||
| 228 | if (better_capacity >= new_capacity) return; | ||
| 229 | |||
| 230 | while (true) { | ||
| 231 | better_capacity += better_capacity / 2 + 8; | ||
| 232 | if (better_capacity >= new_capacity) break; | ||
| 233 | } | ||
| 234 | |||
| 235 | return self.setCapacity(gpa, better_capacity); | ||
| 236 | } | ||
| 237 | |||
| 238 | /// Modify the array so that it can hold exactly `new_capacity` items. | ||
| 239 | /// Invalidates pointers if additional memory is needed. | ||
| 240 | /// `new_capacity` must be greater or equal to `len`. | ||
| 241 | pub fn setCapacity(self: *Self, gpa: *Allocator, new_capacity: usize) !void { | ||
| 242 | assert(new_capacity >= self.len); | ||
| 243 | const new_bytes = try gpa.allocAdvanced( | ||
| 244 | u8, | ||
| 245 | @alignOf(S), | ||
| 246 | capacityInBytes(new_capacity), | ||
| 247 | .exact, | ||
| 248 | ); | ||
| 249 | if (self.len == 0) { | ||
| 250 | self.bytes = new_bytes.ptr; | ||
| 251 | self.capacity = new_capacity; | ||
| 252 | return; | ||
| 253 | } | ||
| 254 | var other = Self{ | ||
| 255 | .bytes = new_bytes.ptr, | ||
| 256 | .capacity = new_capacity, | ||
| 257 | .len = self.len, | ||
| 258 | }; | ||
| 259 | const self_slice = self.slice(); | ||
| 260 | const other_slice = other.slice(); | ||
| 261 | inline for (fields) |field_info, i| { | ||
| 262 | const field = @intToEnum(Field, i); | ||
| 263 | // TODO we should be able to use std.mem.copy here but it causes a | ||
| 264 | // test failure on aarch64 with -OReleaseFast | ||
| 265 | const src_slice = mem.sliceAsBytes(self_slice.items(field)); | ||
| 266 | const dst_slice = mem.sliceAsBytes(other_slice.items(field)); | ||
| 267 | @memcpy(dst_slice.ptr, src_slice.ptr, src_slice.len); | ||
| 268 | } | ||
| 269 | gpa.free(self.allocatedBytes()); | ||
| 270 | self.* = other; | ||
| 271 | } | ||
| 272 | |||
| 273 | fn capacityInBytes(capacity: usize) usize { | ||
| 274 | const sizes_vector: std.meta.Vector(sizes.bytes.len, usize) = sizes.bytes; | ||
| 275 | const capacity_vector = @splat(sizes.bytes.len, capacity); | ||
| 276 | return @reduce(.Add, capacity_vector * sizes_vector); | ||
| 277 | } | ||
| 278 | |||
| 279 | fn allocatedBytes(self: Self) []align(@alignOf(S)) u8 { | ||
| 280 | return self.bytes[0..capacityInBytes(self.capacity)]; | ||
| 281 | } | ||
| 282 | |||
| 283 | fn FieldType(field: Field) type { | ||
| 284 | return meta.fieldInfo(S, field).field_type; | ||
| 285 | } | ||
| 286 | }; | ||
| 287 | } | ||
| 288 | |||
| 289 | test "basic usage" { | ||
| 290 | const testing = std.testing; | ||
| 291 | const ally = testing.allocator; | ||
| 292 | |||
| 293 | const Foo = struct { | ||
| 294 | a: u32, | ||
| 295 | b: []const u8, | ||
| 296 | c: u8, | ||
| 297 | }; | ||
| 298 | |||
| 299 | var list = MultiArrayList(Foo){}; | ||
| 300 | defer list.deinit(ally); | ||
| 301 | |||
| 302 | try list.ensureCapacity(ally, 2); | ||
| 303 | |||
| 304 | list.appendAssumeCapacity(.{ | ||
| 305 | .a = 1, | ||
| 306 | .b = "foobar", | ||
| 307 | .c = 'a', | ||
| 308 | }); | ||
| 309 | |||
| 310 | list.appendAssumeCapacity(.{ | ||
| 311 | .a = 2, | ||
| 312 | .b = "zigzag", | ||
| 313 | .c = 'b', | ||
| 314 | }); | ||
| 315 | |||
| 316 | testing.expectEqualSlices(u32, list.items(.a), &[_]u32{ 1, 2 }); | ||
| 317 | testing.expectEqualSlices(u8, list.items(.c), &[_]u8{ 'a', 'b' }); | ||
| 318 | |||
| 319 | testing.expectEqual(@as(usize, 2), list.items(.b).len); | ||
| 320 | testing.expectEqualStrings("foobar", list.items(.b)[0]); | ||
| 321 | testing.expectEqualStrings("zigzag", list.items(.b)[1]); | ||
| 322 | |||
| 323 | try list.append(ally, .{ | ||
| 324 | .a = 3, | ||
| 325 | .b = "fizzbuzz", | ||
| 326 | .c = 'c', | ||
| 327 | }); | ||
| 328 | |||
| 329 | testing.expectEqualSlices(u32, list.items(.a), &[_]u32{ 1, 2, 3 }); | ||
| 330 | testing.expectEqualSlices(u8, list.items(.c), &[_]u8{ 'a', 'b', 'c' }); | ||
| 331 | |||
| 332 | testing.expectEqual(@as(usize, 3), list.items(.b).len); | ||
| 333 | testing.expectEqualStrings("foobar", list.items(.b)[0]); | ||
| 334 | testing.expectEqualStrings("zigzag", list.items(.b)[1]); | ||
| 335 | testing.expectEqualStrings("fizzbuzz", list.items(.b)[2]); | ||
| 336 | |||
| 337 | // Add 6 more things to force a capacity increase. | ||
| 338 | var i: usize = 0; | ||
| 339 | while (i < 6) : (i += 1) { | ||
| 340 | try list.append(ally, .{ | ||
| 341 | .a = @intCast(u32, 4 + i), | ||
| 342 | .b = "whatever", | ||
| 343 | .c = @intCast(u8, 'd' + i), | ||
| 344 | }); | ||
| 345 | } | ||
| 346 | |||
| 347 | testing.expectEqualSlices( | ||
| 348 | u32, | ||
| 349 | &[_]u32{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }, | ||
| 350 | list.items(.a), | ||
| 351 | ); | ||
| 352 | testing.expectEqualSlices( | ||
| 353 | u8, | ||
| 354 | &[_]u8{ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' }, | ||
| 355 | list.items(.c), | ||
| 356 | ); | ||
| 357 | |||
| 358 | list.shrinkAndFree(ally, 3); | ||
| 359 | |||
| 360 | testing.expectEqualSlices(u32, list.items(.a), &[_]u32{ 1, 2, 3 }); | ||
| 361 | testing.expectEqualSlices(u8, list.items(.c), &[_]u8{ 'a', 'b', 'c' }); | ||
| 362 | |||
| 363 | testing.expectEqual(@as(usize, 3), list.items(.b).len); | ||
| 364 | testing.expectEqualStrings("foobar", list.items(.b)[0]); | ||
| 365 | testing.expectEqualStrings("zigzag", list.items(.b)[1]); | ||
| 366 | testing.expectEqualStrings("fizzbuzz", list.items(.b)[2]); | ||
| 367 | } | ||
| 368 | |||
| 369 | // This was observed to fail on aarch64 with LLVM 11, when the capacityInBytes | ||
| 370 | // function used the @reduce code path. | ||
| 371 | test "regression test for @reduce bug" { | ||
| 372 | const ally = std.testing.allocator; | ||
| 373 | var list = MultiArrayList(struct { | ||
| 374 | tag: std.zig.Token.Tag, | ||
| 375 | start: u32, | ||
| 376 | }){}; | ||
| 377 | defer list.deinit(ally); | ||
| 378 | |||
| 379 | try list.ensureCapacity(ally, 20); | ||
| 380 | |||
| 381 | try list.append(ally, .{ .tag = .keyword_const, .start = 0 }); | ||
| 382 | try list.append(ally, .{ .tag = .identifier, .start = 6 }); | ||
| 383 | try list.append(ally, .{ .tag = .equal, .start = 10 }); | ||
| 384 | try list.append(ally, .{ .tag = .builtin, .start = 12 }); | ||
| 385 | try list.append(ally, .{ .tag = .l_paren, .start = 19 }); | ||
| 386 | try list.append(ally, .{ .tag = .string_literal, .start = 20 }); | ||
| 387 | try list.append(ally, .{ .tag = .r_paren, .start = 25 }); | ||
| 388 | try list.append(ally, .{ .tag = .semicolon, .start = 26 }); | ||
| 389 | try list.append(ally, .{ .tag = .keyword_pub, .start = 29 }); | ||
| 390 | try list.append(ally, .{ .tag = .keyword_fn, .start = 33 }); | ||
| 391 | try list.append(ally, .{ .tag = .identifier, .start = 36 }); | ||
| 392 | try list.append(ally, .{ .tag = .l_paren, .start = 40 }); | ||
| 393 | try list.append(ally, .{ .tag = .r_paren, .start = 41 }); | ||
| 394 | try list.append(ally, .{ .tag = .identifier, .start = 43 }); | ||
| 395 | try list.append(ally, .{ .tag = .bang, .start = 51 }); | ||
| 396 | try list.append(ally, .{ .tag = .identifier, .start = 52 }); | ||
| 397 | try list.append(ally, .{ .tag = .l_brace, .start = 57 }); | ||
| 398 | try list.append(ally, .{ .tag = .identifier, .start = 63 }); | ||
| 399 | try list.append(ally, .{ .tag = .period, .start = 66 }); | ||
| 400 | try list.append(ally, .{ .tag = .identifier, .start = 67 }); | ||
| 401 | try list.append(ally, .{ .tag = .period, .start = 70 }); | ||
| 402 | try list.append(ally, .{ .tag = .identifier, .start = 71 }); | ||
| 403 | try list.append(ally, .{ .tag = .l_paren, .start = 75 }); | ||
| 404 | try list.append(ally, .{ .tag = .string_literal, .start = 76 }); | ||
| 405 | try list.append(ally, .{ .tag = .comma, .start = 113 }); | ||
| 406 | try list.append(ally, .{ .tag = .period, .start = 115 }); | ||
| 407 | try list.append(ally, .{ .tag = .l_brace, .start = 116 }); | ||
| 408 | try list.append(ally, .{ .tag = .r_brace, .start = 117 }); | ||
| 409 | try list.append(ally, .{ .tag = .r_paren, .start = 118 }); | ||
| 410 | try list.append(ally, .{ .tag = .semicolon, .start = 119 }); | ||
| 411 | try list.append(ally, .{ .tag = .r_brace, .start = 121 }); | ||
| 412 | try list.append(ally, .{ .tag = .eof, .start = 123 }); | ||
| 413 | |||
| 414 | const tags = list.items(.tag); | ||
| 415 | std.testing.expectEqual(tags[1], .identifier); | ||
| 416 | std.testing.expectEqual(tags[2], .equal); | ||
| 417 | std.testing.expectEqual(tags[3], .builtin); | ||
| 418 | std.testing.expectEqual(tags[4], .l_paren); | ||
| 419 | std.testing.expectEqual(tags[5], .string_literal); | ||
| 420 | std.testing.expectEqual(tags[6], .r_paren); | ||
| 421 | std.testing.expectEqual(tags[7], .semicolon); | ||
| 422 | std.testing.expectEqual(tags[8], .keyword_pub); | ||
| 423 | std.testing.expectEqual(tags[9], .keyword_fn); | ||
| 424 | std.testing.expectEqual(tags[10], .identifier); | ||
| 425 | std.testing.expectEqual(tags[11], .l_paren); | ||
| 426 | std.testing.expectEqual(tags[12], .r_paren); | ||
| 427 | std.testing.expectEqual(tags[13], .identifier); | ||
| 428 | std.testing.expectEqual(tags[14], .bang); | ||
| 429 | std.testing.expectEqual(tags[15], .identifier); | ||
| 430 | std.testing.expectEqual(tags[16], .l_brace); | ||
| 431 | std.testing.expectEqual(tags[17], .identifier); | ||
| 432 | std.testing.expectEqual(tags[18], .period); | ||
| 433 | std.testing.expectEqual(tags[19], .identifier); | ||
| 434 | std.testing.expectEqual(tags[20], .period); | ||
| 435 | std.testing.expectEqual(tags[21], .identifier); | ||
| 436 | std.testing.expectEqual(tags[22], .l_paren); | ||
| 437 | std.testing.expectEqual(tags[23], .string_literal); | ||
| 438 | std.testing.expectEqual(tags[24], .comma); | ||
| 439 | std.testing.expectEqual(tags[25], .period); | ||
| 440 | std.testing.expectEqual(tags[26], .l_brace); | ||
| 441 | std.testing.expectEqual(tags[27], .r_brace); | ||
| 442 | std.testing.expectEqual(tags[28], .r_paren); | ||
| 443 | std.testing.expectEqual(tags[29], .semicolon); | ||
| 444 | std.testing.expectEqual(tags[30], .r_brace); | ||
| 445 | std.testing.expectEqual(tags[31], .eof); | ||
| 446 | } | ||
lib/std/std.zig+1| ... | @@ -20,6 +20,7 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM | ... | @@ -20,6 +20,7 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM |
| 20 | pub const DynLib = @import("dynamic_library.zig").DynLib; | 20 | pub const DynLib = @import("dynamic_library.zig").DynLib; |
| 21 | pub const HashMap = hash_map.HashMap; | 21 | pub const HashMap = hash_map.HashMap; |
| 22 | pub const HashMapUnmanaged = hash_map.HashMapUnmanaged; | 22 | pub const HashMapUnmanaged = hash_map.HashMapUnmanaged; |
| 23 | pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList; | ||
| 23 | pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray; | 24 | pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray; |
| 24 | pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian; | 25 | pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian; |
| 25 | pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice; | 26 | pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice; |
lib/std/zig.zig-1| ... | @@ -12,7 +12,6 @@ pub const fmtId = @import("zig/fmt.zig").fmtId; | ... | @@ -12,7 +12,6 @@ pub const fmtId = @import("zig/fmt.zig").fmtId; |
| 12 | pub const fmtEscapes = @import("zig/fmt.zig").fmtEscapes; | 12 | pub const fmtEscapes = @import("zig/fmt.zig").fmtEscapes; |
| 13 | pub const parse = @import("zig/parse.zig").parse; | 13 | pub const parse = @import("zig/parse.zig").parse; |
| 14 | pub const parseStringLiteral = @import("zig/string_literal.zig").parse; | 14 | pub const parseStringLiteral = @import("zig/string_literal.zig").parse; |
| 15 | pub const render = @import("zig/render.zig").render; | ||
| 16 | pub const ast = @import("zig/ast.zig"); | 15 | pub const ast = @import("zig/ast.zig"); |
| 17 | pub const system = @import("zig/system.zig"); | 16 | pub const system = @import("zig/system.zig"); |
| 18 | pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; | 17 | pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; |
lib/std/zig/ast.zig+2754-3074| ... | @@ -9,71 +9,78 @@ const testing = std.testing; | ... | @@ -9,71 +9,78 @@ const testing = std.testing; |
| 9 | const mem = std.mem; | 9 | const mem = std.mem; |
| 10 | const Token = std.zig.Token; | 10 | const Token = std.zig.Token; |
| 11 | 11 | ||
| 12 | pub const TokenIndex = usize; | 12 | pub const TokenIndex = u32; |
| 13 | pub const NodeIndex = usize; | 13 | pub const ByteOffset = u32; |
| 14 | |||
| 15 | pub const TokenList = std.MultiArrayList(struct { | ||
| 16 | tag: Token.Tag, | ||
| 17 | start: ByteOffset, | ||
| 18 | }); | ||
| 19 | pub const NodeList = std.MultiArrayList(Node); | ||
| 14 | 20 | ||
| 15 | pub const Tree = struct { | 21 | pub const Tree = struct { |
| 16 | /// Reference to externally-owned data. | 22 | /// Reference to externally-owned data. |
| 17 | source: []const u8, | 23 | source: []const u8, |
| 18 | token_ids: []const Token.Id, | ||
| 19 | token_locs: []const Token.Loc, | ||
| 20 | errors: []const Error, | ||
| 21 | root_node: *Node.Root, | ||
| 22 | 24 | ||
| 23 | arena: std.heap.ArenaAllocator.State, | 25 | tokens: TokenList.Slice, |
| 24 | gpa: *mem.Allocator, | 26 | /// The root AST node is assumed to be index 0. Since there can be no |
| 27 | /// references to the root node, this means 0 is available to indicate null. | ||
| 28 | nodes: NodeList.Slice, | ||
| 29 | extra_data: []Node.Index, | ||
| 25 | 30 | ||
| 26 | /// translate-c uses this to avoid having to emit correct newlines | 31 | errors: []const Error, |
| 27 | /// TODO get rid of this hack | ||
| 28 | generated: bool = false, | ||
| 29 | 32 | ||
| 30 | pub fn deinit(self: *Tree) void { | 33 | pub const Location = struct { |
| 31 | self.gpa.free(self.token_ids); | 34 | line: usize, |
| 32 | self.gpa.free(self.token_locs); | 35 | column: usize, |
| 33 | self.gpa.free(self.errors); | 36 | line_start: usize, |
| 34 | self.arena.promote(self.gpa).deinit(); | 37 | line_end: usize, |
| 35 | } | 38 | }; |
| 36 | 39 | ||
| 37 | pub fn renderError(self: *Tree, parse_error: *const Error, stream: anytype) !void { | 40 | pub fn deinit(tree: *Tree, gpa: *mem.Allocator) void { |
| 38 | return parse_error.render(self.token_ids, stream); | 41 | tree.tokens.deinit(gpa); |
| 42 | tree.nodes.deinit(gpa); | ||
| 43 | gpa.free(tree.extra_data); | ||
| 44 | gpa.free(tree.errors); | ||
| 45 | tree.* = undefined; | ||
| 39 | } | 46 | } |
| 40 | 47 | ||
| 41 | pub fn tokenSlice(self: *Tree, token_index: TokenIndex) []const u8 { | 48 | pub const RenderError = error{ |
| 42 | return self.tokenSliceLoc(self.token_locs[token_index]); | 49 | /// Ran out of memory allocating call stack frames to complete rendering, or |
| 43 | } | 50 | /// ran out of memory allocating space in the output buffer. |
| 51 | OutOfMemory, | ||
| 52 | }; | ||
| 44 | 53 | ||
| 45 | pub fn tokenSliceLoc(self: *Tree, token: Token.Loc) []const u8 { | 54 | /// `gpa` is used for allocating the resulting formatted source code, as well as |
| 46 | return self.source[token.start..token.end]; | 55 | /// for allocating extra stack memory if needed, because this function utilizes recursion. |
| 47 | } | 56 | /// Note: that's not actually true yet, see https://github.com/ziglang/zig/issues/1006. |
| 57 | /// Caller owns the returned slice of bytes, allocated with `gpa`. | ||
| 58 | pub fn render(tree: Tree, gpa: *mem.Allocator) RenderError![]u8 { | ||
| 59 | var buffer = std.ArrayList(u8).init(gpa); | ||
| 60 | defer buffer.deinit(); | ||
| 48 | 61 | ||
| 49 | pub fn getNodeSource(self: *const Tree, node: *const Node) []const u8 { | 62 | try tree.renderToArrayList(&buffer); |
| 50 | const first_token = self.token_locs[node.firstToken()]; | 63 | return buffer.toOwnedSlice(); |
| 51 | const last_token = self.token_locs[node.lastToken()]; | ||
| 52 | return self.source[first_token.start..last_token.end]; | ||
| 53 | } | 64 | } |
| 54 | 65 | ||
| 55 | pub const Location = struct { | 66 | pub fn renderToArrayList(tree: Tree, buffer: *std.ArrayList(u8)) RenderError!void { |
| 56 | line: usize, | 67 | return @import("./render.zig").renderTree(buffer, tree); |
| 57 | column: usize, | 68 | } |
| 58 | line_start: usize, | ||
| 59 | line_end: usize, | ||
| 60 | }; | ||
| 61 | 69 | ||
| 62 | /// Return the Location of the token relative to the offset specified by `start_index`. | 70 | pub fn tokenLocation(self: Tree, start_offset: ByteOffset, token_index: TokenIndex) Location { |
| 63 | pub fn tokenLocationLoc(self: *Tree, start_index: usize, token: Token.Loc) Location { | ||
| 64 | var loc = Location{ | 71 | var loc = Location{ |
| 65 | .line = 0, | 72 | .line = 0, |
| 66 | .column = 0, | 73 | .column = 0, |
| 67 | .line_start = start_index, | 74 | .line_start = start_offset, |
| 68 | .line_end = self.source.len, | 75 | .line_end = self.source.len, |
| 69 | }; | 76 | }; |
| 70 | if (self.generated) | 77 | const token_start = self.tokens.items(.start)[token_index]; |
| 71 | return loc; | 78 | for (self.source[start_offset..]) |c, i| { |
| 72 | const token_start = token.start; | 79 | if (i + start_offset == token_start) { |
| 73 | for (self.source[start_index..]) |c, i| { | 80 | loc.line_end = i + start_offset; |
| 74 | if (i + start_index == token_start) { | 81 | while (loc.line_end < self.source.len and self.source[loc.line_end] != '\n') { |
| 75 | loc.line_end = i + start_index; | 82 | loc.line_end += 1; |
| 76 | while (loc.line_end < self.source.len and self.source[loc.line_end] != '\n') : (loc.line_end += 1) {} | 83 | } |
| 77 | return loc; | 84 | return loc; |
| 78 | } | 85 | } |
| 79 | if (c == '\n') { | 86 | if (c == '\n') { |
| ... | @@ -87,3205 +94,2878 @@ pub const Tree = struct { | ... | @@ -87,3205 +94,2878 @@ pub const Tree = struct { |
| 87 | return loc; | 94 | return loc; |
| 88 | } | 95 | } |
| 89 | 96 | ||
| 90 | pub fn tokenLocation(self: *Tree, start_index: usize, token_index: TokenIndex) Location { | 97 | pub fn tokenSlice(tree: Tree, token_index: TokenIndex) []const u8 { |
| 91 | return self.tokenLocationLoc(start_index, self.token_locs[token_index]); | 98 | const token_starts = tree.tokens.items(.start); |
| 92 | } | 99 | const token_tags = tree.tokens.items(.tag); |
| 100 | const token_tag = token_tags[token_index]; | ||
| 101 | |||
| 102 | // Many tokens can be determined entirely by their tag. | ||
| 103 | if (token_tag.lexeme()) |lexeme| { | ||
| 104 | return lexeme; | ||
| 105 | } | ||
| 93 | 106 | ||
| 94 | pub fn tokensOnSameLine(self: *Tree, token1_index: TokenIndex, token2_index: TokenIndex) bool { | 107 | // For some tokens, re-tokenization is needed to find the end. |
| 95 | return self.tokensOnSameLineLoc(self.token_locs[token1_index], self.token_locs[token2_index]); | 108 | var tokenizer: std.zig.Tokenizer = .{ |
| 109 | .buffer = tree.source, | ||
| 110 | .index = token_starts[token_index], | ||
| 111 | .pending_invalid_token = null, | ||
| 112 | }; | ||
| 113 | const token = tokenizer.next(); | ||
| 114 | assert(token.tag == token_tag); | ||
| 115 | return tree.source[token.loc.start..token.loc.end]; | ||
| 96 | } | 116 | } |
| 97 | 117 | ||
| 98 | pub fn tokensOnSameLineLoc(self: *Tree, token1: Token.Loc, token2: Token.Loc) bool { | 118 | pub fn extraData(tree: Tree, index: usize, comptime T: type) T { |
| 99 | return mem.indexOfScalar(u8, self.source[token1.end..token2.start], '\n') == null; | 119 | const fields = std.meta.fields(T); |
| 120 | var result: T = undefined; | ||
| 121 | inline for (fields) |field, i| { | ||
| 122 | comptime assert(field.field_type == Node.Index); | ||
| 123 | @field(result, field.name) = tree.extra_data[index + i]; | ||
| 124 | } | ||
| 125 | return result; | ||
| 100 | } | 126 | } |
| 101 | 127 | ||
| 102 | pub fn dump(self: *Tree) void { | 128 | pub fn rootDecls(tree: Tree) []const Node.Index { |
| 103 | self.root_node.base.dump(0); | 129 | // Root is always index 0. |
| 130 | const nodes_data = tree.nodes.items(.data); | ||
| 131 | return tree.extra_data[nodes_data[0].lhs..nodes_data[0].rhs]; | ||
| 104 | } | 132 | } |
| 105 | 133 | ||
| 106 | /// Skips over comments | 134 | pub fn renderError(tree: Tree, parse_error: Error, stream: anytype) !void { |
| 107 | pub fn prevToken(self: *Tree, token_index: TokenIndex) TokenIndex { | 135 | const token_tags = tree.tokens.items(.tag); |
| 108 | var index = token_index - 1; | 136 | switch (parse_error.tag) { |
| 109 | while (self.token_ids[index] == Token.Id.LineComment) { | 137 | .asterisk_after_ptr_deref => { |
| 110 | index -= 1; | 138 | return stream.writeAll("'.*' cannot be followed by '*'. Are you missing a space?"); |
| 139 | }, | ||
| 140 | .decl_between_fields => { | ||
| 141 | return stream.writeAll("declarations are not allowed between container fields"); | ||
| 142 | }, | ||
| 143 | .expected_block => { | ||
| 144 | return stream.print("expected block or field, found '{s}'", .{ | ||
| 145 | token_tags[parse_error.token].symbol(), | ||
| 146 | }); | ||
| 147 | }, | ||
| 148 | .expected_block_or_assignment => { | ||
| 149 | return stream.print("expected block or assignment, found '{s}'", .{ | ||
| 150 | token_tags[parse_error.token].symbol(), | ||
| 151 | }); | ||
| 152 | }, | ||
| 153 | .expected_block_or_expr => { | ||
| 154 | return stream.print("expected block or expression, found '{s}'", .{ | ||
| 155 | token_tags[parse_error.token].symbol(), | ||
| 156 | }); | ||
| 157 | }, | ||
| 158 | .expected_block_or_field => { | ||
| 159 | return stream.print("expected block or field, found '{s}'", .{ | ||
| 160 | token_tags[parse_error.token].symbol(), | ||
| 161 | }); | ||
| 162 | }, | ||
| 163 | .expected_container_members => { | ||
| 164 | return stream.print("expected test, comptime, var decl, or container field, found '{s}'", .{ | ||
| 165 | token_tags[parse_error.token].symbol(), | ||
| 166 | }); | ||
| 167 | }, | ||
| 168 | .expected_expr => { | ||
| 169 | return stream.print("expected expression, found '{s}'", .{ | ||
| 170 | token_tags[parse_error.token].symbol(), | ||
| 171 | }); | ||
| 172 | }, | ||
| 173 | .expected_expr_or_assignment => { | ||
| 174 | return stream.print("expected expression or assignment, found '{s}'", .{ | ||
| 175 | token_tags[parse_error.token].symbol(), | ||
| 176 | }); | ||
| 177 | }, | ||
| 178 | .expected_fn => { | ||
| 179 | return stream.print("expected function, found '{s}'", .{ | ||
| 180 | token_tags[parse_error.token].symbol(), | ||
| 181 | }); | ||
| 182 | }, | ||
| 183 | .expected_inlinable => { | ||
| 184 | return stream.print("expected 'while' or 'for', found '{s}'", .{ | ||
| 185 | token_tags[parse_error.token].symbol(), | ||
| 186 | }); | ||
| 187 | }, | ||
| 188 | .expected_labelable => { | ||
| 189 | return stream.print("expected 'while', 'for', 'inline', 'suspend', or '{{', found '{s}'", .{ | ||
| 190 | token_tags[parse_error.token].symbol(), | ||
| 191 | }); | ||
| 192 | }, | ||
| 193 | .expected_param_list => { | ||
| 194 | return stream.print("expected parameter list, found '{s}'", .{ | ||
| 195 | token_tags[parse_error.token].symbol(), | ||
| 196 | }); | ||
| 197 | }, | ||
| 198 | .expected_prefix_expr => { | ||
| 199 | return stream.print("expected prefix expression, found '{s}'", .{ | ||
| 200 | token_tags[parse_error.token].symbol(), | ||
| 201 | }); | ||
| 202 | }, | ||
| 203 | .expected_primary_type_expr => { | ||
| 204 | return stream.print("expected primary type expression, found '{s}'", .{ | ||
| 205 | token_tags[parse_error.token].symbol(), | ||
| 206 | }); | ||
| 207 | }, | ||
| 208 | .expected_pub_item => { | ||
| 209 | return stream.writeAll("expected function or variable declaration after pub"); | ||
| 210 | }, | ||
| 211 | .expected_return_type => { | ||
| 212 | return stream.print("expected return type expression, found '{s}'", .{ | ||
| 213 | token_tags[parse_error.token].symbol(), | ||
| 214 | }); | ||
| 215 | }, | ||
| 216 | .expected_semi_or_else => { | ||
| 217 | return stream.print("expected ';' or 'else', found '{s}'", .{ | ||
| 218 | token_tags[parse_error.token].symbol(), | ||
| 219 | }); | ||
| 220 | }, | ||
| 221 | .expected_semi_or_lbrace => { | ||
| 222 | return stream.print("expected ';' or '{{', found '{s}'", .{ | ||
| 223 | token_tags[parse_error.token].symbol(), | ||
| 224 | }); | ||
| 225 | }, | ||
| 226 | .expected_statement => { | ||
| 227 | return stream.print("expected statement, found '{s}'", .{ | ||
| 228 | token_tags[parse_error.token].symbol(), | ||
| 229 | }); | ||
| 230 | }, | ||
| 231 | .expected_string_literal => { | ||
| 232 | return stream.print("expected string literal, found '{s}'", .{ | ||
| 233 | token_tags[parse_error.token].symbol(), | ||
| 234 | }); | ||
| 235 | }, | ||
| 236 | .expected_suffix_op => { | ||
| 237 | return stream.print("expected pointer dereference, optional unwrap, or field access, found '{s}'", .{ | ||
| 238 | token_tags[parse_error.token].symbol(), | ||
| 239 | }); | ||
| 240 | }, | ||
| 241 | .expected_type_expr => { | ||
| 242 | return stream.print("expected type expression, found '{s}'", .{ | ||
| 243 | token_tags[parse_error.token].symbol(), | ||
| 244 | }); | ||
| 245 | }, | ||
| 246 | .expected_var_decl => { | ||
| 247 | return stream.print("expected variable declaration, found '{s}'", .{ | ||
| 248 | token_tags[parse_error.token].symbol(), | ||
| 249 | }); | ||
| 250 | }, | ||
| 251 | .expected_var_decl_or_fn => { | ||
| 252 | return stream.print("expected variable declaration or function, found '{s}'", .{ | ||
| 253 | token_tags[parse_error.token].symbol(), | ||
| 254 | }); | ||
| 255 | }, | ||
| 256 | .expected_loop_payload => { | ||
| 257 | return stream.print("expected loop payload, found '{s}'", .{ | ||
| 258 | token_tags[parse_error.token].symbol(), | ||
| 259 | }); | ||
| 260 | }, | ||
| 261 | .expected_container => { | ||
| 262 | return stream.print("expected a struct, enum or union, found '{s}'", .{ | ||
| 263 | token_tags[parse_error.token].symbol(), | ||
| 264 | }); | ||
| 265 | }, | ||
| 266 | .extra_align_qualifier => { | ||
| 267 | return stream.writeAll("extra align qualifier"); | ||
| 268 | }, | ||
| 269 | .extra_allowzero_qualifier => { | ||
| 270 | return stream.writeAll("extra allowzero qualifier"); | ||
| 271 | }, | ||
| 272 | .extra_const_qualifier => { | ||
| 273 | return stream.writeAll("extra const qualifier"); | ||
| 274 | }, | ||
| 275 | .extra_volatile_qualifier => { | ||
| 276 | return stream.writeAll("extra volatile qualifier"); | ||
| 277 | }, | ||
| 278 | .invalid_align => { | ||
| 279 | return stream.writeAll("alignment not allowed on arrays"); | ||
| 280 | }, | ||
| 281 | .invalid_and => { | ||
| 282 | return stream.writeAll("`&&` is invalid; note that `and` is boolean AND"); | ||
| 283 | }, | ||
| 284 | .invalid_bit_range => { | ||
| 285 | return stream.writeAll("bit range not allowed on slices and arrays"); | ||
| 286 | }, | ||
| 287 | .invalid_token => { | ||
| 288 | return stream.print("invalid token '{s}'", .{ | ||
| 289 | token_tags[parse_error.token].symbol(), | ||
| 290 | }); | ||
| 291 | }, | ||
| 292 | .same_line_doc_comment => { | ||
| 293 | return stream.writeAll("same line documentation comment"); | ||
| 294 | }, | ||
| 295 | .unattached_doc_comment => { | ||
| 296 | return stream.writeAll("unattached documentation comment"); | ||
| 297 | }, | ||
| 298 | |||
| 299 | .expected_token => { | ||
| 300 | const found_tag = token_tags[parse_error.token]; | ||
| 301 | const expected_symbol = parse_error.extra.expected_tag.symbol(); | ||
| 302 | switch (found_tag) { | ||
| 303 | .invalid => return stream.print("expected '{s}', found invalid bytes", .{ | ||
| 304 | expected_symbol, | ||
| 305 | }), | ||
| 306 | else => return stream.print("expected '{s}', found '{s}'", .{ | ||
| 307 | expected_symbol, found_tag.symbol(), | ||
| 308 | }), | ||
| 309 | } | ||
| 310 | }, | ||
| 111 | } | 311 | } |
| 112 | return index; | ||
| 113 | } | 312 | } |
| 114 | 313 | ||
| 115 | /// Skips over comments | 314 | pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex { |
| 116 | pub fn nextToken(self: *Tree, token_index: TokenIndex) TokenIndex { | 315 | const tags = tree.nodes.items(.tag); |
| 117 | var index = token_index + 1; | 316 | const datas = tree.nodes.items(.data); |
| 118 | while (self.token_ids[index] == Token.Id.LineComment) { | 317 | const main_tokens = tree.nodes.items(.main_token); |
| 119 | index += 1; | 318 | const token_tags = tree.tokens.items(.tag); |
| 120 | } | 319 | var end_offset: TokenIndex = 0; |
| 121 | return index; | 320 | var n = node; |
| 321 | while (true) switch (tags[n]) { | ||
| 322 | .root => return 0, | ||
| 323 | |||
| 324 | .test_decl, | ||
| 325 | .@"errdefer", | ||
| 326 | .@"defer", | ||
| 327 | .bool_not, | ||
| 328 | .negation, | ||
| 329 | .bit_not, | ||
| 330 | .negation_wrap, | ||
| 331 | .address_of, | ||
| 332 | .@"try", | ||
| 333 | .@"await", | ||
| 334 | .optional_type, | ||
| 335 | .@"switch", | ||
| 336 | .switch_comma, | ||
| 337 | .if_simple, | ||
| 338 | .@"if", | ||
| 339 | .@"suspend", | ||
| 340 | .@"resume", | ||
| 341 | .@"continue", | ||
| 342 | .@"break", | ||
| 343 | .@"return", | ||
| 344 | .anyframe_type, | ||
| 345 | .identifier, | ||
| 346 | .anyframe_literal, | ||
| 347 | .char_literal, | ||
| 348 | .integer_literal, | ||
| 349 | .float_literal, | ||
| 350 | .false_literal, | ||
| 351 | .true_literal, | ||
| 352 | .null_literal, | ||
| 353 | .undefined_literal, | ||
| 354 | .unreachable_literal, | ||
| 355 | .string_literal, | ||
| 356 | .multiline_string_literal, | ||
| 357 | .grouped_expression, | ||
| 358 | .builtin_call_two, | ||
| 359 | .builtin_call_two_comma, | ||
| 360 | .builtin_call, | ||
| 361 | .builtin_call_comma, | ||
| 362 | .error_set_decl, | ||
| 363 | .@"anytype", | ||
| 364 | .@"comptime", | ||
| 365 | .@"nosuspend", | ||
| 366 | .asm_simple, | ||
| 367 | .@"asm", | ||
| 368 | .array_type, | ||
| 369 | .array_type_sentinel, | ||
| 370 | .error_value, | ||
| 371 | => return main_tokens[n] - end_offset, | ||
| 372 | |||
| 373 | .array_init_dot, | ||
| 374 | .array_init_dot_comma, | ||
| 375 | .array_init_dot_two, | ||
| 376 | .array_init_dot_two_comma, | ||
| 377 | .struct_init_dot, | ||
| 378 | .struct_init_dot_comma, | ||
| 379 | .struct_init_dot_two, | ||
| 380 | .struct_init_dot_two_comma, | ||
| 381 | .enum_literal, | ||
| 382 | => return main_tokens[n] - 1 - end_offset, | ||
| 383 | |||
| 384 | .@"catch", | ||
| 385 | .field_access, | ||
| 386 | .unwrap_optional, | ||
| 387 | .equal_equal, | ||
| 388 | .bang_equal, | ||
| 389 | .less_than, | ||
| 390 | .greater_than, | ||
| 391 | .less_or_equal, | ||
| 392 | .greater_or_equal, | ||
| 393 | .assign_mul, | ||
| 394 | .assign_div, | ||
| 395 | .assign_mod, | ||
| 396 | .assign_add, | ||
| 397 | .assign_sub, | ||
| 398 | .assign_bit_shift_left, | ||
| 399 | .assign_bit_shift_right, | ||
| 400 | .assign_bit_and, | ||
| 401 | .assign_bit_xor, | ||
| 402 | .assign_bit_or, | ||
| 403 | .assign_mul_wrap, | ||
| 404 | .assign_add_wrap, | ||
| 405 | .assign_sub_wrap, | ||
| 406 | .assign, | ||
| 407 | .merge_error_sets, | ||
| 408 | .mul, | ||
| 409 | .div, | ||
| 410 | .mod, | ||
| 411 | .array_mult, | ||
| 412 | .mul_wrap, | ||
| 413 | .add, | ||
| 414 | .sub, | ||
| 415 | .array_cat, | ||
| 416 | .add_wrap, | ||
| 417 | .sub_wrap, | ||
| 418 | .bit_shift_left, | ||
| 419 | .bit_shift_right, | ||
| 420 | .bit_and, | ||
| 421 | .bit_xor, | ||
| 422 | .bit_or, | ||
| 423 | .@"orelse", | ||
| 424 | .bool_and, | ||
| 425 | .bool_or, | ||
| 426 | .slice_open, | ||
| 427 | .slice, | ||
| 428 | .slice_sentinel, | ||
| 429 | .deref, | ||
| 430 | .array_access, | ||
| 431 | .array_init_one, | ||
| 432 | .array_init_one_comma, | ||
| 433 | .array_init, | ||
| 434 | .array_init_comma, | ||
| 435 | .struct_init_one, | ||
| 436 | .struct_init_one_comma, | ||
| 437 | .struct_init, | ||
| 438 | .struct_init_comma, | ||
| 439 | .call_one, | ||
| 440 | .call_one_comma, | ||
| 441 | .call, | ||
| 442 | .call_comma, | ||
| 443 | .switch_range, | ||
| 444 | .error_union, | ||
| 445 | => n = datas[n].lhs, | ||
| 446 | |||
| 447 | .fn_decl, | ||
| 448 | .fn_proto_simple, | ||
| 449 | .fn_proto_multi, | ||
| 450 | .fn_proto_one, | ||
| 451 | .fn_proto, | ||
| 452 | => { | ||
| 453 | var i = main_tokens[n]; // fn token | ||
| 454 | while (i > 0) { | ||
| 455 | i -= 1; | ||
| 456 | switch (token_tags[i]) { | ||
| 457 | .keyword_extern, | ||
| 458 | .keyword_export, | ||
| 459 | .keyword_pub, | ||
| 460 | .keyword_threadlocal, | ||
| 461 | .string_literal, | ||
| 462 | => continue, | ||
| 463 | |||
| 464 | else => return i + 1 - end_offset, | ||
| 465 | } | ||
| 466 | } | ||
| 467 | return i - end_offset; | ||
| 468 | }, | ||
| 469 | |||
| 470 | .@"usingnamespace" => { | ||
| 471 | const main_token = main_tokens[n]; | ||
| 472 | if (main_token > 0 and token_tags[main_token - 1] == .keyword_pub) { | ||
| 473 | end_offset += 1; | ||
| 474 | } | ||
| 475 | return main_token - end_offset; | ||
| 476 | }, | ||
| 477 | |||
| 478 | .async_call_one, | ||
| 479 | .async_call_one_comma, | ||
| 480 | .async_call, | ||
| 481 | .async_call_comma, | ||
| 482 | => { | ||
| 483 | end_offset += 1; // async token | ||
| 484 | n = datas[n].lhs; | ||
| 485 | }, | ||
| 486 | |||
| 487 | .container_field_init, | ||
| 488 | .container_field_align, | ||
| 489 | .container_field, | ||
| 490 | => { | ||
| 491 | const name_token = main_tokens[n]; | ||
| 492 | if (name_token > 0 and token_tags[name_token - 1] == .keyword_comptime) { | ||
| 493 | end_offset += 1; | ||
| 494 | } | ||
| 495 | return name_token - end_offset; | ||
| 496 | }, | ||
| 497 | |||
| 498 | .global_var_decl, | ||
| 499 | .local_var_decl, | ||
| 500 | .simple_var_decl, | ||
| 501 | .aligned_var_decl, | ||
| 502 | => { | ||
| 503 | var i = main_tokens[n]; // mut token | ||
| 504 | while (i > 0) { | ||
| 505 | i -= 1; | ||
| 506 | switch (token_tags[i]) { | ||
| 507 | .keyword_extern, | ||
| 508 | .keyword_export, | ||
| 509 | .keyword_comptime, | ||
| 510 | .keyword_pub, | ||
| 511 | .keyword_threadlocal, | ||
| 512 | .string_literal, | ||
| 513 | => continue, | ||
| 514 | |||
| 515 | else => return i + 1 - end_offset, | ||
| 516 | } | ||
| 517 | } | ||
| 518 | return i - end_offset; | ||
| 519 | }, | ||
| 520 | |||
| 521 | .block, | ||
| 522 | .block_semicolon, | ||
| 523 | .block_two, | ||
| 524 | .block_two_semicolon, | ||
| 525 | => { | ||
| 526 | // Look for a label. | ||
| 527 | const lbrace = main_tokens[n]; | ||
| 528 | if (token_tags[lbrace - 1] == .colon) { | ||
| 529 | end_offset += 2; | ||
| 530 | } | ||
| 531 | return lbrace - end_offset; | ||
| 532 | }, | ||
| 533 | |||
| 534 | .container_decl, | ||
| 535 | .container_decl_trailing, | ||
| 536 | .container_decl_two, | ||
| 537 | .container_decl_two_trailing, | ||
| 538 | .container_decl_arg, | ||
| 539 | .container_decl_arg_trailing, | ||
| 540 | .tagged_union, | ||
| 541 | .tagged_union_trailing, | ||
| 542 | .tagged_union_two, | ||
| 543 | .tagged_union_two_trailing, | ||
| 544 | .tagged_union_enum_tag, | ||
| 545 | .tagged_union_enum_tag_trailing, | ||
| 546 | => { | ||
| 547 | const main_token = main_tokens[n]; | ||
| 548 | switch (token_tags[main_token - 1]) { | ||
| 549 | .keyword_packed, .keyword_extern => end_offset += 1, | ||
| 550 | else => {}, | ||
| 551 | } | ||
| 552 | return main_token - end_offset; | ||
| 553 | }, | ||
| 554 | |||
| 555 | .ptr_type_aligned, | ||
| 556 | .ptr_type_sentinel, | ||
| 557 | .ptr_type, | ||
| 558 | .ptr_type_bit_range, | ||
| 559 | => { | ||
| 560 | const main_token = main_tokens[n]; | ||
| 561 | return switch (token_tags[main_token]) { | ||
| 562 | .asterisk, | ||
| 563 | .asterisk_asterisk, | ||
| 564 | => switch (token_tags[main_token - 1]) { | ||
| 565 | .l_bracket => main_token - 1, | ||
| 566 | else => main_token, | ||
| 567 | }, | ||
| 568 | .l_bracket => main_token, | ||
| 569 | else => unreachable, | ||
| 570 | } - end_offset; | ||
| 571 | }, | ||
| 572 | |||
| 573 | .switch_case_one => { | ||
| 574 | if (datas[n].lhs == 0) { | ||
| 575 | return main_tokens[n] - 1 - end_offset; // else token | ||
| 576 | } else { | ||
| 577 | n = datas[n].lhs; | ||
| 578 | } | ||
| 579 | }, | ||
| 580 | .switch_case => { | ||
| 581 | const extra = tree.extraData(datas[n].lhs, Node.SubRange); | ||
| 582 | assert(extra.end - extra.start > 0); | ||
| 583 | n = tree.extra_data[extra.start]; | ||
| 584 | }, | ||
| 585 | |||
| 586 | .asm_output, .asm_input => { | ||
| 587 | assert(token_tags[main_tokens[n] - 1] == .l_bracket); | ||
| 588 | return main_tokens[n] - 1 - end_offset; | ||
| 589 | }, | ||
| 590 | |||
| 591 | .while_simple, | ||
| 592 | .while_cont, | ||
| 593 | .@"while", | ||
| 594 | .for_simple, | ||
| 595 | .@"for", | ||
| 596 | => { | ||
| 597 | // Look for a label and inline. | ||
| 598 | const main_token = main_tokens[n]; | ||
| 599 | var result = main_token; | ||
| 600 | if (token_tags[result - 1] == .keyword_inline) { | ||
| 601 | result -= 1; | ||
| 602 | } | ||
| 603 | if (token_tags[result - 1] == .colon) { | ||
| 604 | result -= 2; | ||
| 605 | } | ||
| 606 | return result - end_offset; | ||
| 607 | }, | ||
| 608 | }; | ||
| 122 | } | 609 | } |
| 123 | }; | ||
| 124 | 610 | ||
| 125 | pub const Error = union(enum) { | 611 | pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex { |
| 126 | InvalidToken: InvalidToken, | 612 | const tags = tree.nodes.items(.tag); |
| 127 | ExpectedContainerMembers: ExpectedContainerMembers, | 613 | const datas = tree.nodes.items(.data); |
| 128 | ExpectedStringLiteral: ExpectedStringLiteral, | 614 | const main_tokens = tree.nodes.items(.main_token); |
| 129 | ExpectedIntegerLiteral: ExpectedIntegerLiteral, | 615 | const token_starts = tree.tokens.items(.start); |
| 130 | ExpectedPubItem: ExpectedPubItem, | 616 | const token_tags = tree.tokens.items(.tag); |
| 131 | ExpectedIdentifier: ExpectedIdentifier, | 617 | var n = node; |
| 132 | ExpectedStatement: ExpectedStatement, | 618 | var end_offset: TokenIndex = 0; |
| 133 | ExpectedVarDeclOrFn: ExpectedVarDeclOrFn, | 619 | while (true) switch (tags[n]) { |
| 134 | ExpectedVarDecl: ExpectedVarDecl, | 620 | .root => return @intCast(TokenIndex, tree.tokens.len - 1), |
| 135 | ExpectedFn: ExpectedFn, | 621 | |
| 136 | ExpectedReturnType: ExpectedReturnType, | 622 | .@"usingnamespace", |
| 137 | ExpectedAggregateKw: ExpectedAggregateKw, | 623 | .bool_not, |
| 138 | UnattachedDocComment: UnattachedDocComment, | 624 | .negation, |
| 139 | ExpectedEqOrSemi: ExpectedEqOrSemi, | 625 | .bit_not, |
| 140 | ExpectedSemiOrLBrace: ExpectedSemiOrLBrace, | 626 | .negation_wrap, |
| 141 | ExpectedSemiOrElse: ExpectedSemiOrElse, | 627 | .address_of, |
| 142 | ExpectedLabelOrLBrace: ExpectedLabelOrLBrace, | 628 | .@"try", |
| 143 | ExpectedLBrace: ExpectedLBrace, | 629 | .@"await", |
| 144 | ExpectedColonOrRParen: ExpectedColonOrRParen, | 630 | .optional_type, |
| 145 | ExpectedLabelable: ExpectedLabelable, | 631 | .@"resume", |
| 146 | ExpectedInlinable: ExpectedInlinable, | 632 | .@"nosuspend", |
| 147 | ExpectedAsmOutputReturnOrType: ExpectedAsmOutputReturnOrType, | 633 | .@"comptime", |
| 148 | ExpectedCall: ExpectedCall, | 634 | => n = datas[n].lhs, |
| 149 | ExpectedCallOrFnProto: ExpectedCallOrFnProto, | 635 | |
| 150 | ExpectedSliceOrRBracket: ExpectedSliceOrRBracket, | 636 | .test_decl, |
| 151 | ExtraAlignQualifier: ExtraAlignQualifier, | 637 | .@"errdefer", |
| 152 | ExtraConstQualifier: ExtraConstQualifier, | 638 | .@"defer", |
| 153 | ExtraVolatileQualifier: ExtraVolatileQualifier, | 639 | .@"catch", |
| 154 | ExtraAllowZeroQualifier: ExtraAllowZeroQualifier, | 640 | .equal_equal, |
| 155 | ExpectedTypeExpr: ExpectedTypeExpr, | 641 | .bang_equal, |
| 156 | ExpectedPrimaryTypeExpr: ExpectedPrimaryTypeExpr, | 642 | .less_than, |
| 157 | ExpectedParamType: ExpectedParamType, | 643 | .greater_than, |
| 158 | ExpectedExpr: ExpectedExpr, | 644 | .less_or_equal, |
| 159 | ExpectedPrimaryExpr: ExpectedPrimaryExpr, | 645 | .greater_or_equal, |
| 160 | ExpectedToken: ExpectedToken, | 646 | .assign_mul, |
| 161 | ExpectedCommaOrEnd: ExpectedCommaOrEnd, | 647 | .assign_div, |
| 162 | ExpectedParamList: ExpectedParamList, | 648 | .assign_mod, |
| 163 | ExpectedPayload: ExpectedPayload, | 649 | .assign_add, |
| 164 | ExpectedBlockOrAssignment: ExpectedBlockOrAssignment, | 650 | .assign_sub, |
| 165 | ExpectedBlockOrExpression: ExpectedBlockOrExpression, | 651 | .assign_bit_shift_left, |
| 166 | ExpectedExprOrAssignment: ExpectedExprOrAssignment, | 652 | .assign_bit_shift_right, |
| 167 | ExpectedPrefixExpr: ExpectedPrefixExpr, | 653 | .assign_bit_and, |
| 168 | ExpectedLoopExpr: ExpectedLoopExpr, | 654 | .assign_bit_xor, |
| 169 | ExpectedDerefOrUnwrap: ExpectedDerefOrUnwrap, | 655 | .assign_bit_or, |
| 170 | ExpectedSuffixOp: ExpectedSuffixOp, | 656 | .assign_mul_wrap, |
| 171 | ExpectedBlockOrField: ExpectedBlockOrField, | 657 | .assign_add_wrap, |
| 172 | DeclBetweenFields: DeclBetweenFields, | 658 | .assign_sub_wrap, |
| 173 | InvalidAnd: InvalidAnd, | 659 | .assign, |
| 174 | AsteriskAfterPointerDereference: AsteriskAfterPointerDereference, | 660 | .merge_error_sets, |
| 175 | 661 | .mul, | |
| 176 | pub fn render(self: *const Error, tokens: []const Token.Id, stream: anytype) !void { | 662 | .div, |
| 177 | switch (self.*) { | 663 | .mod, |
| 178 | .InvalidToken => |*x| return x.render(tokens, stream), | 664 | .array_mult, |
| 179 | .ExpectedContainerMembers => |*x| return x.render(tokens, stream), | 665 | .mul_wrap, |
| 180 | .ExpectedStringLiteral => |*x| return x.render(tokens, stream), | 666 | .add, |
| 181 | .ExpectedIntegerLiteral => |*x| return x.render(tokens, stream), | 667 | .sub, |
| 182 | .ExpectedPubItem => |*x| return x.render(tokens, stream), | 668 | .array_cat, |
| 183 | .ExpectedIdentifier => |*x| return x.render(tokens, stream), | 669 | .add_wrap, |
| 184 | .ExpectedStatement => |*x| return x.render(tokens, stream), | 670 | .sub_wrap, |
| 185 | .ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream), | 671 | .bit_shift_left, |
| 186 | .ExpectedVarDecl => |*x| return x.render(tokens, stream), | 672 | .bit_shift_right, |
| 187 | .ExpectedFn => |*x| return x.render(tokens, stream), | 673 | .bit_and, |
| 188 | .ExpectedReturnType => |*x| return x.render(tokens, stream), | 674 | .bit_xor, |
| 189 | .ExpectedAggregateKw => |*x| return x.render(tokens, stream), | 675 | .bit_or, |
| 190 | .UnattachedDocComment => |*x| return x.render(tokens, stream), | 676 | .@"orelse", |
| 191 | .ExpectedEqOrSemi => |*x| return x.render(tokens, stream), | 677 | .bool_and, |
| 192 | .ExpectedSemiOrLBrace => |*x| return x.render(tokens, stream), | 678 | .bool_or, |
| 193 | .ExpectedSemiOrElse => |*x| return x.render(tokens, stream), | 679 | .anyframe_type, |
| 194 | .ExpectedLabelOrLBrace => |*x| return x.render(tokens, stream), | 680 | .error_union, |
| 195 | .ExpectedLBrace => |*x| return x.render(tokens, stream), | 681 | .if_simple, |
| 196 | .ExpectedColonOrRParen => |*x| return x.render(tokens, stream), | 682 | .while_simple, |
| 197 | .ExpectedLabelable => |*x| return x.render(tokens, stream), | 683 | .for_simple, |
| 198 | .ExpectedInlinable => |*x| return x.render(tokens, stream), | 684 | .fn_proto_simple, |
| 199 | .ExpectedAsmOutputReturnOrType => |*x| return x.render(tokens, stream), | 685 | .fn_proto_multi, |
| 200 | .ExpectedCall => |*x| return x.render(tokens, stream), | 686 | .ptr_type_aligned, |
| 201 | .ExpectedCallOrFnProto => |*x| return x.render(tokens, stream), | 687 | .ptr_type_sentinel, |
| 202 | .ExpectedSliceOrRBracket => |*x| return x.render(tokens, stream), | 688 | .ptr_type, |
| 203 | .ExtraAlignQualifier => |*x| return x.render(tokens, stream), | 689 | .ptr_type_bit_range, |
| 204 | .ExtraConstQualifier => |*x| return x.render(tokens, stream), | 690 | .array_type, |
| 205 | .ExtraVolatileQualifier => |*x| return x.render(tokens, stream), | 691 | .switch_case_one, |
| 206 | .ExtraAllowZeroQualifier => |*x| return x.render(tokens, stream), | 692 | .switch_case, |
| 207 | .ExpectedTypeExpr => |*x| return x.render(tokens, stream), | 693 | .switch_range, |
| 208 | .ExpectedPrimaryTypeExpr => |*x| return x.render(tokens, stream), | 694 | => n = datas[n].rhs, |
| 209 | .ExpectedParamType => |*x| return x.render(tokens, stream), | 695 | |
| 210 | .ExpectedExpr => |*x| return x.render(tokens, stream), | 696 | .field_access, |
| 211 | .ExpectedPrimaryExpr => |*x| return x.render(tokens, stream), | 697 | .unwrap_optional, |
| 212 | .ExpectedToken => |*x| return x.render(tokens, stream), | 698 | .grouped_expression, |
| 213 | .ExpectedCommaOrEnd => |*x| return x.render(tokens, stream), | 699 | .multiline_string_literal, |
| 214 | .ExpectedParamList => |*x| return x.render(tokens, stream), | 700 | .error_set_decl, |
| 215 | .ExpectedPayload => |*x| return x.render(tokens, stream), | 701 | .asm_simple, |
| 216 | .ExpectedBlockOrAssignment => |*x| return x.render(tokens, stream), | 702 | .asm_output, |
| 217 | .ExpectedBlockOrExpression => |*x| return x.render(tokens, stream), | 703 | .asm_input, |
| 218 | .ExpectedExprOrAssignment => |*x| return x.render(tokens, stream), | 704 | .error_value, |
| 219 | .ExpectedPrefixExpr => |*x| return x.render(tokens, stream), | 705 | => return datas[n].rhs + end_offset, |
| 220 | .ExpectedLoopExpr => |*x| return x.render(tokens, stream), | 706 | |
| 221 | .ExpectedDerefOrUnwrap => |*x| return x.render(tokens, stream), | 707 | .@"anytype", |
| 222 | .ExpectedSuffixOp => |*x| return x.render(tokens, stream), | 708 | .anyframe_literal, |
| 223 | .ExpectedBlockOrField => |*x| return x.render(tokens, stream), | 709 | .char_literal, |
| 224 | .DeclBetweenFields => |*x| return x.render(tokens, stream), | 710 | .integer_literal, |
| 225 | .InvalidAnd => |*x| return x.render(tokens, stream), | 711 | .float_literal, |
| 226 | .AsteriskAfterPointerDereference => |*x| return x.render(tokens, stream), | 712 | .false_literal, |
| 227 | } | 713 | .true_literal, |
| 714 | .null_literal, | ||
| 715 | .undefined_literal, | ||
| 716 | .unreachable_literal, | ||
| 717 | .identifier, | ||
| 718 | .deref, | ||
| 719 | .enum_literal, | ||
| 720 | .string_literal, | ||
| 721 | => return main_tokens[n] + end_offset, | ||
| 722 | |||
| 723 | .@"return" => if (datas[n].lhs != 0) { | ||
| 724 | n = datas[n].lhs; | ||
| 725 | } else { | ||
| 726 | return main_tokens[n] + end_offset; | ||
| 727 | }, | ||
| 728 | |||
| 729 | .call, .async_call => { | ||
| 730 | end_offset += 1; // for the rparen | ||
| 731 | const params = tree.extraData(datas[n].rhs, Node.SubRange); | ||
| 732 | if (params.end - params.start == 0) { | ||
| 733 | return main_tokens[n] + end_offset; | ||
| 734 | } | ||
| 735 | n = tree.extra_data[params.end - 1]; // last parameter | ||
| 736 | }, | ||
| 737 | .tagged_union_enum_tag => { | ||
| 738 | const members = tree.extraData(datas[n].rhs, Node.SubRange); | ||
| 739 | if (members.end - members.start == 0) { | ||
| 740 | end_offset += 4; // for the rparen + rparen + lbrace + rbrace | ||
| 741 | n = datas[n].lhs; | ||
| 742 | } else { | ||
| 743 | end_offset += 1; // for the rbrace | ||
| 744 | n = tree.extra_data[members.end - 1]; // last parameter | ||
| 745 | } | ||
| 746 | }, | ||
| 747 | .call_comma, | ||
| 748 | .async_call_comma, | ||
| 749 | .tagged_union_enum_tag_trailing, | ||
| 750 | => { | ||
| 751 | end_offset += 2; // for the comma/semicolon + rparen/rbrace | ||
| 752 | const params = tree.extraData(datas[n].rhs, Node.SubRange); | ||
| 753 | assert(params.end > params.start); | ||
| 754 | n = tree.extra_data[params.end - 1]; // last parameter | ||
| 755 | }, | ||
| 756 | .@"switch" => { | ||
| 757 | const cases = tree.extraData(datas[n].rhs, Node.SubRange); | ||
| 758 | if (cases.end - cases.start == 0) { | ||
| 759 | end_offset += 3; // rparen, lbrace, rbrace | ||
| 760 | n = datas[n].lhs; // condition expression | ||
| 761 | } else { | ||
| 762 | end_offset += 1; // for the rbrace | ||
| 763 | n = tree.extra_data[cases.end - 1]; // last case | ||
| 764 | } | ||
| 765 | }, | ||
| 766 | .container_decl_arg => { | ||
| 767 | const members = tree.extraData(datas[n].rhs, Node.SubRange); | ||
| 768 | if (members.end - members.start == 0) { | ||
| 769 | end_offset += 1; // for the rparen | ||
| 770 | n = datas[n].lhs; | ||
| 771 | } else { | ||
| 772 | end_offset += 1; // for the rbrace | ||
| 773 | n = tree.extra_data[members.end - 1]; // last parameter | ||
| 774 | } | ||
| 775 | }, | ||
| 776 | .@"asm" => { | ||
| 777 | const extra = tree.extraData(datas[n].rhs, Node.Asm); | ||
| 778 | return extra.rparen + end_offset; | ||
| 779 | }, | ||
| 780 | .array_init, | ||
| 781 | .struct_init, | ||
| 782 | => { | ||
| 783 | const elements = tree.extraData(datas[n].rhs, Node.SubRange); | ||
| 784 | assert(elements.end - elements.start > 0); | ||
| 785 | end_offset += 1; // for the rbrace | ||
| 786 | n = tree.extra_data[elements.end - 1]; // last element | ||
| 787 | }, | ||
| 788 | .array_init_comma, | ||
| 789 | .struct_init_comma, | ||
| 790 | .container_decl_arg_trailing, | ||
| 791 | .switch_comma, | ||
| 792 | => { | ||
| 793 | const members = tree.extraData(datas[n].rhs, Node.SubRange); | ||
| 794 | assert(members.end - members.start > 0); | ||
| 795 | end_offset += 2; // for the comma + rbrace | ||
| 796 | n = tree.extra_data[members.end - 1]; // last parameter | ||
| 797 | }, | ||
| 798 | .array_init_dot, | ||
| 799 | .struct_init_dot, | ||
| 800 | .block, | ||
| 801 | .container_decl, | ||
| 802 | .tagged_union, | ||
| 803 | .builtin_call, | ||
| 804 | => { | ||
| 805 | assert(datas[n].rhs - datas[n].lhs > 0); | ||
| 806 | end_offset += 1; // for the rbrace | ||
| 807 | n = tree.extra_data[datas[n].rhs - 1]; // last statement | ||
| 808 | }, | ||
| 809 | .array_init_dot_comma, | ||
| 810 | .struct_init_dot_comma, | ||
| 811 | .block_semicolon, | ||
| 812 | .container_decl_trailing, | ||
| 813 | .tagged_union_trailing, | ||
| 814 | .builtin_call_comma, | ||
| 815 | => { | ||
| 816 | assert(datas[n].rhs - datas[n].lhs > 0); | ||
| 817 | end_offset += 2; // for the comma/semicolon + rbrace/rparen | ||
| 818 | n = tree.extra_data[datas[n].rhs - 1]; // last member | ||
| 819 | }, | ||
| 820 | .call_one, | ||
| 821 | .async_call_one, | ||
| 822 | .array_access, | ||
| 823 | => { | ||
| 824 | end_offset += 1; // for the rparen/rbracket | ||
| 825 | if (datas[n].rhs == 0) { | ||
| 826 | return main_tokens[n] + end_offset; | ||
| 827 | } | ||
| 828 | n = datas[n].rhs; | ||
| 829 | }, | ||
| 830 | .array_init_dot_two, | ||
| 831 | .block_two, | ||
| 832 | .builtin_call_two, | ||
| 833 | .struct_init_dot_two, | ||
| 834 | .container_decl_two, | ||
| 835 | .tagged_union_two, | ||
| 836 | => { | ||
| 837 | if (datas[n].rhs != 0) { | ||
| 838 | end_offset += 1; // for the rparen/rbrace | ||
| 839 | n = datas[n].rhs; | ||
| 840 | } else if (datas[n].lhs != 0) { | ||
| 841 | end_offset += 1; // for the rparen/rbrace | ||
| 842 | n = datas[n].lhs; | ||
| 843 | } else { | ||
| 844 | switch (tags[n]) { | ||
| 845 | .array_init_dot_two, | ||
| 846 | .block_two, | ||
| 847 | .struct_init_dot_two, | ||
| 848 | => end_offset += 1, // rbrace | ||
| 849 | .builtin_call_two => end_offset += 2, // lparen/lbrace + rparen/rbrace | ||
| 850 | .container_decl_two => { | ||
| 851 | var i: u32 = 2; // lbrace + rbrace | ||
| 852 | while (token_tags[main_tokens[n] + i] == .container_doc_comment) i += 1; | ||
| 853 | end_offset += i; | ||
| 854 | }, | ||
| 855 | .tagged_union_two => { | ||
| 856 | var i: u32 = 5; // (enum) {} | ||
| 857 | while (token_tags[main_tokens[n] + i] == .container_doc_comment) i += 1; | ||
| 858 | end_offset += i; | ||
| 859 | }, | ||
| 860 | else => unreachable, | ||
| 861 | } | ||
| 862 | return main_tokens[n] + end_offset; | ||
| 863 | } | ||
| 864 | }, | ||
| 865 | .array_init_dot_two_comma, | ||
| 866 | .builtin_call_two_comma, | ||
| 867 | .block_two_semicolon, | ||
| 868 | .struct_init_dot_two_comma, | ||
| 869 | .container_decl_two_trailing, | ||
| 870 | .tagged_union_two_trailing, | ||
| 871 | => { | ||
| 872 | end_offset += 2; // for the comma/semicolon + rbrace/rparen | ||
| 873 | if (datas[n].rhs != 0) { | ||
| 874 | n = datas[n].rhs; | ||
| 875 | } else if (datas[n].lhs != 0) { | ||
| 876 | n = datas[n].lhs; | ||
| 877 | } else { | ||
| 878 | unreachable; | ||
| 879 | } | ||
| 880 | }, | ||
| 881 | .simple_var_decl => { | ||
| 882 | if (datas[n].rhs != 0) { | ||
| 883 | n = datas[n].rhs; | ||
| 884 | } else if (datas[n].lhs != 0) { | ||
| 885 | n = datas[n].lhs; | ||
| 886 | } else { | ||
| 887 | end_offset += 1; // from mut token to name | ||
| 888 | return main_tokens[n] + end_offset; | ||
| 889 | } | ||
| 890 | }, | ||
| 891 | .aligned_var_decl => { | ||
| 892 | if (datas[n].rhs != 0) { | ||
| 893 | n = datas[n].rhs; | ||
| 894 | } else if (datas[n].lhs != 0) { | ||
| 895 | end_offset += 1; // for the rparen | ||
| 896 | n = datas[n].lhs; | ||
| 897 | } else { | ||
| 898 | end_offset += 1; // from mut token to name | ||
| 899 | return main_tokens[n] + end_offset; | ||
| 900 | } | ||
| 901 | }, | ||
| 902 | .global_var_decl => { | ||
| 903 | if (datas[n].rhs != 0) { | ||
| 904 | n = datas[n].rhs; | ||
| 905 | } else { | ||
| 906 | const extra = tree.extraData(datas[n].lhs, Node.GlobalVarDecl); | ||
| 907 | if (extra.section_node != 0) { | ||
| 908 | end_offset += 1; // for the rparen | ||
| 909 | n = extra.section_node; | ||
| 910 | } else if (extra.align_node != 0) { | ||
| 911 | end_offset += 1; // for the rparen | ||
| 912 | n = extra.align_node; | ||
| 913 | } else if (extra.type_node != 0) { | ||
| 914 | n = extra.type_node; | ||
| 915 | } else { | ||
| 916 | end_offset += 1; // from mut token to name | ||
| 917 | return main_tokens[n] + end_offset; | ||
| 918 | } | ||
| 919 | } | ||
| 920 | }, | ||
| 921 | .local_var_decl => { | ||
| 922 | if (datas[n].rhs != 0) { | ||
| 923 | n = datas[n].rhs; | ||
| 924 | } else { | ||
| 925 | const extra = tree.extraData(datas[n].lhs, Node.LocalVarDecl); | ||
| 926 | if (extra.align_node != 0) { | ||
| 927 | end_offset += 1; // for the rparen | ||
| 928 | n = extra.align_node; | ||
| 929 | } else if (extra.type_node != 0) { | ||
| 930 | n = extra.type_node; | ||
| 931 | } else { | ||
| 932 | end_offset += 1; // from mut token to name | ||
| 933 | return main_tokens[n] + end_offset; | ||
| 934 | } | ||
| 935 | } | ||
| 936 | }, | ||
| 937 | .container_field_init => { | ||
| 938 | if (datas[n].rhs != 0) { | ||
| 939 | n = datas[n].rhs; | ||
| 940 | } else if (datas[n].lhs != 0) { | ||
| 941 | n = datas[n].lhs; | ||
| 942 | } else { | ||
| 943 | return main_tokens[n] + end_offset; | ||
| 944 | } | ||
| 945 | }, | ||
| 946 | .container_field_align => { | ||
| 947 | if (datas[n].rhs != 0) { | ||
| 948 | end_offset += 1; // for the rparen | ||
| 949 | n = datas[n].rhs; | ||
| 950 | } else if (datas[n].lhs != 0) { | ||
| 951 | n = datas[n].lhs; | ||
| 952 | } else { | ||
| 953 | return main_tokens[n] + end_offset; | ||
| 954 | } | ||
| 955 | }, | ||
| 956 | .container_field => { | ||
| 957 | const extra = tree.extraData(datas[n].rhs, Node.ContainerField); | ||
| 958 | if (extra.value_expr != 0) { | ||
| 959 | n = extra.value_expr; | ||
| 960 | } else if (extra.align_expr != 0) { | ||
| 961 | end_offset += 1; // for the rparen | ||
| 962 | n = extra.align_expr; | ||
| 963 | } else if (datas[n].lhs != 0) { | ||
| 964 | n = datas[n].lhs; | ||
| 965 | } else { | ||
| 966 | return main_tokens[n] + end_offset; | ||
| 967 | } | ||
| 968 | }, | ||
| 969 | |||
| 970 | .array_init_one, | ||
| 971 | .struct_init_one, | ||
| 972 | => { | ||
| 973 | end_offset += 1; // rbrace | ||
| 974 | if (datas[n].rhs == 0) { | ||
| 975 | return main_tokens[n] + end_offset; | ||
| 976 | } else { | ||
| 977 | n = datas[n].rhs; | ||
| 978 | } | ||
| 979 | }, | ||
| 980 | .slice_open, | ||
| 981 | .call_one_comma, | ||
| 982 | .async_call_one_comma, | ||
| 983 | .array_init_one_comma, | ||
| 984 | .struct_init_one_comma, | ||
| 985 | => { | ||
| 986 | end_offset += 2; // ellipsis2 + rbracket, or comma + rparen | ||
| 987 | n = datas[n].rhs; | ||
| 988 | assert(n != 0); | ||
| 989 | }, | ||
| 990 | .slice => { | ||
| 991 | const extra = tree.extraData(datas[n].rhs, Node.Slice); | ||
| 992 | assert(extra.end != 0); // should have used SliceOpen | ||
| 993 | end_offset += 1; // rbracket | ||
| 994 | n = extra.end; | ||
| 995 | }, | ||
| 996 | .slice_sentinel => { | ||
| 997 | const extra = tree.extraData(datas[n].rhs, Node.SliceSentinel); | ||
| 998 | assert(extra.sentinel != 0); // should have used Slice | ||
| 999 | end_offset += 1; // rbracket | ||
| 1000 | n = extra.sentinel; | ||
| 1001 | }, | ||
| 1002 | |||
| 1003 | .@"continue" => { | ||
| 1004 | if (datas[n].lhs != 0) { | ||
| 1005 | return datas[n].lhs + end_offset; | ||
| 1006 | } else { | ||
| 1007 | return main_tokens[n] + end_offset; | ||
| 1008 | } | ||
| 1009 | }, | ||
| 1010 | .@"break" => { | ||
| 1011 | if (datas[n].rhs != 0) { | ||
| 1012 | n = datas[n].rhs; | ||
| 1013 | } else if (datas[n].lhs != 0) { | ||
| 1014 | return datas[n].lhs + end_offset; | ||
| 1015 | } else { | ||
| 1016 | return main_tokens[n] + end_offset; | ||
| 1017 | } | ||
| 1018 | }, | ||
| 1019 | .fn_decl => { | ||
| 1020 | if (datas[n].rhs != 0) { | ||
| 1021 | n = datas[n].rhs; | ||
| 1022 | } else { | ||
| 1023 | n = datas[n].lhs; | ||
| 1024 | } | ||
| 1025 | }, | ||
| 1026 | .fn_proto_one => { | ||
| 1027 | const extra = tree.extraData(datas[n].lhs, Node.FnProtoOne); | ||
| 1028 | // linksection, callconv, align can appear in any order, so we | ||
| 1029 | // find the last one here. | ||
| 1030 | var max_node: Node.Index = datas[n].rhs; | ||
| 1031 | var max_start = token_starts[main_tokens[max_node]]; | ||
| 1032 | var max_offset: TokenIndex = 0; | ||
| 1033 | if (extra.align_expr != 0) { | ||
| 1034 | const start = token_starts[main_tokens[extra.align_expr]]; | ||
| 1035 | if (start > max_start) { | ||
| 1036 | max_node = extra.align_expr; | ||
| 1037 | max_start = start; | ||
| 1038 | max_offset = 1; // for the rparen | ||
| 1039 | } | ||
| 1040 | } | ||
| 1041 | if (extra.section_expr != 0) { | ||
| 1042 | const start = token_starts[main_tokens[extra.section_expr]]; | ||
| 1043 | if (start > max_start) { | ||
| 1044 | max_node = extra.section_expr; | ||
| 1045 | max_start = start; | ||
| 1046 | max_offset = 1; // for the rparen | ||
| 1047 | } | ||
| 1048 | } | ||
| 1049 | if (extra.callconv_expr != 0) { | ||
| 1050 | const start = token_starts[main_tokens[extra.callconv_expr]]; | ||
| 1051 | if (start > max_start) { | ||
| 1052 | max_node = extra.callconv_expr; | ||
| 1053 | max_start = start; | ||
| 1054 | max_offset = 1; // for the rparen | ||
| 1055 | } | ||
| 1056 | } | ||
| 1057 | n = max_node; | ||
| 1058 | end_offset += max_offset; | ||
| 1059 | }, | ||
| 1060 | .fn_proto => { | ||
| 1061 | const extra = tree.extraData(datas[n].lhs, Node.FnProto); | ||
| 1062 | // linksection, callconv, align can appear in any order, so we | ||
| 1063 | // find the last one here. | ||
| 1064 | var max_node: Node.Index = datas[n].rhs; | ||
| 1065 | var max_start = token_starts[main_tokens[max_node]]; | ||
| 1066 | var max_offset: TokenIndex = 0; | ||
| 1067 | if (extra.align_expr != 0) { | ||
| 1068 | const start = token_starts[main_tokens[extra.align_expr]]; | ||
| 1069 | if (start > max_start) { | ||
| 1070 | max_node = extra.align_expr; | ||
| 1071 | max_start = start; | ||
| 1072 | max_offset = 1; // for the rparen | ||
| 1073 | } | ||
| 1074 | } | ||
| 1075 | if (extra.section_expr != 0) { | ||
| 1076 | const start = token_starts[main_tokens[extra.section_expr]]; | ||
| 1077 | if (start > max_start) { | ||
| 1078 | max_node = extra.section_expr; | ||
| 1079 | max_start = start; | ||
| 1080 | max_offset = 1; // for the rparen | ||
| 1081 | } | ||
| 1082 | } | ||
| 1083 | if (extra.callconv_expr != 0) { | ||
| 1084 | const start = token_starts[main_tokens[extra.callconv_expr]]; | ||
| 1085 | if (start > max_start) { | ||
| 1086 | max_node = extra.callconv_expr; | ||
| 1087 | max_start = start; | ||
| 1088 | max_offset = 1; // for the rparen | ||
| 1089 | } | ||
| 1090 | } | ||
| 1091 | n = max_node; | ||
| 1092 | end_offset += max_offset; | ||
| 1093 | }, | ||
| 1094 | .while_cont => { | ||
| 1095 | const extra = tree.extraData(datas[n].rhs, Node.WhileCont); | ||
| 1096 | assert(extra.then_expr != 0); | ||
| 1097 | n = extra.then_expr; | ||
| 1098 | }, | ||
| 1099 | .@"while" => { | ||
| 1100 | const extra = tree.extraData(datas[n].rhs, Node.While); | ||
| 1101 | assert(extra.else_expr != 0); | ||
| 1102 | n = extra.else_expr; | ||
| 1103 | }, | ||
| 1104 | .@"if", .@"for" => { | ||
| 1105 | const extra = tree.extraData(datas[n].rhs, Node.If); | ||
| 1106 | assert(extra.else_expr != 0); | ||
| 1107 | n = extra.else_expr; | ||
| 1108 | }, | ||
| 1109 | .@"suspend" => { | ||
| 1110 | if (datas[n].lhs != 0) { | ||
| 1111 | n = datas[n].lhs; | ||
| 1112 | } else { | ||
| 1113 | return main_tokens[n] + end_offset; | ||
| 1114 | } | ||
| 1115 | }, | ||
| 1116 | .array_type_sentinel => { | ||
| 1117 | const extra = tree.extraData(datas[n].rhs, Node.ArrayTypeSentinel); | ||
| 1118 | n = extra.elem_type; | ||
| 1119 | }, | ||
| 1120 | }; | ||
| 228 | } | 1121 | } |
| 229 | 1122 | ||
| 230 | pub fn loc(self: *const Error) TokenIndex { | 1123 | pub fn tokensOnSameLine(tree: Tree, token1: TokenIndex, token2: TokenIndex) bool { |
| 231 | switch (self.*) { | 1124 | const token_starts = tree.tokens.items(.start); |
| 232 | .InvalidToken => |x| return x.token, | 1125 | const source = tree.source[token_starts[token1]..token_starts[token2]]; |
| 233 | .ExpectedContainerMembers => |x| return x.token, | 1126 | return mem.indexOfScalar(u8, source, '\n') == null; |
| 234 | .ExpectedStringLiteral => |x| return x.token, | ||
| 235 | .ExpectedIntegerLiteral => |x| return x.token, | ||
| 236 | .ExpectedPubItem => |x| return x.token, | ||
| 237 | .ExpectedIdentifier => |x| return x.token, | ||
| 238 | .ExpectedStatement => |x| return x.token, | ||
| 239 | .ExpectedVarDeclOrFn => |x| return x.token, | ||
| 240 | .ExpectedVarDecl => |x| return x.token, | ||
| 241 | .ExpectedFn => |x| return x.token, | ||
| 242 | .ExpectedReturnType => |x| return x.token, | ||
| 243 | .ExpectedAggregateKw => |x| return x.token, | ||
| 244 | .UnattachedDocComment => |x| return x.token, | ||
| 245 | .ExpectedEqOrSemi => |x| return x.token, | ||
| 246 | .ExpectedSemiOrLBrace => |x| return x.token, | ||
| 247 | .ExpectedSemiOrElse => |x| return x.token, | ||
| 248 | .ExpectedLabelOrLBrace => |x| return x.token, | ||
| 249 | .ExpectedLBrace => |x| return x.token, | ||
| 250 | .ExpectedColonOrRParen => |x| return x.token, | ||
| 251 | .ExpectedLabelable => |x| return x.token, | ||
| 252 | .ExpectedInlinable => |x| return x.token, | ||
| 253 | .ExpectedAsmOutputReturnOrType => |x| return x.token, | ||
| 254 | .ExpectedCall => |x| return x.node.firstToken(), | ||
| 255 | .ExpectedCallOrFnProto => |x| return x.node.firstToken(), | ||
| 256 | .ExpectedSliceOrRBracket => |x| return x.token, | ||
| 257 | .ExtraAlignQualifier => |x| return x.token, | ||
| 258 | .ExtraConstQualifier => |x| return x.token, | ||
| 259 | .ExtraVolatileQualifier => |x| return x.token, | ||
| 260 | .ExtraAllowZeroQualifier => |x| return x.token, | ||
| 261 | .ExpectedTypeExpr => |x| return x.token, | ||
| 262 | .ExpectedPrimaryTypeExpr => |x| return x.token, | ||
| 263 | .ExpectedParamType => |x| return x.token, | ||
| 264 | .ExpectedExpr => |x| return x.token, | ||
| 265 | .ExpectedPrimaryExpr => |x| return x.token, | ||
| 266 | .ExpectedToken => |x| return x.token, | ||
| 267 | .ExpectedCommaOrEnd => |x| return x.token, | ||
| 268 | .ExpectedParamList => |x| return x.token, | ||
| 269 | .ExpectedPayload => |x| return x.token, | ||
| 270 | .ExpectedBlockOrAssignment => |x| return x.token, | ||
| 271 | .ExpectedBlockOrExpression => |x| return x.token, | ||
| 272 | .ExpectedExprOrAssignment => |x| return x.token, | ||
| 273 | .ExpectedPrefixExpr => |x| return x.token, | ||
| 274 | .ExpectedLoopExpr => |x| return x.token, | ||
| 275 | .ExpectedDerefOrUnwrap => |x| return x.token, | ||
| 276 | .ExpectedSuffixOp => |x| return x.token, | ||
| 277 | .ExpectedBlockOrField => |x| return x.token, | ||
| 278 | .DeclBetweenFields => |x| return x.token, | ||
| 279 | .InvalidAnd => |x| return x.token, | ||
| 280 | .AsteriskAfterPointerDereference => |x| return x.token, | ||
| 281 | } | ||
| 282 | } | 1127 | } |
| 283 | 1128 | ||
| 284 | pub const InvalidToken = SingleTokenError("Invalid token '{s}'"); | 1129 | pub fn getNodeSource(tree: Tree, node: Node.Index) []const u8 { |
| 285 | pub const ExpectedContainerMembers = SingleTokenError("Expected test, comptime, var decl, or container field, found '{s}'"); | 1130 | const token_starts = tree.tokens.items(.start); |
| 286 | pub const ExpectedStringLiteral = SingleTokenError("Expected string literal, found '{s}'"); | 1131 | const first_token = tree.firstToken(node); |
| 287 | pub const ExpectedIntegerLiteral = SingleTokenError("Expected integer literal, found '{s}'"); | 1132 | const last_token = tree.lastToken(node); |
| 288 | pub const ExpectedIdentifier = SingleTokenError("Expected identifier, found '{s}'"); | 1133 | const start = token_starts[first_token]; |
| 289 | pub const ExpectedStatement = SingleTokenError("Expected statement, found '{s}'"); | 1134 | const end = token_starts[last_token] + tree.tokenSlice(last_token).len; |
| 290 | pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found '{s}'"); | 1135 | return tree.source[start..end]; |
| 291 | pub const ExpectedVarDecl = SingleTokenError("Expected variable declaration, found '{s}'"); | 1136 | } |
| 292 | pub const ExpectedFn = SingleTokenError("Expected function, found '{s}'"); | ||
| 293 | pub const ExpectedReturnType = SingleTokenError("Expected 'var' or return type expression, found '{s}'"); | ||
| 294 | pub const ExpectedAggregateKw = SingleTokenError("Expected '" ++ Token.Id.Keyword_struct.symbol() ++ "', '" ++ Token.Id.Keyword_union.symbol() ++ "', '" ++ Token.Id.Keyword_enum.symbol() ++ "', or '" ++ Token.Id.Keyword_opaque.symbol() ++ "', found '{s}'"); | ||
| 295 | pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found '{s}'"); | ||
| 296 | pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found '{s}'"); | ||
| 297 | pub const ExpectedSemiOrElse = SingleTokenError("Expected ';' or 'else', found '{s}'"); | ||
| 298 | pub const ExpectedLBrace = SingleTokenError("Expected '{{', found '{s}'"); | ||
| 299 | pub const ExpectedLabelOrLBrace = SingleTokenError("Expected label or '{{', found '{s}'"); | ||
| 300 | pub const ExpectedColonOrRParen = SingleTokenError("Expected ':' or ')', found '{s}'"); | ||
| 301 | pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found '{s}'"); | ||
| 302 | pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found '{s}'"); | ||
| 303 | pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or '" ++ Token.Id.Identifier.symbol() ++ "', found '{s}'"); | ||
| 304 | pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found '{s}'"); | ||
| 305 | pub const ExpectedTypeExpr = SingleTokenError("Expected type expression, found '{s}'"); | ||
| 306 | pub const ExpectedPrimaryTypeExpr = SingleTokenError("Expected primary type expression, found '{s}'"); | ||
| 307 | pub const ExpectedExpr = SingleTokenError("Expected expression, found '{s}'"); | ||
| 308 | pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found '{s}'"); | ||
| 309 | pub const ExpectedParamList = SingleTokenError("Expected parameter list, found '{s}'"); | ||
| 310 | pub const ExpectedPayload = SingleTokenError("Expected loop payload, found '{s}'"); | ||
| 311 | pub const ExpectedBlockOrAssignment = SingleTokenError("Expected block or assignment, found '{s}'"); | ||
| 312 | pub const ExpectedBlockOrExpression = SingleTokenError("Expected block or expression, found '{s}'"); | ||
| 313 | pub const ExpectedExprOrAssignment = SingleTokenError("Expected expression or assignment, found '{s}'"); | ||
| 314 | pub const ExpectedPrefixExpr = SingleTokenError("Expected prefix expression, found '{s}'"); | ||
| 315 | pub const ExpectedLoopExpr = SingleTokenError("Expected loop expression, found '{s}'"); | ||
| 316 | pub const ExpectedDerefOrUnwrap = SingleTokenError("Expected pointer dereference or optional unwrap, found '{s}'"); | ||
| 317 | pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{s}'"); | ||
| 318 | pub const ExpectedBlockOrField = SingleTokenError("Expected block or field, found '{s}'"); | ||
| 319 | |||
| 320 | pub const ExpectedParamType = SimpleError("Expected parameter type"); | ||
| 321 | pub const ExpectedPubItem = SimpleError("Expected function or variable declaration after pub"); | ||
| 322 | pub const UnattachedDocComment = SimpleError("Unattached documentation comment"); | ||
| 323 | pub const ExtraAlignQualifier = SimpleError("Extra align qualifier"); | ||
| 324 | pub const ExtraConstQualifier = SimpleError("Extra const qualifier"); | ||
| 325 | pub const ExtraVolatileQualifier = SimpleError("Extra volatile qualifier"); | ||
| 326 | pub const ExtraAllowZeroQualifier = SimpleError("Extra allowzero qualifier"); | ||
| 327 | pub const DeclBetweenFields = SimpleError("Declarations are not allowed between container fields"); | ||
| 328 | pub const InvalidAnd = SimpleError("`&&` is invalid. Note that `and` is boolean AND."); | ||
| 329 | pub const AsteriskAfterPointerDereference = SimpleError("`.*` can't be followed by `*`. Are you missing a space?"); | ||
| 330 | |||
| 331 | pub const ExpectedCall = struct { | ||
| 332 | node: *Node, | ||
| 333 | |||
| 334 | pub fn render(self: *const ExpectedCall, tokens: []const Token.Id, stream: anytype) !void { | ||
| 335 | return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ ", found {s}", .{ | ||
| 336 | @tagName(self.node.tag), | ||
| 337 | }); | ||
| 338 | } | ||
| 339 | }; | ||
| 340 | 1137 | ||
| 341 | pub const ExpectedCallOrFnProto = struct { | 1138 | pub fn globalVarDecl(tree: Tree, node: Node.Index) full.VarDecl { |
| 342 | node: *Node, | 1139 | assert(tree.nodes.items(.tag)[node] == .global_var_decl); |
| 1140 | const data = tree.nodes.items(.data)[node]; | ||
| 1141 | const extra = tree.extraData(data.lhs, Node.GlobalVarDecl); | ||
| 1142 | return tree.fullVarDecl(.{ | ||
| 1143 | .type_node = extra.type_node, | ||
| 1144 | .align_node = extra.align_node, | ||
| 1145 | .section_node = extra.section_node, | ||
| 1146 | .init_node = data.rhs, | ||
| 1147 | .mut_token = tree.nodes.items(.main_token)[node], | ||
| 1148 | }); | ||
| 1149 | } | ||
| 343 | 1150 | ||
| 344 | pub fn render(self: *const ExpectedCallOrFnProto, tokens: []const Token.Id, stream: anytype) !void { | 1151 | pub fn localVarDecl(tree: Tree, node: Node.Index) full.VarDecl { |
| 345 | return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ " or " ++ | 1152 | assert(tree.nodes.items(.tag)[node] == .local_var_decl); |
| 346 | @tagName(Node.Tag.FnProto) ++ ", found {s}", .{@tagName(self.node.tag)}); | 1153 | const data = tree.nodes.items(.data)[node]; |
| 347 | } | 1154 | const extra = tree.extraData(data.lhs, Node.LocalVarDecl); |
| 348 | }; | 1155 | return tree.fullVarDecl(.{ |
| 1156 | .type_node = extra.type_node, | ||
| 1157 | .align_node = extra.align_node, | ||
| 1158 | .section_node = 0, | ||
| 1159 | .init_node = data.rhs, | ||
| 1160 | .mut_token = tree.nodes.items(.main_token)[node], | ||
| 1161 | }); | ||
| 1162 | } | ||
| 349 | 1163 | ||
| 350 | pub const ExpectedToken = struct { | 1164 | pub fn simpleVarDecl(tree: Tree, node: Node.Index) full.VarDecl { |
| 351 | token: TokenIndex, | 1165 | assert(tree.nodes.items(.tag)[node] == .simple_var_decl); |
| 352 | expected_id: Token.Id, | 1166 | const data = tree.nodes.items(.data)[node]; |
| 1167 | return tree.fullVarDecl(.{ | ||
| 1168 | .type_node = data.lhs, | ||
| 1169 | .align_node = 0, | ||
| 1170 | .section_node = 0, | ||
| 1171 | .init_node = data.rhs, | ||
| 1172 | .mut_token = tree.nodes.items(.main_token)[node], | ||
| 1173 | }); | ||
| 1174 | } | ||
| 353 | 1175 | ||
| 354 | pub fn render(self: *const ExpectedToken, tokens: []const Token.Id, stream: anytype) !void { | 1176 | pub fn alignedVarDecl(tree: Tree, node: Node.Index) full.VarDecl { |
| 355 | const found_token = tokens[self.token]; | 1177 | assert(tree.nodes.items(.tag)[node] == .aligned_var_decl); |
| 356 | switch (found_token) { | 1178 | const data = tree.nodes.items(.data)[node]; |
| 357 | .Invalid => { | 1179 | return tree.fullVarDecl(.{ |
| 358 | return stream.print("expected '{s}', found invalid bytes", .{self.expected_id.symbol()}); | 1180 | .type_node = 0, |
| 359 | }, | 1181 | .align_node = data.lhs, |
| 360 | else => { | 1182 | .section_node = 0, |
| 361 | const token_name = found_token.symbol(); | 1183 | .init_node = data.rhs, |
| 362 | return stream.print("expected '{s}', found '{s}'", .{ self.expected_id.symbol(), token_name }); | 1184 | .mut_token = tree.nodes.items(.main_token)[node], |
| 363 | }, | 1185 | }); |
| 364 | } | 1186 | } |
| 365 | } | ||
| 366 | }; | ||
| 367 | 1187 | ||
| 368 | pub const ExpectedCommaOrEnd = struct { | 1188 | pub fn ifSimple(tree: Tree, node: Node.Index) full.If { |
| 369 | token: TokenIndex, | 1189 | assert(tree.nodes.items(.tag)[node] == .if_simple); |
| 370 | end_id: Token.Id, | 1190 | const data = tree.nodes.items(.data)[node]; |
| 1191 | return tree.fullIf(.{ | ||
| 1192 | .cond_expr = data.lhs, | ||
| 1193 | .then_expr = data.rhs, | ||
| 1194 | .else_expr = 0, | ||
| 1195 | .if_token = tree.nodes.items(.main_token)[node], | ||
| 1196 | }); | ||
| 1197 | } | ||
| 371 | 1198 | ||
| 372 | pub fn render(self: *const ExpectedCommaOrEnd, tokens: []const Token.Id, stream: anytype) !void { | 1199 | pub fn ifFull(tree: Tree, node: Node.Index) full.If { |
| 373 | const actual_token = tokens[self.token]; | 1200 | assert(tree.nodes.items(.tag)[node] == .@"if"); |
| 374 | return stream.print("expected ',' or '{s}', found '{s}'", .{ | 1201 | const data = tree.nodes.items(.data)[node]; |
| 375 | self.end_id.symbol(), | 1202 | const extra = tree.extraData(data.rhs, Node.If); |
| 376 | actual_token.symbol(), | 1203 | return tree.fullIf(.{ |
| 377 | }); | 1204 | .cond_expr = data.lhs, |
| 378 | } | 1205 | .then_expr = extra.then_expr, |
| 379 | }; | 1206 | .else_expr = extra.else_expr, |
| 1207 | .if_token = tree.nodes.items(.main_token)[node], | ||
| 1208 | }); | ||
| 1209 | } | ||
| 380 | 1210 | ||
| 381 | fn SingleTokenError(comptime msg: []const u8) type { | 1211 | pub fn containerField(tree: Tree, node: Node.Index) full.ContainerField { |
| 382 | return struct { | 1212 | assert(tree.nodes.items(.tag)[node] == .container_field); |
| 383 | const ThisError = @This(); | 1213 | const data = tree.nodes.items(.data)[node]; |
| 1214 | const extra = tree.extraData(data.rhs, Node.ContainerField); | ||
| 1215 | return tree.fullContainerField(.{ | ||
| 1216 | .name_token = tree.nodes.items(.main_token)[node], | ||
| 1217 | .type_expr = data.lhs, | ||
| 1218 | .value_expr = extra.value_expr, | ||
| 1219 | .align_expr = extra.align_expr, | ||
| 1220 | }); | ||
| 1221 | } | ||
| 384 | 1222 | ||
| 385 | token: TokenIndex, | 1223 | pub fn containerFieldInit(tree: Tree, node: Node.Index) full.ContainerField { |
| 1224 | assert(tree.nodes.items(.tag)[node] == .container_field_init); | ||
| 1225 | const data = tree.nodes.items(.data)[node]; | ||
| 1226 | return tree.fullContainerField(.{ | ||
| 1227 | .name_token = tree.nodes.items(.main_token)[node], | ||
| 1228 | .type_expr = data.lhs, | ||
| 1229 | .value_expr = data.rhs, | ||
| 1230 | .align_expr = 0, | ||
| 1231 | }); | ||
| 1232 | } | ||
| 386 | 1233 | ||
| 387 | pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: anytype) !void { | 1234 | pub fn containerFieldAlign(tree: Tree, node: Node.Index) full.ContainerField { |
| 388 | const actual_token = tokens[self.token]; | 1235 | assert(tree.nodes.items(.tag)[node] == .container_field_align); |
| 389 | return stream.print(msg, .{actual_token.symbol()}); | 1236 | const data = tree.nodes.items(.data)[node]; |
| 390 | } | 1237 | return tree.fullContainerField(.{ |
| 391 | }; | 1238 | .name_token = tree.nodes.items(.main_token)[node], |
| 1239 | .type_expr = data.lhs, | ||
| 1240 | .value_expr = 0, | ||
| 1241 | .align_expr = data.rhs, | ||
| 1242 | }); | ||
| 392 | } | 1243 | } |
| 393 | 1244 | ||
| 394 | fn SimpleError(comptime msg: []const u8) type { | 1245 | pub fn fnProtoSimple(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.FnProto { |
| 395 | return struct { | 1246 | assert(tree.nodes.items(.tag)[node] == .fn_proto_simple); |
| 396 | const ThisError = @This(); | 1247 | const data = tree.nodes.items(.data)[node]; |
| 1248 | buffer[0] = data.lhs; | ||
| 1249 | const params = if (data.lhs == 0) buffer[0..0] else buffer[0..1]; | ||
| 1250 | return tree.fullFnProto(.{ | ||
| 1251 | .fn_token = tree.nodes.items(.main_token)[node], | ||
| 1252 | .return_type = data.rhs, | ||
| 1253 | .params = params, | ||
| 1254 | .align_expr = 0, | ||
| 1255 | .section_expr = 0, | ||
| 1256 | .callconv_expr = 0, | ||
| 1257 | }); | ||
| 1258 | } | ||
| 397 | 1259 | ||
| 398 | token: TokenIndex, | 1260 | pub fn fnProtoMulti(tree: Tree, node: Node.Index) full.FnProto { |
| 1261 | assert(tree.nodes.items(.tag)[node] == .fn_proto_multi); | ||
| 1262 | const data = tree.nodes.items(.data)[node]; | ||
| 1263 | const params_range = tree.extraData(data.lhs, Node.SubRange); | ||
| 1264 | const params = tree.extra_data[params_range.start..params_range.end]; | ||
| 1265 | return tree.fullFnProto(.{ | ||
| 1266 | .fn_token = tree.nodes.items(.main_token)[node], | ||
| 1267 | .return_type = data.rhs, | ||
| 1268 | .params = params, | ||
| 1269 | .align_expr = 0, | ||
| 1270 | .section_expr = 0, | ||
| 1271 | .callconv_expr = 0, | ||
| 1272 | }); | ||
| 1273 | } | ||
| 399 | 1274 | ||
| 400 | pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: anytype) !void { | 1275 | pub fn fnProtoOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.FnProto { |
| 401 | return stream.writeAll(msg); | 1276 | assert(tree.nodes.items(.tag)[node] == .fn_proto_one); |
| 402 | } | 1277 | const data = tree.nodes.items(.data)[node]; |
| 403 | }; | 1278 | const extra = tree.extraData(data.lhs, Node.FnProtoOne); |
| 1279 | buffer[0] = extra.param; | ||
| 1280 | const params = if (extra.param == 0) buffer[0..0] else buffer[0..1]; | ||
| 1281 | return tree.fullFnProto(.{ | ||
| 1282 | .fn_token = tree.nodes.items(.main_token)[node], | ||
| 1283 | .return_type = data.rhs, | ||
| 1284 | .params = params, | ||
| 1285 | .align_expr = extra.align_expr, | ||
| 1286 | .section_expr = extra.section_expr, | ||
| 1287 | .callconv_expr = extra.callconv_expr, | ||
| 1288 | }); | ||
| 404 | } | 1289 | } |
| 405 | }; | ||
| 406 | 1290 | ||
| 407 | pub const Node = struct { | 1291 | pub fn fnProto(tree: Tree, node: Node.Index) full.FnProto { |
| 408 | tag: Tag, | 1292 | assert(tree.nodes.items(.tag)[node] == .fn_proto); |
| 1293 | const data = tree.nodes.items(.data)[node]; | ||
| 1294 | const extra = tree.extraData(data.lhs, Node.FnProto); | ||
| 1295 | const params = tree.extra_data[extra.params_start..extra.params_end]; | ||
| 1296 | return tree.fullFnProto(.{ | ||
| 1297 | .fn_token = tree.nodes.items(.main_token)[node], | ||
| 1298 | .return_type = data.rhs, | ||
| 1299 | .params = params, | ||
| 1300 | .align_expr = extra.align_expr, | ||
| 1301 | .section_expr = extra.section_expr, | ||
| 1302 | .callconv_expr = extra.callconv_expr, | ||
| 1303 | }); | ||
| 1304 | } | ||
| 409 | 1305 | ||
| 410 | pub const Tag = enum { | 1306 | pub fn structInitOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.StructInit { |
| 411 | // Top level | 1307 | assert(tree.nodes.items(.tag)[node] == .struct_init_one or |
| 412 | Root, | 1308 | tree.nodes.items(.tag)[node] == .struct_init_one_comma); |
| 413 | Use, | 1309 | const data = tree.nodes.items(.data)[node]; |
| 414 | TestDecl, | 1310 | buffer[0] = data.rhs; |
| 415 | 1311 | const fields = if (data.rhs == 0) buffer[0..0] else buffer[0..1]; | |
| 416 | // Statements | 1312 | return tree.fullStructInit(.{ |
| 417 | VarDecl, | 1313 | .lbrace = tree.nodes.items(.main_token)[node], |
| 418 | Defer, | 1314 | .fields = fields, |
| 419 | 1315 | .type_expr = data.lhs, | |
| 420 | // Infix operators | 1316 | }); |
| 421 | Catch, | 1317 | } |
| 422 | |||
| 423 | // SimpleInfixOp | ||
| 424 | Add, | ||
| 425 | AddWrap, | ||
| 426 | ArrayCat, | ||
| 427 | ArrayMult, | ||
| 428 | Assign, | ||
| 429 | AssignBitAnd, | ||
| 430 | AssignBitOr, | ||
| 431 | AssignBitShiftLeft, | ||
| 432 | AssignBitShiftRight, | ||
| 433 | AssignBitXor, | ||
| 434 | AssignDiv, | ||
| 435 | AssignSub, | ||
| 436 | AssignSubWrap, | ||
| 437 | AssignMod, | ||
| 438 | AssignAdd, | ||
| 439 | AssignAddWrap, | ||
| 440 | AssignMul, | ||
| 441 | AssignMulWrap, | ||
| 442 | BangEqual, | ||
| 443 | BitAnd, | ||
| 444 | BitOr, | ||
| 445 | BitShiftLeft, | ||
| 446 | BitShiftRight, | ||
| 447 | BitXor, | ||
| 448 | BoolAnd, | ||
| 449 | BoolOr, | ||
| 450 | Div, | ||
| 451 | EqualEqual, | ||
| 452 | ErrorUnion, | ||
| 453 | GreaterOrEqual, | ||
| 454 | GreaterThan, | ||
| 455 | LessOrEqual, | ||
| 456 | LessThan, | ||
| 457 | MergeErrorSets, | ||
| 458 | Mod, | ||
| 459 | Mul, | ||
| 460 | MulWrap, | ||
| 461 | Period, | ||
| 462 | Range, | ||
| 463 | Sub, | ||
| 464 | SubWrap, | ||
| 465 | OrElse, | ||
| 466 | |||
| 467 | // SimplePrefixOp | ||
| 468 | AddressOf, | ||
| 469 | Await, | ||
| 470 | BitNot, | ||
| 471 | BoolNot, | ||
| 472 | OptionalType, | ||
| 473 | Negation, | ||
| 474 | NegationWrap, | ||
| 475 | Resume, | ||
| 476 | Try, | ||
| 477 | |||
| 478 | ArrayType, | ||
| 479 | /// ArrayType but has a sentinel node. | ||
| 480 | ArrayTypeSentinel, | ||
| 481 | PtrType, | ||
| 482 | SliceType, | ||
| 483 | /// `a[b..c]` | ||
| 484 | Slice, | ||
| 485 | /// `a.*` | ||
| 486 | Deref, | ||
| 487 | /// `a.?` | ||
| 488 | UnwrapOptional, | ||
| 489 | /// `a[b]` | ||
| 490 | ArrayAccess, | ||
| 491 | /// `T{a, b}` | ||
| 492 | ArrayInitializer, | ||
| 493 | /// ArrayInitializer but with `.` instead of a left-hand-side operand. | ||
| 494 | ArrayInitializerDot, | ||
| 495 | /// `T{.a = b}` | ||
| 496 | StructInitializer, | ||
| 497 | /// StructInitializer but with `.` instead of a left-hand-side operand. | ||
| 498 | StructInitializerDot, | ||
| 499 | /// `foo()` | ||
| 500 | Call, | ||
| 501 | |||
| 502 | // Control flow | ||
| 503 | Switch, | ||
| 504 | While, | ||
| 505 | For, | ||
| 506 | If, | ||
| 507 | Suspend, | ||
| 508 | Continue, | ||
| 509 | Break, | ||
| 510 | Return, | ||
| 511 | |||
| 512 | // Type expressions | ||
| 513 | AnyType, | ||
| 514 | ErrorType, | ||
| 515 | FnProto, | ||
| 516 | AnyFrameType, | ||
| 517 | |||
| 518 | // Primary expressions | ||
| 519 | IntegerLiteral, | ||
| 520 | FloatLiteral, | ||
| 521 | EnumLiteral, | ||
| 522 | StringLiteral, | ||
| 523 | MultilineStringLiteral, | ||
| 524 | CharLiteral, | ||
| 525 | BoolLiteral, | ||
| 526 | NullLiteral, | ||
| 527 | UndefinedLiteral, | ||
| 528 | Unreachable, | ||
| 529 | Identifier, | ||
| 530 | GroupedExpression, | ||
| 531 | BuiltinCall, | ||
| 532 | ErrorSetDecl, | ||
| 533 | ContainerDecl, | ||
| 534 | Asm, | ||
| 535 | Comptime, | ||
| 536 | Nosuspend, | ||
| 537 | Block, | ||
| 538 | LabeledBlock, | ||
| 539 | |||
| 540 | // Misc | ||
| 541 | DocComment, | ||
| 542 | SwitchCase, // TODO make this not a child of AST Node | ||
| 543 | SwitchElse, // TODO make this not a child of AST Node | ||
| 544 | Else, // TODO make this not a child of AST Node | ||
| 545 | Payload, // TODO make this not a child of AST Node | ||
| 546 | PointerPayload, // TODO make this not a child of AST Node | ||
| 547 | PointerIndexPayload, // TODO make this not a child of AST Node | ||
| 548 | ContainerField, | ||
| 549 | ErrorTag, // TODO make this not a child of AST Node | ||
| 550 | FieldInitializer, // TODO make this not a child of AST Node | ||
| 551 | |||
| 552 | pub fn Type(tag: Tag) type { | ||
| 553 | return switch (tag) { | ||
| 554 | .Root => Root, | ||
| 555 | .Use => Use, | ||
| 556 | .TestDecl => TestDecl, | ||
| 557 | .VarDecl => VarDecl, | ||
| 558 | .Defer => Defer, | ||
| 559 | .Catch => Catch, | ||
| 560 | |||
| 561 | .Add, | ||
| 562 | .AddWrap, | ||
| 563 | .ArrayCat, | ||
| 564 | .ArrayMult, | ||
| 565 | .Assign, | ||
| 566 | .AssignBitAnd, | ||
| 567 | .AssignBitOr, | ||
| 568 | .AssignBitShiftLeft, | ||
| 569 | .AssignBitShiftRight, | ||
| 570 | .AssignBitXor, | ||
| 571 | .AssignDiv, | ||
| 572 | .AssignSub, | ||
| 573 | .AssignSubWrap, | ||
| 574 | .AssignMod, | ||
| 575 | .AssignAdd, | ||
| 576 | .AssignAddWrap, | ||
| 577 | .AssignMul, | ||
| 578 | .AssignMulWrap, | ||
| 579 | .BangEqual, | ||
| 580 | .BitAnd, | ||
| 581 | .BitOr, | ||
| 582 | .BitShiftLeft, | ||
| 583 | .BitShiftRight, | ||
| 584 | .BitXor, | ||
| 585 | .BoolAnd, | ||
| 586 | .BoolOr, | ||
| 587 | .Div, | ||
| 588 | .EqualEqual, | ||
| 589 | .ErrorUnion, | ||
| 590 | .GreaterOrEqual, | ||
| 591 | .GreaterThan, | ||
| 592 | .LessOrEqual, | ||
| 593 | .LessThan, | ||
| 594 | .MergeErrorSets, | ||
| 595 | .Mod, | ||
| 596 | .Mul, | ||
| 597 | .MulWrap, | ||
| 598 | .Period, | ||
| 599 | .Range, | ||
| 600 | .Sub, | ||
| 601 | .SubWrap, | ||
| 602 | .OrElse, | ||
| 603 | => SimpleInfixOp, | ||
| 604 | |||
| 605 | .AddressOf, | ||
| 606 | .Await, | ||
| 607 | .BitNot, | ||
| 608 | .BoolNot, | ||
| 609 | .OptionalType, | ||
| 610 | .Negation, | ||
| 611 | .NegationWrap, | ||
| 612 | .Resume, | ||
| 613 | .Try, | ||
| 614 | => SimplePrefixOp, | ||
| 615 | |||
| 616 | .Identifier, | ||
| 617 | .BoolLiteral, | ||
| 618 | .NullLiteral, | ||
| 619 | .UndefinedLiteral, | ||
| 620 | .Unreachable, | ||
| 621 | .AnyType, | ||
| 622 | .ErrorType, | ||
| 623 | .IntegerLiteral, | ||
| 624 | .FloatLiteral, | ||
| 625 | .StringLiteral, | ||
| 626 | .CharLiteral, | ||
| 627 | => OneToken, | ||
| 628 | |||
| 629 | .Continue, | ||
| 630 | .Break, | ||
| 631 | .Return, | ||
| 632 | => ControlFlowExpression, | ||
| 633 | |||
| 634 | .ArrayType => ArrayType, | ||
| 635 | .ArrayTypeSentinel => ArrayTypeSentinel, | ||
| 636 | |||
| 637 | .PtrType => PtrType, | ||
| 638 | .SliceType => SliceType, | ||
| 639 | .Slice => Slice, | ||
| 640 | .Deref, .UnwrapOptional => SimpleSuffixOp, | ||
| 641 | .ArrayAccess => ArrayAccess, | ||
| 642 | |||
| 643 | .ArrayInitializer => ArrayInitializer, | ||
| 644 | .ArrayInitializerDot => ArrayInitializerDot, | ||
| 645 | |||
| 646 | .StructInitializer => StructInitializer, | ||
| 647 | .StructInitializerDot => StructInitializerDot, | ||
| 648 | |||
| 649 | .Call => Call, | ||
| 650 | .Switch => Switch, | ||
| 651 | .While => While, | ||
| 652 | .For => For, | ||
| 653 | .If => If, | ||
| 654 | .Suspend => Suspend, | ||
| 655 | .FnProto => FnProto, | ||
| 656 | .AnyFrameType => AnyFrameType, | ||
| 657 | .EnumLiteral => EnumLiteral, | ||
| 658 | .MultilineStringLiteral => MultilineStringLiteral, | ||
| 659 | .GroupedExpression => GroupedExpression, | ||
| 660 | .BuiltinCall => BuiltinCall, | ||
| 661 | .ErrorSetDecl => ErrorSetDecl, | ||
| 662 | .ContainerDecl => ContainerDecl, | ||
| 663 | .Asm => Asm, | ||
| 664 | .Comptime => Comptime, | ||
| 665 | .Nosuspend => Nosuspend, | ||
| 666 | .Block => Block, | ||
| 667 | .LabeledBlock => LabeledBlock, | ||
| 668 | .DocComment => DocComment, | ||
| 669 | .SwitchCase => SwitchCase, | ||
| 670 | .SwitchElse => SwitchElse, | ||
| 671 | .Else => Else, | ||
| 672 | .Payload => Payload, | ||
| 673 | .PointerPayload => PointerPayload, | ||
| 674 | .PointerIndexPayload => PointerIndexPayload, | ||
| 675 | .ContainerField => ContainerField, | ||
| 676 | .ErrorTag => ErrorTag, | ||
| 677 | .FieldInitializer => FieldInitializer, | ||
| 678 | }; | ||
| 679 | } | ||
| 680 | 1318 | ||
| 681 | pub fn isBlock(tag: Tag) bool { | 1319 | pub fn structInitDotTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.StructInit { |
| 682 | return switch (tag) { | 1320 | assert(tree.nodes.items(.tag)[node] == .struct_init_dot_two or |
| 683 | .Block, .LabeledBlock => true, | 1321 | tree.nodes.items(.tag)[node] == .struct_init_dot_two_comma); |
| 684 | else => false, | 1322 | const data = tree.nodes.items(.data)[node]; |
| 685 | }; | 1323 | buffer.* = .{ data.lhs, data.rhs }; |
| 686 | } | 1324 | const fields = if (data.rhs != 0) |
| 687 | }; | 1325 | buffer[0..2] |
| 1326 | else if (data.lhs != 0) | ||
| 1327 | buffer[0..1] | ||
| 1328 | else | ||
| 1329 | buffer[0..0]; | ||
| 1330 | return tree.fullStructInit(.{ | ||
| 1331 | .lbrace = tree.nodes.items(.main_token)[node], | ||
| 1332 | .fields = fields, | ||
| 1333 | .type_expr = 0, | ||
| 1334 | }); | ||
| 1335 | } | ||
| 688 | 1336 | ||
| 689 | /// Prefer `castTag` to this. | 1337 | pub fn structInitDot(tree: Tree, node: Node.Index) full.StructInit { |
| 690 | pub fn cast(base: *Node, comptime T: type) ?*T { | 1338 | assert(tree.nodes.items(.tag)[node] == .struct_init_dot or |
| 691 | if (std.meta.fieldInfo(T, .base).default_value) |default_base| { | 1339 | tree.nodes.items(.tag)[node] == .struct_init_dot_comma); |
| 692 | return base.castTag(default_base.tag); | 1340 | const data = tree.nodes.items(.data)[node]; |
| 693 | } | 1341 | return tree.fullStructInit(.{ |
| 694 | inline for (@typeInfo(Tag).Enum.fields) |field| { | 1342 | .lbrace = tree.nodes.items(.main_token)[node], |
| 695 | const tag = @intToEnum(Tag, field.value); | 1343 | .fields = tree.extra_data[data.lhs..data.rhs], |
| 696 | if (base.tag == tag) { | 1344 | .type_expr = 0, |
| 697 | if (T == tag.Type()) { | 1345 | }); |
| 698 | return @fieldParentPtr(T, "base", base); | ||
| 699 | } | ||
| 700 | return null; | ||
| 701 | } | ||
| 702 | } | ||
| 703 | unreachable; | ||
| 704 | } | 1346 | } |
| 705 | 1347 | ||
| 706 | pub fn castTag(base: *Node, comptime tag: Tag) ?*tag.Type() { | 1348 | pub fn structInit(tree: Tree, node: Node.Index) full.StructInit { |
| 707 | if (base.tag == tag) { | 1349 | assert(tree.nodes.items(.tag)[node] == .struct_init or |
| 708 | return @fieldParentPtr(tag.Type(), "base", base); | 1350 | tree.nodes.items(.tag)[node] == .struct_init_comma); |
| 709 | } | 1351 | const data = tree.nodes.items(.data)[node]; |
| 710 | return null; | 1352 | const fields_range = tree.extraData(data.rhs, Node.SubRange); |
| 1353 | return tree.fullStructInit(.{ | ||
| 1354 | .lbrace = tree.nodes.items(.main_token)[node], | ||
| 1355 | .fields = tree.extra_data[fields_range.start..fields_range.end], | ||
| 1356 | .type_expr = data.lhs, | ||
| 1357 | }); | ||
| 711 | } | 1358 | } |
| 712 | 1359 | ||
| 713 | pub fn iterate(base: *Node, index: usize) ?*Node { | 1360 | pub fn arrayInitOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.ArrayInit { |
| 714 | inline for (@typeInfo(Tag).Enum.fields) |field| { | 1361 | assert(tree.nodes.items(.tag)[node] == .array_init_one or |
| 715 | const tag = @intToEnum(Tag, field.value); | 1362 | tree.nodes.items(.tag)[node] == .array_init_one_comma); |
| 716 | if (base.tag == tag) { | 1363 | const data = tree.nodes.items(.data)[node]; |
| 717 | return @fieldParentPtr(tag.Type(), "base", base).iterate(index); | 1364 | buffer[0] = data.rhs; |
| 718 | } | 1365 | const elements = if (data.rhs == 0) buffer[0..0] else buffer[0..1]; |
| 719 | } | 1366 | return .{ |
| 720 | unreachable; | 1367 | .ast = .{ |
| 1368 | .lbrace = tree.nodes.items(.main_token)[node], | ||
| 1369 | .elements = elements, | ||
| 1370 | .type_expr = data.lhs, | ||
| 1371 | }, | ||
| 1372 | }; | ||
| 721 | } | 1373 | } |
| 722 | 1374 | ||
| 723 | pub fn firstToken(base: *const Node) TokenIndex { | 1375 | pub fn arrayInitDotTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.ArrayInit { |
| 724 | inline for (@typeInfo(Tag).Enum.fields) |field| { | 1376 | assert(tree.nodes.items(.tag)[node] == .array_init_dot_two or |
| 725 | const tag = @intToEnum(Tag, field.value); | 1377 | tree.nodes.items(.tag)[node] == .array_init_dot_two_comma); |
| 726 | if (base.tag == tag) { | 1378 | const data = tree.nodes.items(.data)[node]; |
| 727 | return @fieldParentPtr(tag.Type(), "base", base).firstToken(); | 1379 | buffer.* = .{ data.lhs, data.rhs }; |
| 728 | } | 1380 | const elements = if (data.rhs != 0) |
| 729 | } | 1381 | buffer[0..2] |
| 730 | unreachable; | 1382 | else if (data.lhs != 0) |
| 1383 | buffer[0..1] | ||
| 1384 | else | ||
| 1385 | buffer[0..0]; | ||
| 1386 | return .{ | ||
| 1387 | .ast = .{ | ||
| 1388 | .lbrace = tree.nodes.items(.main_token)[node], | ||
| 1389 | .elements = elements, | ||
| 1390 | .type_expr = 0, | ||
| 1391 | }, | ||
| 1392 | }; | ||
| 731 | } | 1393 | } |
| 732 | 1394 | ||
| 733 | pub fn lastToken(base: *const Node) TokenIndex { | 1395 | pub fn arrayInitDot(tree: Tree, node: Node.Index) full.ArrayInit { |
| 734 | inline for (@typeInfo(Tag).Enum.fields) |field| { | 1396 | assert(tree.nodes.items(.tag)[node] == .array_init_dot or |
| 735 | const tag = @intToEnum(Tag, field.value); | 1397 | tree.nodes.items(.tag)[node] == .array_init_dot_comma); |
| 736 | if (base.tag == tag) { | 1398 | const data = tree.nodes.items(.data)[node]; |
| 737 | return @fieldParentPtr(tag.Type(), "base", base).lastToken(); | 1399 | return .{ |
| 738 | } | 1400 | .ast = .{ |
| 739 | } | 1401 | .lbrace = tree.nodes.items(.main_token)[node], |
| 740 | unreachable; | 1402 | .elements = tree.extra_data[data.lhs..data.rhs], |
| 741 | } | 1403 | .type_expr = 0, |
| 742 | 1404 | }, | |
| 743 | pub fn requireSemiColon(base: *const Node) bool { | 1405 | }; |
| 744 | var n = base; | 1406 | } |
| 745 | while (true) { | ||
| 746 | switch (n.tag) { | ||
| 747 | .Root, | ||
| 748 | .ContainerField, | ||
| 749 | .Block, | ||
| 750 | .LabeledBlock, | ||
| 751 | .Payload, | ||
| 752 | .PointerPayload, | ||
| 753 | .PointerIndexPayload, | ||
| 754 | .Switch, | ||
| 755 | .SwitchCase, | ||
| 756 | .SwitchElse, | ||
| 757 | .FieldInitializer, | ||
| 758 | .DocComment, | ||
| 759 | .TestDecl, | ||
| 760 | => return false, | ||
| 761 | |||
| 762 | .While => { | ||
| 763 | const while_node = @fieldParentPtr(While, "base", n); | ||
| 764 | if (while_node.@"else") |@"else"| { | ||
| 765 | n = &@"else".base; | ||
| 766 | continue; | ||
| 767 | } | ||
| 768 | 1407 | ||
| 769 | return !while_node.body.tag.isBlock(); | 1408 | pub fn arrayInit(tree: Tree, node: Node.Index) full.ArrayInit { |
| 770 | }, | 1409 | assert(tree.nodes.items(.tag)[node] == .array_init or |
| 771 | .For => { | 1410 | tree.nodes.items(.tag)[node] == .array_init_comma); |
| 772 | const for_node = @fieldParentPtr(For, "base", n); | 1411 | const data = tree.nodes.items(.data)[node]; |
| 773 | if (for_node.@"else") |@"else"| { | 1412 | const elem_range = tree.extraData(data.rhs, Node.SubRange); |
| 774 | n = &@"else".base; | 1413 | return .{ |
| 775 | continue; | 1414 | .ast = .{ |
| 776 | } | 1415 | .lbrace = tree.nodes.items(.main_token)[node], |
| 1416 | .elements = tree.extra_data[elem_range.start..elem_range.end], | ||
| 1417 | .type_expr = data.lhs, | ||
| 1418 | }, | ||
| 1419 | }; | ||
| 1420 | } | ||
| 777 | 1421 | ||
| 778 | return !for_node.body.tag.isBlock(); | 1422 | pub fn arrayType(tree: Tree, node: Node.Index) full.ArrayType { |
| 779 | }, | 1423 | assert(tree.nodes.items(.tag)[node] == .array_type); |
| 780 | .If => { | 1424 | const data = tree.nodes.items(.data)[node]; |
| 781 | const if_node = @fieldParentPtr(If, "base", n); | 1425 | return .{ |
| 782 | if (if_node.@"else") |@"else"| { | 1426 | .ast = .{ |
| 783 | n = &@"else".base; | 1427 | .lbracket = tree.nodes.items(.main_token)[node], |
| 784 | continue; | 1428 | .elem_count = data.lhs, |
| 785 | } | 1429 | .sentinel = null, |
| 1430 | .elem_type = data.rhs, | ||
| 1431 | }, | ||
| 1432 | }; | ||
| 1433 | } | ||
| 786 | 1434 | ||
| 787 | return !if_node.body.tag.isBlock(); | 1435 | pub fn arrayTypeSentinel(tree: Tree, node: Node.Index) full.ArrayType { |
| 788 | }, | 1436 | assert(tree.nodes.items(.tag)[node] == .array_type_sentinel); |
| 789 | .Else => { | 1437 | const data = tree.nodes.items(.data)[node]; |
| 790 | const else_node = @fieldParentPtr(Else, "base", n); | 1438 | const extra = tree.extraData(data.rhs, Node.ArrayTypeSentinel); |
| 791 | n = else_node.body; | 1439 | return .{ |
| 792 | continue; | 1440 | .ast = .{ |
| 793 | }, | 1441 | .lbracket = tree.nodes.items(.main_token)[node], |
| 794 | .Defer => { | 1442 | .elem_count = data.lhs, |
| 795 | const defer_node = @fieldParentPtr(Defer, "base", n); | 1443 | .sentinel = extra.sentinel, |
| 796 | return !defer_node.expr.tag.isBlock(); | 1444 | .elem_type = extra.elem_type, |
| 797 | }, | 1445 | }, |
| 798 | .Comptime => { | 1446 | }; |
| 799 | const comptime_node = @fieldParentPtr(Comptime, "base", n); | 1447 | } |
| 800 | return !comptime_node.expr.tag.isBlock(); | ||
| 801 | }, | ||
| 802 | .Suspend => { | ||
| 803 | const suspend_node = @fieldParentPtr(Suspend, "base", n); | ||
| 804 | if (suspend_node.body) |body| { | ||
| 805 | return !body.tag.isBlock(); | ||
| 806 | } | ||
| 807 | 1448 | ||
| 808 | return true; | 1449 | pub fn ptrTypeAligned(tree: Tree, node: Node.Index) full.PtrType { |
| 809 | }, | 1450 | assert(tree.nodes.items(.tag)[node] == .ptr_type_aligned); |
| 810 | .Nosuspend => { | 1451 | const data = tree.nodes.items(.data)[node]; |
| 811 | const nosuspend_node = @fieldParentPtr(Nosuspend, "base", n); | 1452 | return tree.fullPtrType(.{ |
| 812 | return !nosuspend_node.expr.tag.isBlock(); | 1453 | .main_token = tree.nodes.items(.main_token)[node], |
| 813 | }, | 1454 | .align_node = data.lhs, |
| 814 | else => return true, | 1455 | .sentinel = 0, |
| 815 | } | 1456 | .bit_range_start = 0, |
| 816 | } | 1457 | .bit_range_end = 0, |
| 1458 | .child_type = data.rhs, | ||
| 1459 | }); | ||
| 817 | } | 1460 | } |
| 818 | 1461 | ||
| 819 | /// Asserts the node is a Block or LabeledBlock and returns the statements slice. | 1462 | pub fn ptrTypeSentinel(tree: Tree, node: Node.Index) full.PtrType { |
| 820 | pub fn blockStatements(base: *Node) []*Node { | 1463 | assert(tree.nodes.items(.tag)[node] == .ptr_type_sentinel); |
| 821 | if (base.castTag(.Block)) |block| { | 1464 | const data = tree.nodes.items(.data)[node]; |
| 822 | return block.statements(); | 1465 | return tree.fullPtrType(.{ |
| 823 | } else if (base.castTag(.LabeledBlock)) |labeled_block| { | 1466 | .main_token = tree.nodes.items(.main_token)[node], |
| 824 | return labeled_block.statements(); | 1467 | .align_node = 0, |
| 825 | } else { | 1468 | .sentinel = data.lhs, |
| 826 | unreachable; | 1469 | .bit_range_start = 0, |
| 827 | } | 1470 | .bit_range_end = 0, |
| 1471 | .child_type = data.rhs, | ||
| 1472 | }); | ||
| 828 | } | 1473 | } |
| 829 | 1474 | ||
| 830 | pub fn findFirstWithId(self: *Node, id: Id) ?*Node { | 1475 | pub fn ptrType(tree: Tree, node: Node.Index) full.PtrType { |
| 831 | if (self.id == id) return self; | 1476 | assert(tree.nodes.items(.tag)[node] == .ptr_type); |
| 832 | var child_i: usize = 0; | 1477 | const data = tree.nodes.items(.data)[node]; |
| 833 | while (self.iterate(child_i)) |child| : (child_i += 1) { | 1478 | const extra = tree.extraData(data.lhs, Node.PtrType); |
| 834 | if (child.findFirstWithId(id)) |result| return result; | 1479 | return tree.fullPtrType(.{ |
| 835 | } | 1480 | .main_token = tree.nodes.items(.main_token)[node], |
| 836 | return null; | 1481 | .align_node = extra.align_node, |
| 1482 | .sentinel = extra.sentinel, | ||
| 1483 | .bit_range_start = 0, | ||
| 1484 | .bit_range_end = 0, | ||
| 1485 | .child_type = data.rhs, | ||
| 1486 | }); | ||
| 837 | } | 1487 | } |
| 838 | 1488 | ||
| 839 | pub fn dump(self: *Node, indent: usize) void { | 1489 | pub fn ptrTypeBitRange(tree: Tree, node: Node.Index) full.PtrType { |
| 840 | { | 1490 | assert(tree.nodes.items(.tag)[node] == .ptr_type_bit_range); |
| 841 | var i: usize = 0; | 1491 | const data = tree.nodes.items(.data)[node]; |
| 842 | while (i < indent) : (i += 1) { | 1492 | const extra = tree.extraData(data.lhs, Node.PtrTypeBitRange); |
| 843 | std.debug.warn(" ", .{}); | 1493 | return tree.fullPtrType(.{ |
| 844 | } | 1494 | .main_token = tree.nodes.items(.main_token)[node], |
| 845 | } | 1495 | .align_node = extra.align_node, |
| 846 | std.debug.warn("{s}\n", .{@tagName(self.tag)}); | 1496 | .sentinel = extra.sentinel, |
| 1497 | .bit_range_start = extra.bit_range_start, | ||
| 1498 | .bit_range_end = extra.bit_range_end, | ||
| 1499 | .child_type = data.rhs, | ||
| 1500 | }); | ||
| 1501 | } | ||
| 847 | 1502 | ||
| 848 | var child_i: usize = 0; | 1503 | pub fn sliceOpen(tree: Tree, node: Node.Index) full.Slice { |
| 849 | while (self.iterate(child_i)) |child| : (child_i += 1) { | 1504 | assert(tree.nodes.items(.tag)[node] == .slice_open); |
| 850 | child.dump(indent + 2); | 1505 | const data = tree.nodes.items(.data)[node]; |
| 851 | } | 1506 | return .{ |
| 1507 | .ast = .{ | ||
| 1508 | .sliced = data.lhs, | ||
| 1509 | .lbracket = tree.nodes.items(.main_token)[node], | ||
| 1510 | .start = data.rhs, | ||
| 1511 | .end = 0, | ||
| 1512 | .sentinel = 0, | ||
| 1513 | }, | ||
| 1514 | }; | ||
| 852 | } | 1515 | } |
| 853 | 1516 | ||
| 854 | /// The decls data follows this struct in memory as an array of Node pointers. | 1517 | pub fn slice(tree: Tree, node: Node.Index) full.Slice { |
| 855 | pub const Root = struct { | 1518 | assert(tree.nodes.items(.tag)[node] == .slice); |
| 856 | base: Node = Node{ .tag = .Root }, | 1519 | const data = tree.nodes.items(.data)[node]; |
| 857 | eof_token: TokenIndex, | 1520 | const extra = tree.extraData(data.rhs, Node.Slice); |
| 858 | decls_len: NodeIndex, | 1521 | return .{ |
| 1522 | .ast = .{ | ||
| 1523 | .sliced = data.lhs, | ||
| 1524 | .lbracket = tree.nodes.items(.main_token)[node], | ||
| 1525 | .start = extra.start, | ||
| 1526 | .end = extra.end, | ||
| 1527 | .sentinel = 0, | ||
| 1528 | }, | ||
| 1529 | }; | ||
| 1530 | } | ||
| 859 | 1531 | ||
| 860 | /// After this the caller must initialize the decls list. | 1532 | pub fn sliceSentinel(tree: Tree, node: Node.Index) full.Slice { |
| 861 | pub fn create(allocator: *mem.Allocator, decls_len: NodeIndex, eof_token: TokenIndex) !*Root { | 1533 | assert(tree.nodes.items(.tag)[node] == .slice_sentinel); |
| 862 | const bytes = try allocator.alignedAlloc(u8, @alignOf(Root), sizeInBytes(decls_len)); | 1534 | const data = tree.nodes.items(.data)[node]; |
| 863 | const self = @ptrCast(*Root, bytes.ptr); | 1535 | const extra = tree.extraData(data.rhs, Node.SliceSentinel); |
| 864 | self.* = .{ | 1536 | return .{ |
| 865 | .eof_token = eof_token, | 1537 | .ast = .{ |
| 866 | .decls_len = decls_len, | 1538 | .sliced = data.lhs, |
| 867 | }; | 1539 | .lbracket = tree.nodes.items(.main_token)[node], |
| 868 | return self; | 1540 | .start = extra.start, |
| 869 | } | 1541 | .end = extra.end, |
| 1542 | .sentinel = extra.sentinel, | ||
| 1543 | }, | ||
| 1544 | }; | ||
| 1545 | } | ||
| 870 | 1546 | ||
| 871 | pub fn destroy(self: *Decl, allocator: *mem.Allocator) void { | 1547 | pub fn containerDeclTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.ContainerDecl { |
| 872 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.decls_len)]; | 1548 | assert(tree.nodes.items(.tag)[node] == .container_decl_two or |
| 873 | allocator.free(bytes); | 1549 | tree.nodes.items(.tag)[node] == .container_decl_two_trailing); |
| 874 | } | 1550 | const data = tree.nodes.items(.data)[node]; |
| 1551 | buffer.* = .{ data.lhs, data.rhs }; | ||
| 1552 | const members = if (data.rhs != 0) | ||
| 1553 | buffer[0..2] | ||
| 1554 | else if (data.lhs != 0) | ||
| 1555 | buffer[0..1] | ||
| 1556 | else | ||
| 1557 | buffer[0..0]; | ||
| 1558 | return tree.fullContainerDecl(.{ | ||
| 1559 | .main_token = tree.nodes.items(.main_token)[node], | ||
| 1560 | .enum_token = null, | ||
| 1561 | .members = members, | ||
| 1562 | .arg = 0, | ||
| 1563 | }); | ||
| 1564 | } | ||
| 875 | 1565 | ||
| 876 | pub fn iterate(self: *const Root, index: usize) ?*Node { | 1566 | pub fn containerDecl(tree: Tree, node: Node.Index) full.ContainerDecl { |
| 877 | var i = index; | 1567 | assert(tree.nodes.items(.tag)[node] == .container_decl or |
| 1568 | tree.nodes.items(.tag)[node] == .container_decl_trailing); | ||
| 1569 | const data = tree.nodes.items(.data)[node]; | ||
| 1570 | return tree.fullContainerDecl(.{ | ||
| 1571 | .main_token = tree.nodes.items(.main_token)[node], | ||
| 1572 | .enum_token = null, | ||
| 1573 | .members = tree.extra_data[data.lhs..data.rhs], | ||
| 1574 | .arg = 0, | ||
| 1575 | }); | ||
| 1576 | } | ||
| 878 | 1577 | ||
| 879 | if (i < self.decls_len) return self.declsConst()[i]; | 1578 | pub fn containerDeclArg(tree: Tree, node: Node.Index) full.ContainerDecl { |
| 880 | return null; | 1579 | assert(tree.nodes.items(.tag)[node] == .container_decl_arg or |
| 881 | } | 1580 | tree.nodes.items(.tag)[node] == .container_decl_arg_trailing); |
| 1581 | const data = tree.nodes.items(.data)[node]; | ||
| 1582 | const members_range = tree.extraData(data.rhs, Node.SubRange); | ||
| 1583 | return tree.fullContainerDecl(.{ | ||
| 1584 | .main_token = tree.nodes.items(.main_token)[node], | ||
| 1585 | .enum_token = null, | ||
| 1586 | .members = tree.extra_data[members_range.start..members_range.end], | ||
| 1587 | .arg = data.lhs, | ||
| 1588 | }); | ||
| 1589 | } | ||
| 882 | 1590 | ||
| 883 | pub fn decls(self: *Root) []*Node { | 1591 | pub fn taggedUnionTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.ContainerDecl { |
| 884 | const decls_start = @ptrCast([*]u8, self) + @sizeOf(Root); | 1592 | assert(tree.nodes.items(.tag)[node] == .tagged_union_two or |
| 885 | return @ptrCast([*]*Node, decls_start)[0..self.decls_len]; | 1593 | tree.nodes.items(.tag)[node] == .tagged_union_two_trailing); |
| 886 | } | 1594 | const data = tree.nodes.items(.data)[node]; |
| 1595 | buffer.* = .{ data.lhs, data.rhs }; | ||
| 1596 | const members = if (data.rhs != 0) | ||
| 1597 | buffer[0..2] | ||
| 1598 | else if (data.lhs != 0) | ||
| 1599 | buffer[0..1] | ||
| 1600 | else | ||
| 1601 | buffer[0..0]; | ||
| 1602 | const main_token = tree.nodes.items(.main_token)[node]; | ||
| 1603 | return tree.fullContainerDecl(.{ | ||
| 1604 | .main_token = main_token, | ||
| 1605 | .enum_token = main_token + 2, // union lparen enum | ||
| 1606 | .members = members, | ||
| 1607 | .arg = 0, | ||
| 1608 | }); | ||
| 1609 | } | ||
| 887 | 1610 | ||
| 888 | pub fn declsConst(self: *const Root) []const *Node { | 1611 | pub fn taggedUnion(tree: Tree, node: Node.Index) full.ContainerDecl { |
| 889 | const decls_start = @ptrCast([*]const u8, self) + @sizeOf(Root); | 1612 | assert(tree.nodes.items(.tag)[node] == .tagged_union or |
| 890 | return @ptrCast([*]const *Node, decls_start)[0..self.decls_len]; | 1613 | tree.nodes.items(.tag)[node] == .tagged_union_trailing); |
| 891 | } | 1614 | const data = tree.nodes.items(.data)[node]; |
| 1615 | const main_token = tree.nodes.items(.main_token)[node]; | ||
| 1616 | return tree.fullContainerDecl(.{ | ||
| 1617 | .main_token = main_token, | ||
| 1618 | .enum_token = main_token + 2, // union lparen enum | ||
| 1619 | .members = tree.extra_data[data.lhs..data.rhs], | ||
| 1620 | .arg = 0, | ||
| 1621 | }); | ||
| 1622 | } | ||
| 892 | 1623 | ||
| 893 | pub fn firstToken(self: *const Root) TokenIndex { | 1624 | pub fn taggedUnionEnumTag(tree: Tree, node: Node.Index) full.ContainerDecl { |
| 894 | if (self.decls_len == 0) return self.eof_token; | 1625 | assert(tree.nodes.items(.tag)[node] == .tagged_union_enum_tag or |
| 895 | return self.declsConst()[0].firstToken(); | 1626 | tree.nodes.items(.tag)[node] == .tagged_union_enum_tag_trailing); |
| 896 | } | 1627 | const data = tree.nodes.items(.data)[node]; |
| 1628 | const members_range = tree.extraData(data.rhs, Node.SubRange); | ||
| 1629 | const main_token = tree.nodes.items(.main_token)[node]; | ||
| 1630 | return tree.fullContainerDecl(.{ | ||
| 1631 | .main_token = main_token, | ||
| 1632 | .enum_token = main_token + 2, // union lparen enum | ||
| 1633 | .members = tree.extra_data[members_range.start..members_range.end], | ||
| 1634 | .arg = data.lhs, | ||
| 1635 | }); | ||
| 1636 | } | ||
| 897 | 1637 | ||
| 898 | pub fn lastToken(self: *const Root) TokenIndex { | 1638 | pub fn switchCaseOne(tree: Tree, node: Node.Index) full.SwitchCase { |
| 899 | if (self.decls_len == 0) return self.eof_token; | 1639 | const data = &tree.nodes.items(.data)[node]; |
| 900 | return self.declsConst()[self.decls_len - 1].lastToken(); | 1640 | const values: *[1]Node.Index = &data.lhs; |
| 901 | } | 1641 | return tree.fullSwitchCase(.{ |
| 1642 | .values = if (data.lhs == 0) values[0..0] else values[0..1], | ||
| 1643 | .arrow_token = tree.nodes.items(.main_token)[node], | ||
| 1644 | .target_expr = data.rhs, | ||
| 1645 | }); | ||
| 1646 | } | ||
| 902 | 1647 | ||
| 903 | fn sizeInBytes(decls_len: NodeIndex) usize { | 1648 | pub fn switchCase(tree: Tree, node: Node.Index) full.SwitchCase { |
| 904 | return @sizeOf(Root) + @sizeOf(*Node) * @as(usize, decls_len); | 1649 | const data = tree.nodes.items(.data)[node]; |
| 905 | } | 1650 | const extra = tree.extraData(data.lhs, Node.SubRange); |
| 906 | }; | 1651 | return tree.fullSwitchCase(.{ |
| 1652 | .values = tree.extra_data[extra.start..extra.end], | ||
| 1653 | .arrow_token = tree.nodes.items(.main_token)[node], | ||
| 1654 | .target_expr = data.rhs, | ||
| 1655 | }); | ||
| 1656 | } | ||
| 907 | 1657 | ||
| 908 | /// Trailed in memory by possibly many things, with each optional thing | 1658 | pub fn asmSimple(tree: Tree, node: Node.Index) full.Asm { |
| 909 | /// determined by a bit in `trailer_flags`. | 1659 | const data = tree.nodes.items(.data)[node]; |
| 910 | pub const VarDecl = struct { | 1660 | return tree.fullAsm(.{ |
| 911 | base: Node = Node{ .tag = .VarDecl }, | 1661 | .asm_token = tree.nodes.items(.main_token)[node], |
| 912 | trailer_flags: TrailerFlags, | 1662 | .template = data.lhs, |
| 913 | mut_token: TokenIndex, | 1663 | .items = &.{}, |
| 914 | name_token: TokenIndex, | 1664 | .rparen = data.rhs, |
| 915 | semicolon_token: TokenIndex, | ||
| 916 | |||
| 917 | pub const TrailerFlags = std.meta.TrailerFlags(struct { | ||
| 918 | doc_comments: *DocComment, | ||
| 919 | visib_token: TokenIndex, | ||
| 920 | thread_local_token: TokenIndex, | ||
| 921 | eq_token: TokenIndex, | ||
| 922 | comptime_token: TokenIndex, | ||
| 923 | extern_export_token: TokenIndex, | ||
| 924 | lib_name: *Node, | ||
| 925 | type_node: *Node, | ||
| 926 | align_node: *Node, | ||
| 927 | section_node: *Node, | ||
| 928 | init_node: *Node, | ||
| 929 | }); | 1665 | }); |
| 1666 | } | ||
| 930 | 1667 | ||
| 931 | pub fn getDocComments(self: *const VarDecl) ?*DocComment { | 1668 | pub fn asmFull(tree: Tree, node: Node.Index) full.Asm { |
| 932 | return self.getTrailer(.doc_comments); | 1669 | const data = tree.nodes.items(.data)[node]; |
| 933 | } | 1670 | const extra = tree.extraData(data.rhs, Node.Asm); |
| 1671 | return tree.fullAsm(.{ | ||
| 1672 | .asm_token = tree.nodes.items(.main_token)[node], | ||
| 1673 | .template = data.lhs, | ||
| 1674 | .items = tree.extra_data[extra.items_start..extra.items_end], | ||
| 1675 | .rparen = extra.rparen, | ||
| 1676 | }); | ||
| 1677 | } | ||
| 934 | 1678 | ||
| 935 | pub fn setDocComments(self: *VarDecl, value: *DocComment) void { | 1679 | pub fn whileSimple(tree: Tree, node: Node.Index) full.While { |
| 936 | self.setTrailer(.doc_comments, value); | 1680 | const data = tree.nodes.items(.data)[node]; |
| 937 | } | 1681 | return tree.fullWhile(.{ |
| 1682 | .while_token = tree.nodes.items(.main_token)[node], | ||
| 1683 | .cond_expr = data.lhs, | ||
| 1684 | .cont_expr = 0, | ||
| 1685 | .then_expr = data.rhs, | ||
| 1686 | .else_expr = 0, | ||
| 1687 | }); | ||
| 1688 | } | ||
| 938 | 1689 | ||
| 939 | pub fn getVisibToken(self: *const VarDecl) ?TokenIndex { | 1690 | pub fn whileCont(tree: Tree, node: Node.Index) full.While { |
| 940 | return self.getTrailer(.visib_token); | 1691 | const data = tree.nodes.items(.data)[node]; |
| 941 | } | 1692 | const extra = tree.extraData(data.rhs, Node.WhileCont); |
| 1693 | return tree.fullWhile(.{ | ||
| 1694 | .while_token = tree.nodes.items(.main_token)[node], | ||
| 1695 | .cond_expr = data.lhs, | ||
| 1696 | .cont_expr = extra.cont_expr, | ||
| 1697 | .then_expr = extra.then_expr, | ||
| 1698 | .else_expr = 0, | ||
| 1699 | }); | ||
| 1700 | } | ||
| 942 | 1701 | ||
| 943 | pub fn setVisibToken(self: *VarDecl, value: TokenIndex) void { | 1702 | pub fn whileFull(tree: Tree, node: Node.Index) full.While { |
| 944 | self.setTrailer(.visib_token, value); | 1703 | const data = tree.nodes.items(.data)[node]; |
| 945 | } | 1704 | const extra = tree.extraData(data.rhs, Node.While); |
| 1705 | return tree.fullWhile(.{ | ||
| 1706 | .while_token = tree.nodes.items(.main_token)[node], | ||
| 1707 | .cond_expr = data.lhs, | ||
| 1708 | .cont_expr = extra.cont_expr, | ||
| 1709 | .then_expr = extra.then_expr, | ||
| 1710 | .else_expr = extra.else_expr, | ||
| 1711 | }); | ||
| 1712 | } | ||
| 946 | 1713 | ||
| 947 | pub fn getThreadLocalToken(self: *const VarDecl) ?TokenIndex { | 1714 | pub fn forSimple(tree: Tree, node: Node.Index) full.While { |
| 948 | return self.getTrailer(.thread_local_token); | 1715 | const data = tree.nodes.items(.data)[node]; |
| 949 | } | 1716 | return tree.fullWhile(.{ |
| 1717 | .while_token = tree.nodes.items(.main_token)[node], | ||
| 1718 | .cond_expr = data.lhs, | ||
| 1719 | .cont_expr = 0, | ||
| 1720 | .then_expr = data.rhs, | ||
| 1721 | .else_expr = 0, | ||
| 1722 | }); | ||
| 1723 | } | ||
| 950 | 1724 | ||
| 951 | pub fn setThreadLocalToken(self: *VarDecl, value: TokenIndex) void { | 1725 | pub fn forFull(tree: Tree, node: Node.Index) full.While { |
| 952 | self.setTrailer(.thread_local_token, value); | 1726 | const data = tree.nodes.items(.data)[node]; |
| 953 | } | 1727 | const extra = tree.extraData(data.rhs, Node.If); |
| 1728 | return tree.fullWhile(.{ | ||
| 1729 | .while_token = tree.nodes.items(.main_token)[node], | ||
| 1730 | .cond_expr = data.lhs, | ||
| 1731 | .cont_expr = 0, | ||
| 1732 | .then_expr = extra.then_expr, | ||
| 1733 | .else_expr = extra.else_expr, | ||
| 1734 | }); | ||
| 1735 | } | ||
| 954 | 1736 | ||
| 955 | pub fn getEqToken(self: *const VarDecl) ?TokenIndex { | 1737 | pub fn callOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.Call { |
| 956 | return self.getTrailer(.eq_token); | 1738 | const data = tree.nodes.items(.data)[node]; |
| 957 | } | 1739 | buffer.* = .{data.rhs}; |
| 1740 | const params = if (data.rhs != 0) buffer[0..1] else buffer[0..0]; | ||
| 1741 | return tree.fullCall(.{ | ||
| 1742 | .lparen = tree.nodes.items(.main_token)[node], | ||
| 1743 | .fn_expr = data.lhs, | ||
| 1744 | .params = params, | ||
| 1745 | }); | ||
| 1746 | } | ||
| 958 | 1747 | ||
| 959 | pub fn setEqToken(self: *VarDecl, value: TokenIndex) void { | 1748 | pub fn callFull(tree: Tree, node: Node.Index) full.Call { |
| 960 | self.setTrailer(.eq_token, value); | 1749 | const data = tree.nodes.items(.data)[node]; |
| 961 | } | 1750 | const extra = tree.extraData(data.rhs, Node.SubRange); |
| 1751 | return tree.fullCall(.{ | ||
| 1752 | .lparen = tree.nodes.items(.main_token)[node], | ||
| 1753 | .fn_expr = data.lhs, | ||
| 1754 | .params = tree.extra_data[extra.start..extra.end], | ||
| 1755 | }); | ||
| 1756 | } | ||
| 962 | 1757 | ||
| 963 | pub fn getComptimeToken(self: *const VarDecl) ?TokenIndex { | 1758 | fn fullVarDecl(tree: Tree, info: full.VarDecl.Ast) full.VarDecl { |
| 964 | return self.getTrailer(.comptime_token); | 1759 | const token_tags = tree.tokens.items(.tag); |
| 1760 | var result: full.VarDecl = .{ | ||
| 1761 | .ast = info, | ||
| 1762 | .visib_token = null, | ||
| 1763 | .extern_export_token = null, | ||
| 1764 | .lib_name = null, | ||
| 1765 | .threadlocal_token = null, | ||
| 1766 | .comptime_token = null, | ||
| 1767 | }; | ||
| 1768 | var i = info.mut_token; | ||
| 1769 | while (i > 0) { | ||
| 1770 | i -= 1; | ||
| 1771 | switch (token_tags[i]) { | ||
| 1772 | .keyword_extern, .keyword_export => result.extern_export_token = i, | ||
| 1773 | .keyword_comptime => result.comptime_token = i, | ||
| 1774 | .keyword_pub => result.visib_token = i, | ||
| 1775 | .keyword_threadlocal => result.threadlocal_token = i, | ||
| 1776 | .string_literal => result.lib_name = i, | ||
| 1777 | else => break, | ||
| 1778 | } | ||
| 965 | } | 1779 | } |
| 1780 | return result; | ||
| 1781 | } | ||
| 966 | 1782 | ||
| 967 | pub fn setComptimeToken(self: *VarDecl, value: TokenIndex) void { | 1783 | fn fullIf(tree: Tree, info: full.If.Ast) full.If { |
| 968 | self.setTrailer(.comptime_token, value); | 1784 | const token_tags = tree.tokens.items(.tag); |
| 1785 | var result: full.If = .{ | ||
| 1786 | .ast = info, | ||
| 1787 | .payload_token = null, | ||
| 1788 | .error_token = null, | ||
| 1789 | .else_token = undefined, | ||
| 1790 | }; | ||
| 1791 | // if (cond_expr) |x| | ||
| 1792 | // ^ ^ | ||
| 1793 | const payload_pipe = tree.lastToken(info.cond_expr) + 2; | ||
| 1794 | if (token_tags[payload_pipe] == .pipe) { | ||
| 1795 | result.payload_token = payload_pipe + 1; | ||
| 969 | } | 1796 | } |
| 970 | 1797 | if (info.else_expr != 0) { | |
| 971 | pub fn getExternExportToken(self: *const VarDecl) ?TokenIndex { | 1798 | // then_expr else |x| |
| 972 | return self.getTrailer(.extern_export_token); | 1799 | // ^ ^ |
| 1800 | result.else_token = tree.lastToken(info.then_expr) + 1; | ||
| 1801 | if (token_tags[result.else_token + 1] == .pipe) { | ||
| 1802 | result.error_token = result.else_token + 2; | ||
| 1803 | } | ||
| 973 | } | 1804 | } |
| 1805 | return result; | ||
| 1806 | } | ||
| 974 | 1807 | ||
| 975 | pub fn setExternExportToken(self: *VarDecl, value: TokenIndex) void { | 1808 | fn fullContainerField(tree: Tree, info: full.ContainerField.Ast) full.ContainerField { |
| 976 | self.setTrailer(.extern_export_token, value); | 1809 | const token_tags = tree.tokens.items(.tag); |
| 1810 | var result: full.ContainerField = .{ | ||
| 1811 | .ast = info, | ||
| 1812 | .comptime_token = null, | ||
| 1813 | }; | ||
| 1814 | // comptime name: type = init, | ||
| 1815 | // ^ | ||
| 1816 | if (info.name_token > 0 and token_tags[info.name_token - 1] == .keyword_comptime) { | ||
| 1817 | result.comptime_token = info.name_token - 1; | ||
| 977 | } | 1818 | } |
| 1819 | return result; | ||
| 1820 | } | ||
| 978 | 1821 | ||
| 979 | pub fn getLibName(self: *const VarDecl) ?*Node { | 1822 | fn fullFnProto(tree: Tree, info: full.FnProto.Ast) full.FnProto { |
| 980 | return self.getTrailer(.lib_name); | 1823 | const token_tags = tree.tokens.items(.tag); |
| 981 | } | 1824 | var result: full.FnProto = .{ |
| 982 | 1825 | .ast = info, | |
| 983 | pub fn setLibName(self: *VarDecl, value: *Node) void { | 1826 | .visib_token = null, |
| 984 | self.setTrailer(.lib_name, value); | 1827 | .extern_export_token = null, |
| 985 | } | 1828 | .lib_name = null, |
| 986 | 1829 | .name_token = null, | |
| 987 | pub fn getTypeNode(self: *const VarDecl) ?*Node { | 1830 | .lparen = undefined, |
| 988 | return self.getTrailer(.type_node); | 1831 | }; |
| 989 | } | 1832 | var i = info.fn_token; |
| 990 | 1833 | while (i > 0) { | |
| 991 | pub fn setTypeNode(self: *VarDecl, value: *Node) void { | ||
| 992 | self.setTrailer(.type_node, value); | ||
| 993 | } | ||
| 994 | |||
| 995 | pub fn getAlignNode(self: *const VarDecl) ?*Node { | ||
| 996 | return self.getTrailer(.align_node); | ||
| 997 | } | ||
| 998 | |||
| 999 | pub fn setAlignNode(self: *VarDecl, value: *Node) void { | ||
| 1000 | self.setTrailer(.align_node, value); | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | pub fn getSectionNode(self: *const VarDecl) ?*Node { | ||
| 1004 | return self.getTrailer(.section_node); | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | pub fn setSectionNode(self: *VarDecl, value: *Node) void { | ||
| 1008 | self.setTrailer(.section_node, value); | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | pub fn getInitNode(self: *const VarDecl) ?*Node { | ||
| 1012 | return self.getTrailer(.init_node); | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | pub fn setInitNode(self: *VarDecl, value: *Node) void { | ||
| 1016 | self.setTrailer(.init_node, value); | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | pub const RequiredFields = struct { | ||
| 1020 | mut_token: TokenIndex, | ||
| 1021 | name_token: TokenIndex, | ||
| 1022 | semicolon_token: TokenIndex, | ||
| 1023 | }; | ||
| 1024 | |||
| 1025 | fn getTrailer(self: *const VarDecl, comptime field: TrailerFlags.FieldEnum) ?TrailerFlags.Field(field) { | ||
| 1026 | const trailers_start = @ptrCast([*]const u8, self) + @sizeOf(VarDecl); | ||
| 1027 | return self.trailer_flags.get(trailers_start, field); | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | fn setTrailer(self: *VarDecl, comptime field: TrailerFlags.FieldEnum, value: TrailerFlags.Field(field)) void { | ||
| 1031 | const trailers_start = @ptrCast([*]u8, self) + @sizeOf(VarDecl); | ||
| 1032 | self.trailer_flags.set(trailers_start, field, value); | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: TrailerFlags.InitStruct) !*VarDecl { | ||
| 1036 | const trailer_flags = TrailerFlags.init(trailers); | ||
| 1037 | const bytes = try allocator.alignedAlloc(u8, @alignOf(VarDecl), sizeInBytes(trailer_flags)); | ||
| 1038 | const var_decl = @ptrCast(*VarDecl, bytes.ptr); | ||
| 1039 | var_decl.* = .{ | ||
| 1040 | .trailer_flags = trailer_flags, | ||
| 1041 | .mut_token = required.mut_token, | ||
| 1042 | .name_token = required.name_token, | ||
| 1043 | .semicolon_token = required.semicolon_token, | ||
| 1044 | }; | ||
| 1045 | const trailers_start = bytes.ptr + @sizeOf(VarDecl); | ||
| 1046 | trailer_flags.setMany(trailers_start, trailers); | ||
| 1047 | return var_decl; | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | pub fn destroy(self: *VarDecl, allocator: *mem.Allocator) void { | ||
| 1051 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.trailer_flags)]; | ||
| 1052 | allocator.free(bytes); | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | pub fn iterate(self: *const VarDecl, index: usize) ?*Node { | ||
| 1056 | var i = index; | ||
| 1057 | |||
| 1058 | if (self.getTypeNode()) |type_node| { | ||
| 1059 | if (i < 1) return type_node; | ||
| 1060 | i -= 1; | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | if (self.getAlignNode()) |align_node| { | ||
| 1064 | if (i < 1) return align_node; | ||
| 1065 | i -= 1; | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | if (self.getSectionNode()) |section_node| { | ||
| 1069 | if (i < 1) return section_node; | ||
| 1070 | i -= 1; | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | if (self.getInitNode()) |init_node| { | ||
| 1074 | if (i < 1) return init_node; | ||
| 1075 | i -= 1; | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | return null; | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | pub fn firstToken(self: *const VarDecl) TokenIndex { | ||
| 1082 | if (self.getVisibToken()) |visib_token| return visib_token; | ||
| 1083 | if (self.getThreadLocalToken()) |thread_local_token| return thread_local_token; | ||
| 1084 | if (self.getComptimeToken()) |comptime_token| return comptime_token; | ||
| 1085 | if (self.getExternExportToken()) |extern_export_token| return extern_export_token; | ||
| 1086 | assert(self.getLibName() == null); | ||
| 1087 | return self.mut_token; | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | pub fn lastToken(self: *const VarDecl) TokenIndex { | ||
| 1091 | return self.semicolon_token; | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | fn sizeInBytes(trailer_flags: TrailerFlags) usize { | ||
| 1095 | return @sizeOf(VarDecl) + trailer_flags.sizeInBytes(); | ||
| 1096 | } | ||
| 1097 | }; | ||
| 1098 | |||
| 1099 | pub const Use = struct { | ||
| 1100 | base: Node = Node{ .tag = .Use }, | ||
| 1101 | doc_comments: ?*DocComment, | ||
| 1102 | visib_token: ?TokenIndex, | ||
| 1103 | use_token: TokenIndex, | ||
| 1104 | expr: *Node, | ||
| 1105 | semicolon_token: TokenIndex, | ||
| 1106 | |||
| 1107 | pub fn iterate(self: *const Use, index: usize) ?*Node { | ||
| 1108 | var i = index; | ||
| 1109 | |||
| 1110 | if (i < 1) return self.expr; | ||
| 1111 | i -= 1; | ||
| 1112 | |||
| 1113 | return null; | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | pub fn firstToken(self: *const Use) TokenIndex { | ||
| 1117 | if (self.visib_token) |visib_token| return visib_token; | ||
| 1118 | return self.use_token; | ||
| 1119 | } | ||
| 1120 | |||
| 1121 | pub fn lastToken(self: *const Use) TokenIndex { | ||
| 1122 | return self.semicolon_token; | ||
| 1123 | } | ||
| 1124 | }; | ||
| 1125 | |||
| 1126 | pub const ErrorSetDecl = struct { | ||
| 1127 | base: Node = Node{ .tag = .ErrorSetDecl }, | ||
| 1128 | error_token: TokenIndex, | ||
| 1129 | rbrace_token: TokenIndex, | ||
| 1130 | decls_len: NodeIndex, | ||
| 1131 | |||
| 1132 | /// After this the caller must initialize the decls list. | ||
| 1133 | pub fn alloc(allocator: *mem.Allocator, decls_len: NodeIndex) !*ErrorSetDecl { | ||
| 1134 | const bytes = try allocator.alignedAlloc(u8, @alignOf(ErrorSetDecl), sizeInBytes(decls_len)); | ||
| 1135 | return @ptrCast(*ErrorSetDecl, bytes.ptr); | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | pub fn free(self: *ErrorSetDecl, allocator: *mem.Allocator) void { | ||
| 1139 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.decls_len)]; | ||
| 1140 | allocator.free(bytes); | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | pub fn iterate(self: *const ErrorSetDecl, index: usize) ?*Node { | ||
| 1144 | var i = index; | ||
| 1145 | |||
| 1146 | if (i < self.decls_len) return self.declsConst()[i]; | ||
| 1147 | i -= self.decls_len; | ||
| 1148 | |||
| 1149 | return null; | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | pub fn firstToken(self: *const ErrorSetDecl) TokenIndex { | ||
| 1153 | return self.error_token; | ||
| 1154 | } | ||
| 1155 | |||
| 1156 | pub fn lastToken(self: *const ErrorSetDecl) TokenIndex { | ||
| 1157 | return self.rbrace_token; | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | pub fn decls(self: *ErrorSetDecl) []*Node { | ||
| 1161 | const decls_start = @ptrCast([*]u8, self) + @sizeOf(ErrorSetDecl); | ||
| 1162 | return @ptrCast([*]*Node, decls_start)[0..self.decls_len]; | ||
| 1163 | } | ||
| 1164 | |||
| 1165 | pub fn declsConst(self: *const ErrorSetDecl) []const *Node { | ||
| 1166 | const decls_start = @ptrCast([*]const u8, self) + @sizeOf(ErrorSetDecl); | ||
| 1167 | return @ptrCast([*]const *Node, decls_start)[0..self.decls_len]; | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | fn sizeInBytes(decls_len: NodeIndex) usize { | ||
| 1171 | return @sizeOf(ErrorSetDecl) + @sizeOf(*Node) * @as(usize, decls_len); | ||
| 1172 | } | ||
| 1173 | }; | ||
| 1174 | |||
| 1175 | /// The fields and decls Node pointers directly follow this struct in memory. | ||
| 1176 | pub const ContainerDecl = struct { | ||
| 1177 | base: Node = Node{ .tag = .ContainerDecl }, | ||
| 1178 | kind_token: TokenIndex, | ||
| 1179 | layout_token: ?TokenIndex, | ||
| 1180 | lbrace_token: TokenIndex, | ||
| 1181 | rbrace_token: TokenIndex, | ||
| 1182 | fields_and_decls_len: NodeIndex, | ||
| 1183 | init_arg_expr: InitArg, | ||
| 1184 | |||
| 1185 | pub const InitArg = union(enum) { | ||
| 1186 | None, | ||
| 1187 | Enum: ?*Node, | ||
| 1188 | Type: *Node, | ||
| 1189 | }; | ||
| 1190 | |||
| 1191 | /// After this the caller must initialize the fields_and_decls list. | ||
| 1192 | pub fn alloc(allocator: *mem.Allocator, fields_and_decls_len: NodeIndex) !*ContainerDecl { | ||
| 1193 | const bytes = try allocator.alignedAlloc(u8, @alignOf(ContainerDecl), sizeInBytes(fields_and_decls_len)); | ||
| 1194 | return @ptrCast(*ContainerDecl, bytes.ptr); | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | pub fn free(self: *ContainerDecl, allocator: *mem.Allocator) void { | ||
| 1198 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.fields_and_decls_len)]; | ||
| 1199 | allocator.free(bytes); | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | pub fn iterate(self: *const ContainerDecl, index: usize) ?*Node { | ||
| 1203 | var i = index; | ||
| 1204 | |||
| 1205 | switch (self.init_arg_expr) { | ||
| 1206 | .Type => |t| { | ||
| 1207 | if (i < 1) return t; | ||
| 1208 | i -= 1; | ||
| 1209 | }, | ||
| 1210 | .None, .Enum => {}, | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | if (i < self.fields_and_decls_len) return self.fieldsAndDeclsConst()[i]; | ||
| 1214 | i -= self.fields_and_decls_len; | ||
| 1215 | |||
| 1216 | return null; | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | pub fn firstToken(self: *const ContainerDecl) TokenIndex { | ||
| 1220 | if (self.layout_token) |layout_token| { | ||
| 1221 | return layout_token; | ||
| 1222 | } | ||
| 1223 | return self.kind_token; | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | pub fn lastToken(self: *const ContainerDecl) TokenIndex { | ||
| 1227 | return self.rbrace_token; | ||
| 1228 | } | ||
| 1229 | |||
| 1230 | pub fn fieldsAndDecls(self: *ContainerDecl) []*Node { | ||
| 1231 | const decls_start = @ptrCast([*]u8, self) + @sizeOf(ContainerDecl); | ||
| 1232 | return @ptrCast([*]*Node, decls_start)[0..self.fields_and_decls_len]; | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | pub fn fieldsAndDeclsConst(self: *const ContainerDecl) []const *Node { | ||
| 1236 | const decls_start = @ptrCast([*]const u8, self) + @sizeOf(ContainerDecl); | ||
| 1237 | return @ptrCast([*]const *Node, decls_start)[0..self.fields_and_decls_len]; | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | fn sizeInBytes(fields_and_decls_len: NodeIndex) usize { | ||
| 1241 | return @sizeOf(ContainerDecl) + @sizeOf(*Node) * @as(usize, fields_and_decls_len); | ||
| 1242 | } | ||
| 1243 | }; | ||
| 1244 | |||
| 1245 | pub const ContainerField = struct { | ||
| 1246 | base: Node = Node{ .tag = .ContainerField }, | ||
| 1247 | doc_comments: ?*DocComment, | ||
| 1248 | comptime_token: ?TokenIndex, | ||
| 1249 | name_token: TokenIndex, | ||
| 1250 | type_expr: ?*Node, | ||
| 1251 | value_expr: ?*Node, | ||
| 1252 | align_expr: ?*Node, | ||
| 1253 | |||
| 1254 | pub fn iterate(self: *const ContainerField, index: usize) ?*Node { | ||
| 1255 | var i = index; | ||
| 1256 | |||
| 1257 | if (self.type_expr) |type_expr| { | ||
| 1258 | if (i < 1) return type_expr; | ||
| 1259 | i -= 1; | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | if (self.align_expr) |align_expr| { | ||
| 1263 | if (i < 1) return align_expr; | ||
| 1264 | i -= 1; | ||
| 1265 | } | ||
| 1266 | |||
| 1267 | if (self.value_expr) |value_expr| { | ||
| 1268 | if (i < 1) return value_expr; | ||
| 1269 | i -= 1; | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | return null; | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | pub fn firstToken(self: *const ContainerField) TokenIndex { | ||
| 1276 | return self.comptime_token orelse self.name_token; | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | pub fn lastToken(self: *const ContainerField) TokenIndex { | ||
| 1280 | if (self.value_expr) |value_expr| { | ||
| 1281 | return value_expr.lastToken(); | ||
| 1282 | } | ||
| 1283 | if (self.align_expr) |align_expr| { | ||
| 1284 | // The expression refers to what's inside the parenthesis, the | ||
| 1285 | // last token is the closing one | ||
| 1286 | return align_expr.lastToken() + 1; | ||
| 1287 | } | ||
| 1288 | if (self.type_expr) |type_expr| { | ||
| 1289 | return type_expr.lastToken(); | ||
| 1290 | } | ||
| 1291 | |||
| 1292 | return self.name_token; | ||
| 1293 | } | ||
| 1294 | }; | ||
| 1295 | |||
| 1296 | pub const ErrorTag = struct { | ||
| 1297 | base: Node = Node{ .tag = .ErrorTag }, | ||
| 1298 | doc_comments: ?*DocComment, | ||
| 1299 | name_token: TokenIndex, | ||
| 1300 | |||
| 1301 | pub fn iterate(self: *const ErrorTag, index: usize) ?*Node { | ||
| 1302 | var i = index; | ||
| 1303 | |||
| 1304 | if (self.doc_comments) |comments| { | ||
| 1305 | if (i < 1) return &comments.base; | ||
| 1306 | i -= 1; | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | return null; | ||
| 1310 | } | ||
| 1311 | |||
| 1312 | pub fn firstToken(self: *const ErrorTag) TokenIndex { | ||
| 1313 | return self.name_token; | ||
| 1314 | } | ||
| 1315 | |||
| 1316 | pub fn lastToken(self: *const ErrorTag) TokenIndex { | ||
| 1317 | return self.name_token; | ||
| 1318 | } | ||
| 1319 | }; | ||
| 1320 | |||
| 1321 | pub const OneToken = struct { | ||
| 1322 | base: Node, | ||
| 1323 | token: TokenIndex, | ||
| 1324 | |||
| 1325 | pub fn iterate(self: *const OneToken, index: usize) ?*Node { | ||
| 1326 | return null; | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | pub fn firstToken(self: *const OneToken) TokenIndex { | ||
| 1330 | return self.token; | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | pub fn lastToken(self: *const OneToken) TokenIndex { | ||
| 1334 | return self.token; | ||
| 1335 | } | ||
| 1336 | }; | ||
| 1337 | |||
| 1338 | /// The params are directly after the FnProto in memory. | ||
| 1339 | /// Next, each optional thing determined by a bit in `trailer_flags`. | ||
| 1340 | pub const FnProto = struct { | ||
| 1341 | base: Node = Node{ .tag = .FnProto }, | ||
| 1342 | trailer_flags: TrailerFlags, | ||
| 1343 | fn_token: TokenIndex, | ||
| 1344 | params_len: NodeIndex, | ||
| 1345 | return_type: ReturnType, | ||
| 1346 | |||
| 1347 | pub const TrailerFlags = std.meta.TrailerFlags(struct { | ||
| 1348 | doc_comments: *DocComment, | ||
| 1349 | body_node: *Node, | ||
| 1350 | lib_name: *Node, // populated if this is an extern declaration | ||
| 1351 | align_expr: *Node, // populated if align(A) is present | ||
| 1352 | section_expr: *Node, // populated if linksection(A) is present | ||
| 1353 | callconv_expr: *Node, // populated if callconv(A) is present | ||
| 1354 | visib_token: TokenIndex, | ||
| 1355 | name_token: TokenIndex, | ||
| 1356 | var_args_token: TokenIndex, | ||
| 1357 | extern_export_inline_token: TokenIndex, | ||
| 1358 | is_extern_prototype: void, // TODO: Remove once extern fn rewriting is | ||
| 1359 | is_async: void, // TODO: remove once async fn rewriting is | ||
| 1360 | is_inline: void, // TODO: remove once inline fn rewriting is | ||
| 1361 | }); | ||
| 1362 | |||
| 1363 | pub const RequiredFields = struct { | ||
| 1364 | fn_token: TokenIndex, | ||
| 1365 | params_len: NodeIndex, | ||
| 1366 | return_type: ReturnType, | ||
| 1367 | }; | ||
| 1368 | |||
| 1369 | pub const ReturnType = union(enum) { | ||
| 1370 | Explicit: *Node, | ||
| 1371 | InferErrorSet: *Node, | ||
| 1372 | Invalid: TokenIndex, | ||
| 1373 | }; | ||
| 1374 | |||
| 1375 | pub const ParamDecl = struct { | ||
| 1376 | doc_comments: ?*DocComment, | ||
| 1377 | comptime_token: ?TokenIndex, | ||
| 1378 | noalias_token: ?TokenIndex, | ||
| 1379 | name_token: ?TokenIndex, | ||
| 1380 | param_type: ParamType, | ||
| 1381 | |||
| 1382 | pub const ParamType = union(enum) { | ||
| 1383 | any_type: *Node, | ||
| 1384 | type_expr: *Node, | ||
| 1385 | }; | ||
| 1386 | |||
| 1387 | pub fn iterate(self: *const ParamDecl, index: usize) ?*Node { | ||
| 1388 | var i = index; | ||
| 1389 | |||
| 1390 | if (i < 1) { | ||
| 1391 | switch (self.param_type) { | ||
| 1392 | .any_type, .type_expr => |node| return node, | ||
| 1393 | } | ||
| 1394 | } | ||
| 1395 | i -= 1; | ||
| 1396 | |||
| 1397 | return null; | ||
| 1398 | } | ||
| 1399 | |||
| 1400 | pub fn firstToken(self: *const ParamDecl) TokenIndex { | ||
| 1401 | if (self.comptime_token) |comptime_token| return comptime_token; | ||
| 1402 | if (self.noalias_token) |noalias_token| return noalias_token; | ||
| 1403 | if (self.name_token) |name_token| return name_token; | ||
| 1404 | switch (self.param_type) { | ||
| 1405 | .any_type, .type_expr => |node| return node.firstToken(), | ||
| 1406 | } | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | pub fn lastToken(self: *const ParamDecl) TokenIndex { | ||
| 1410 | switch (self.param_type) { | ||
| 1411 | .any_type, .type_expr => |node| return node.lastToken(), | ||
| 1412 | } | ||
| 1413 | } | ||
| 1414 | }; | ||
| 1415 | |||
| 1416 | /// For debugging purposes. | ||
| 1417 | pub fn dump(self: *const FnProto) void { | ||
| 1418 | const trailers_start = @alignCast( | ||
| 1419 | @alignOf(ParamDecl), | ||
| 1420 | @ptrCast([*]const u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len, | ||
| 1421 | ); | ||
| 1422 | std.debug.print("{*} flags: {b} name_token: {s} {*} params_len: {d}\n", .{ | ||
| 1423 | self, | ||
| 1424 | self.trailer_flags.bits, | ||
| 1425 | self.getNameToken(), | ||
| 1426 | self.trailer_flags.ptrConst(trailers_start, .name_token), | ||
| 1427 | self.params_len, | ||
| 1428 | }); | ||
| 1429 | } | ||
| 1430 | |||
| 1431 | pub fn getDocComments(self: *const FnProto) ?*DocComment { | ||
| 1432 | return self.getTrailer(.doc_comments); | ||
| 1433 | } | ||
| 1434 | |||
| 1435 | pub fn setDocComments(self: *FnProto, value: *DocComment) void { | ||
| 1436 | self.setTrailer(.doc_comments, value); | ||
| 1437 | } | ||
| 1438 | |||
| 1439 | pub fn getBodyNode(self: *const FnProto) ?*Node { | ||
| 1440 | return self.getTrailer(.body_node); | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | pub fn setBodyNode(self: *FnProto, value: *Node) void { | ||
| 1444 | self.setTrailer(.body_node, value); | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | pub fn getLibName(self: *const FnProto) ?*Node { | ||
| 1448 | return self.getTrailer(.lib_name); | ||
| 1449 | } | ||
| 1450 | |||
| 1451 | pub fn setLibName(self: *FnProto, value: *Node) void { | ||
| 1452 | self.setTrailer(.lib_name, value); | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | pub fn getAlignExpr(self: *const FnProto) ?*Node { | ||
| 1456 | return self.getTrailer(.align_expr); | ||
| 1457 | } | ||
| 1458 | |||
| 1459 | pub fn setAlignExpr(self: *FnProto, value: *Node) void { | ||
| 1460 | self.setTrailer(.align_expr, value); | ||
| 1461 | } | ||
| 1462 | |||
| 1463 | pub fn getSectionExpr(self: *const FnProto) ?*Node { | ||
| 1464 | return self.getTrailer(.section_expr); | ||
| 1465 | } | ||
| 1466 | |||
| 1467 | pub fn setSectionExpr(self: *FnProto, value: *Node) void { | ||
| 1468 | self.setTrailer(.section_expr, value); | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | pub fn getCallconvExpr(self: *const FnProto) ?*Node { | ||
| 1472 | return self.getTrailer(.callconv_expr); | ||
| 1473 | } | ||
| 1474 | |||
| 1475 | pub fn setCallconvExpr(self: *FnProto, value: *Node) void { | ||
| 1476 | self.setTrailer(.callconv_expr, value); | ||
| 1477 | } | ||
| 1478 | |||
| 1479 | pub fn getVisibToken(self: *const FnProto) ?TokenIndex { | ||
| 1480 | return self.getTrailer(.visib_token); | ||
| 1481 | } | ||
| 1482 | |||
| 1483 | pub fn setVisibToken(self: *FnProto, value: TokenIndex) void { | ||
| 1484 | self.setTrailer(.visib_token, value); | ||
| 1485 | } | ||
| 1486 | |||
| 1487 | pub fn getNameToken(self: *const FnProto) ?TokenIndex { | ||
| 1488 | return self.getTrailer(.name_token); | ||
| 1489 | } | ||
| 1490 | |||
| 1491 | pub fn setNameToken(self: *FnProto, value: TokenIndex) void { | ||
| 1492 | self.setTrailer(.name_token, value); | ||
| 1493 | } | ||
| 1494 | |||
| 1495 | pub fn getVarArgsToken(self: *const FnProto) ?TokenIndex { | ||
| 1496 | return self.getTrailer(.var_args_token); | ||
| 1497 | } | ||
| 1498 | |||
| 1499 | pub fn setVarArgsToken(self: *FnProto, value: TokenIndex) void { | ||
| 1500 | self.setTrailer(.var_args_token, value); | ||
| 1501 | } | ||
| 1502 | |||
| 1503 | pub fn getExternExportInlineToken(self: *const FnProto) ?TokenIndex { | ||
| 1504 | return self.getTrailer(.extern_export_inline_token); | ||
| 1505 | } | ||
| 1506 | |||
| 1507 | pub fn setExternExportInlineToken(self: *FnProto, value: TokenIndex) void { | ||
| 1508 | self.setTrailer(.extern_export_inline_token, value); | ||
| 1509 | } | ||
| 1510 | |||
| 1511 | pub fn getIsExternPrototype(self: *const FnProto) ?void { | ||
| 1512 | return self.getTrailer(.is_extern_prototype); | ||
| 1513 | } | ||
| 1514 | |||
| 1515 | pub fn setIsExternPrototype(self: *FnProto, value: void) void { | ||
| 1516 | self.setTrailer(.is_extern_prototype, value); | ||
| 1517 | } | ||
| 1518 | |||
| 1519 | pub fn getIsAsync(self: *const FnProto) ?void { | ||
| 1520 | return self.getTrailer(.is_async); | ||
| 1521 | } | ||
| 1522 | |||
| 1523 | pub fn setIsAsync(self: *FnProto, value: void) void { | ||
| 1524 | self.setTrailer(.is_async, value); | ||
| 1525 | } | ||
| 1526 | |||
| 1527 | pub fn getIsInline(self: *const FnProto) ?void { | ||
| 1528 | return self.getTrailer(.is_inline); | ||
| 1529 | } | ||
| 1530 | |||
| 1531 | pub fn setIsInline(self: *FnProto, value: void) void { | ||
| 1532 | self.setTrailer(.is_inline, value); | ||
| 1533 | } | ||
| 1534 | |||
| 1535 | fn getTrailer(self: *const FnProto, comptime field: TrailerFlags.FieldEnum) ?TrailerFlags.Field(field) { | ||
| 1536 | const trailers_start = @alignCast( | ||
| 1537 | @alignOf(ParamDecl), | ||
| 1538 | @ptrCast([*]const u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len, | ||
| 1539 | ); | ||
| 1540 | return self.trailer_flags.get(trailers_start, field); | ||
| 1541 | } | ||
| 1542 | |||
| 1543 | fn setTrailer(self: *FnProto, comptime field: TrailerFlags.FieldEnum, value: TrailerFlags.Field(field)) void { | ||
| 1544 | const trailers_start = @alignCast( | ||
| 1545 | @alignOf(ParamDecl), | ||
| 1546 | @ptrCast([*]u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len, | ||
| 1547 | ); | ||
| 1548 | self.trailer_flags.set(trailers_start, field, value); | ||
| 1549 | } | ||
| 1550 | |||
| 1551 | /// After this the caller must initialize the params list. | ||
| 1552 | pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: TrailerFlags.InitStruct) !*FnProto { | ||
| 1553 | const trailer_flags = TrailerFlags.init(trailers); | ||
| 1554 | const bytes = try allocator.alignedAlloc(u8, @alignOf(FnProto), sizeInBytes( | ||
| 1555 | required.params_len, | ||
| 1556 | trailer_flags, | ||
| 1557 | )); | ||
| 1558 | const fn_proto = @ptrCast(*FnProto, bytes.ptr); | ||
| 1559 | fn_proto.* = .{ | ||
| 1560 | .trailer_flags = trailer_flags, | ||
| 1561 | .fn_token = required.fn_token, | ||
| 1562 | .params_len = required.params_len, | ||
| 1563 | .return_type = required.return_type, | ||
| 1564 | }; | ||
| 1565 | const trailers_start = @alignCast( | ||
| 1566 | @alignOf(ParamDecl), | ||
| 1567 | bytes.ptr + @sizeOf(FnProto) + @sizeOf(ParamDecl) * required.params_len, | ||
| 1568 | ); | ||
| 1569 | trailer_flags.setMany(trailers_start, trailers); | ||
| 1570 | return fn_proto; | ||
| 1571 | } | ||
| 1572 | |||
| 1573 | pub fn destroy(self: *FnProto, allocator: *mem.Allocator) void { | ||
| 1574 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.params_len, self.trailer_flags)]; | ||
| 1575 | allocator.free(bytes); | ||
| 1576 | } | ||
| 1577 | |||
| 1578 | pub fn iterate(self: *const FnProto, index: usize) ?*Node { | ||
| 1579 | var i = index; | ||
| 1580 | |||
| 1581 | if (self.getLibName()) |lib_name| { | ||
| 1582 | if (i < 1) return lib_name; | ||
| 1583 | i -= 1; | ||
| 1584 | } | ||
| 1585 | |||
| 1586 | const params_len: usize = if (self.params_len == 0) | ||
| 1587 | 0 | ||
| 1588 | else switch (self.paramsConst()[self.params_len - 1].param_type) { | ||
| 1589 | .any_type, .type_expr => self.params_len, | ||
| 1590 | }; | ||
| 1591 | if (i < params_len) { | ||
| 1592 | switch (self.paramsConst()[i].param_type) { | ||
| 1593 | .any_type => |n| return n, | ||
| 1594 | .type_expr => |n| return n, | ||
| 1595 | } | ||
| 1596 | } | ||
| 1597 | i -= params_len; | ||
| 1598 | |||
| 1599 | if (self.getAlignExpr()) |align_expr| { | ||
| 1600 | if (i < 1) return align_expr; | ||
| 1601 | i -= 1; | ||
| 1602 | } | ||
| 1603 | |||
| 1604 | if (self.getSectionExpr()) |section_expr| { | ||
| 1605 | if (i < 1) return section_expr; | ||
| 1606 | i -= 1; | ||
| 1607 | } | ||
| 1608 | |||
| 1609 | switch (self.return_type) { | ||
| 1610 | .Explicit, .InferErrorSet => |node| { | ||
| 1611 | if (i < 1) return node; | ||
| 1612 | i -= 1; | ||
| 1613 | }, | ||
| 1614 | .Invalid => {}, | ||
| 1615 | } | ||
| 1616 | |||
| 1617 | if (self.getBodyNode()) |body_node| { | ||
| 1618 | if (i < 1) return body_node; | ||
| 1619 | i -= 1; | ||
| 1620 | } | ||
| 1621 | |||
| 1622 | return null; | ||
| 1623 | } | ||
| 1624 | |||
| 1625 | pub fn firstToken(self: *const FnProto) TokenIndex { | ||
| 1626 | if (self.getVisibToken()) |visib_token| return visib_token; | ||
| 1627 | if (self.getExternExportInlineToken()) |extern_export_inline_token| return extern_export_inline_token; | ||
| 1628 | assert(self.getLibName() == null); | ||
| 1629 | return self.fn_token; | ||
| 1630 | } | ||
| 1631 | |||
| 1632 | pub fn lastToken(self: *const FnProto) TokenIndex { | ||
| 1633 | if (self.getBodyNode()) |body_node| return body_node.lastToken(); | ||
| 1634 | switch (self.return_type) { | ||
| 1635 | .Explicit, .InferErrorSet => |node| return node.lastToken(), | ||
| 1636 | .Invalid => |tok| return tok, | ||
| 1637 | } | ||
| 1638 | } | ||
| 1639 | |||
| 1640 | pub fn params(self: *FnProto) []ParamDecl { | ||
| 1641 | const params_start = @ptrCast([*]u8, self) + @sizeOf(FnProto); | ||
| 1642 | return @ptrCast([*]ParamDecl, params_start)[0..self.params_len]; | ||
| 1643 | } | ||
| 1644 | |||
| 1645 | pub fn paramsConst(self: *const FnProto) []const ParamDecl { | ||
| 1646 | const params_start = @ptrCast([*]const u8, self) + @sizeOf(FnProto); | ||
| 1647 | return @ptrCast([*]const ParamDecl, params_start)[0..self.params_len]; | ||
| 1648 | } | ||
| 1649 | |||
| 1650 | fn sizeInBytes(params_len: NodeIndex, trailer_flags: TrailerFlags) usize { | ||
| 1651 | return @sizeOf(FnProto) + @sizeOf(ParamDecl) * @as(usize, params_len) + trailer_flags.sizeInBytes(); | ||
| 1652 | } | ||
| 1653 | }; | ||
| 1654 | |||
| 1655 | pub const AnyFrameType = struct { | ||
| 1656 | base: Node = Node{ .tag = .AnyFrameType }, | ||
| 1657 | anyframe_token: TokenIndex, | ||
| 1658 | result: ?Result, | ||
| 1659 | |||
| 1660 | pub const Result = struct { | ||
| 1661 | arrow_token: TokenIndex, | ||
| 1662 | return_type: *Node, | ||
| 1663 | }; | ||
| 1664 | |||
| 1665 | pub fn iterate(self: *const AnyFrameType, index: usize) ?*Node { | ||
| 1666 | var i = index; | ||
| 1667 | |||
| 1668 | if (self.result) |result| { | ||
| 1669 | if (i < 1) return result.return_type; | ||
| 1670 | i -= 1; | ||
| 1671 | } | ||
| 1672 | |||
| 1673 | return null; | ||
| 1674 | } | ||
| 1675 | |||
| 1676 | pub fn firstToken(self: *const AnyFrameType) TokenIndex { | ||
| 1677 | return self.anyframe_token; | ||
| 1678 | } | ||
| 1679 | |||
| 1680 | pub fn lastToken(self: *const AnyFrameType) TokenIndex { | ||
| 1681 | if (self.result) |result| return result.return_type.lastToken(); | ||
| 1682 | return self.anyframe_token; | ||
| 1683 | } | ||
| 1684 | }; | ||
| 1685 | |||
| 1686 | /// The statements of the block follow Block directly in memory. | ||
| 1687 | pub const Block = struct { | ||
| 1688 | base: Node = Node{ .tag = .Block }, | ||
| 1689 | statements_len: NodeIndex, | ||
| 1690 | lbrace: TokenIndex, | ||
| 1691 | rbrace: TokenIndex, | ||
| 1692 | |||
| 1693 | /// After this the caller must initialize the statements list. | ||
| 1694 | pub fn alloc(allocator: *mem.Allocator, statements_len: NodeIndex) !*Block { | ||
| 1695 | const bytes = try allocator.alignedAlloc(u8, @alignOf(Block), sizeInBytes(statements_len)); | ||
| 1696 | return @ptrCast(*Block, bytes.ptr); | ||
| 1697 | } | ||
| 1698 | |||
| 1699 | pub fn free(self: *Block, allocator: *mem.Allocator) void { | ||
| 1700 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.statements_len)]; | ||
| 1701 | allocator.free(bytes); | ||
| 1702 | } | ||
| 1703 | |||
| 1704 | pub fn iterate(self: *const Block, index: usize) ?*Node { | ||
| 1705 | var i = index; | ||
| 1706 | |||
| 1707 | if (i < self.statements_len) return self.statementsConst()[i]; | ||
| 1708 | i -= self.statements_len; | ||
| 1709 | |||
| 1710 | return null; | ||
| 1711 | } | ||
| 1712 | |||
| 1713 | pub fn firstToken(self: *const Block) TokenIndex { | ||
| 1714 | return self.lbrace; | ||
| 1715 | } | ||
| 1716 | |||
| 1717 | pub fn lastToken(self: *const Block) TokenIndex { | ||
| 1718 | return self.rbrace; | ||
| 1719 | } | ||
| 1720 | |||
| 1721 | pub fn statements(self: *Block) []*Node { | ||
| 1722 | const decls_start = @ptrCast([*]u8, self) + @sizeOf(Block); | ||
| 1723 | return @ptrCast([*]*Node, decls_start)[0..self.statements_len]; | ||
| 1724 | } | ||
| 1725 | |||
| 1726 | pub fn statementsConst(self: *const Block) []const *Node { | ||
| 1727 | const decls_start = @ptrCast([*]const u8, self) + @sizeOf(Block); | ||
| 1728 | return @ptrCast([*]const *Node, decls_start)[0..self.statements_len]; | ||
| 1729 | } | ||
| 1730 | |||
| 1731 | fn sizeInBytes(statements_len: NodeIndex) usize { | ||
| 1732 | return @sizeOf(Block) + @sizeOf(*Node) * @as(usize, statements_len); | ||
| 1733 | } | ||
| 1734 | }; | ||
| 1735 | |||
| 1736 | /// The statements of the block follow LabeledBlock directly in memory. | ||
| 1737 | pub const LabeledBlock = struct { | ||
| 1738 | base: Node = Node{ .tag = .LabeledBlock }, | ||
| 1739 | statements_len: NodeIndex, | ||
| 1740 | lbrace: TokenIndex, | ||
| 1741 | rbrace: TokenIndex, | ||
| 1742 | label: TokenIndex, | ||
| 1743 | |||
| 1744 | /// After this the caller must initialize the statements list. | ||
| 1745 | pub fn alloc(allocator: *mem.Allocator, statements_len: NodeIndex) !*LabeledBlock { | ||
| 1746 | const bytes = try allocator.alignedAlloc(u8, @alignOf(LabeledBlock), sizeInBytes(statements_len)); | ||
| 1747 | return @ptrCast(*LabeledBlock, bytes.ptr); | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | pub fn free(self: *LabeledBlock, allocator: *mem.Allocator) void { | ||
| 1751 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.statements_len)]; | ||
| 1752 | allocator.free(bytes); | ||
| 1753 | } | ||
| 1754 | |||
| 1755 | pub fn iterate(self: *const LabeledBlock, index: usize) ?*Node { | ||
| 1756 | var i = index; | ||
| 1757 | |||
| 1758 | if (i < self.statements_len) return self.statementsConst()[i]; | ||
| 1759 | i -= self.statements_len; | ||
| 1760 | |||
| 1761 | return null; | ||
| 1762 | } | ||
| 1763 | |||
| 1764 | pub fn firstToken(self: *const LabeledBlock) TokenIndex { | ||
| 1765 | return self.label; | ||
| 1766 | } | ||
| 1767 | |||
| 1768 | pub fn lastToken(self: *const LabeledBlock) TokenIndex { | ||
| 1769 | return self.rbrace; | ||
| 1770 | } | ||
| 1771 | |||
| 1772 | pub fn statements(self: *LabeledBlock) []*Node { | ||
| 1773 | const decls_start = @ptrCast([*]u8, self) + @sizeOf(LabeledBlock); | ||
| 1774 | return @ptrCast([*]*Node, decls_start)[0..self.statements_len]; | ||
| 1775 | } | ||
| 1776 | |||
| 1777 | pub fn statementsConst(self: *const LabeledBlock) []const *Node { | ||
| 1778 | const decls_start = @ptrCast([*]const u8, self) + @sizeOf(LabeledBlock); | ||
| 1779 | return @ptrCast([*]const *Node, decls_start)[0..self.statements_len]; | ||
| 1780 | } | ||
| 1781 | |||
| 1782 | fn sizeInBytes(statements_len: NodeIndex) usize { | ||
| 1783 | return @sizeOf(LabeledBlock) + @sizeOf(*Node) * @as(usize, statements_len); | ||
| 1784 | } | ||
| 1785 | }; | ||
| 1786 | |||
| 1787 | pub const Defer = struct { | ||
| 1788 | base: Node = Node{ .tag = .Defer }, | ||
| 1789 | defer_token: TokenIndex, | ||
| 1790 | payload: ?*Node, | ||
| 1791 | expr: *Node, | ||
| 1792 | |||
| 1793 | pub fn iterate(self: *const Defer, index: usize) ?*Node { | ||
| 1794 | var i = index; | ||
| 1795 | |||
| 1796 | if (i < 1) return self.expr; | ||
| 1797 | i -= 1; | ||
| 1798 | |||
| 1799 | return null; | ||
| 1800 | } | ||
| 1801 | |||
| 1802 | pub fn firstToken(self: *const Defer) TokenIndex { | ||
| 1803 | return self.defer_token; | ||
| 1804 | } | ||
| 1805 | |||
| 1806 | pub fn lastToken(self: *const Defer) TokenIndex { | ||
| 1807 | return self.expr.lastToken(); | ||
| 1808 | } | ||
| 1809 | }; | ||
| 1810 | |||
| 1811 | pub const Comptime = struct { | ||
| 1812 | base: Node = Node{ .tag = .Comptime }, | ||
| 1813 | doc_comments: ?*DocComment, | ||
| 1814 | comptime_token: TokenIndex, | ||
| 1815 | expr: *Node, | ||
| 1816 | |||
| 1817 | pub fn iterate(self: *const Comptime, index: usize) ?*Node { | ||
| 1818 | var i = index; | ||
| 1819 | |||
| 1820 | if (i < 1) return self.expr; | ||
| 1821 | i -= 1; | ||
| 1822 | |||
| 1823 | return null; | ||
| 1824 | } | ||
| 1825 | |||
| 1826 | pub fn firstToken(self: *const Comptime) TokenIndex { | ||
| 1827 | return self.comptime_token; | ||
| 1828 | } | ||
| 1829 | |||
| 1830 | pub fn lastToken(self: *const Comptime) TokenIndex { | ||
| 1831 | return self.expr.lastToken(); | ||
| 1832 | } | ||
| 1833 | }; | ||
| 1834 | |||
| 1835 | pub const Nosuspend = struct { | ||
| 1836 | base: Node = Node{ .tag = .Nosuspend }, | ||
| 1837 | nosuspend_token: TokenIndex, | ||
| 1838 | expr: *Node, | ||
| 1839 | |||
| 1840 | pub fn iterate(self: *const Nosuspend, index: usize) ?*Node { | ||
| 1841 | var i = index; | ||
| 1842 | |||
| 1843 | if (i < 1) return self.expr; | ||
| 1844 | i -= 1; | ||
| 1845 | |||
| 1846 | return null; | ||
| 1847 | } | ||
| 1848 | |||
| 1849 | pub fn firstToken(self: *const Nosuspend) TokenIndex { | ||
| 1850 | return self.nosuspend_token; | ||
| 1851 | } | ||
| 1852 | |||
| 1853 | pub fn lastToken(self: *const Nosuspend) TokenIndex { | ||
| 1854 | return self.expr.lastToken(); | ||
| 1855 | } | ||
| 1856 | }; | ||
| 1857 | |||
| 1858 | pub const Payload = struct { | ||
| 1859 | base: Node = Node{ .tag = .Payload }, | ||
| 1860 | lpipe: TokenIndex, | ||
| 1861 | error_symbol: *Node, | ||
| 1862 | rpipe: TokenIndex, | ||
| 1863 | |||
| 1864 | pub fn iterate(self: *const Payload, index: usize) ?*Node { | ||
| 1865 | var i = index; | ||
| 1866 | |||
| 1867 | if (i < 1) return self.error_symbol; | ||
| 1868 | i -= 1; | ||
| 1869 | |||
| 1870 | return null; | ||
| 1871 | } | ||
| 1872 | |||
| 1873 | pub fn firstToken(self: *const Payload) TokenIndex { | ||
| 1874 | return self.lpipe; | ||
| 1875 | } | ||
| 1876 | |||
| 1877 | pub fn lastToken(self: *const Payload) TokenIndex { | ||
| 1878 | return self.rpipe; | ||
| 1879 | } | ||
| 1880 | }; | ||
| 1881 | |||
| 1882 | pub const PointerPayload = struct { | ||
| 1883 | base: Node = Node{ .tag = .PointerPayload }, | ||
| 1884 | lpipe: TokenIndex, | ||
| 1885 | ptr_token: ?TokenIndex, | ||
| 1886 | value_symbol: *Node, | ||
| 1887 | rpipe: TokenIndex, | ||
| 1888 | |||
| 1889 | pub fn iterate(self: *const PointerPayload, index: usize) ?*Node { | ||
| 1890 | var i = index; | ||
| 1891 | |||
| 1892 | if (i < 1) return self.value_symbol; | ||
| 1893 | i -= 1; | ||
| 1894 | |||
| 1895 | return null; | ||
| 1896 | } | ||
| 1897 | |||
| 1898 | pub fn firstToken(self: *const PointerPayload) TokenIndex { | ||
| 1899 | return self.lpipe; | ||
| 1900 | } | ||
| 1901 | |||
| 1902 | pub fn lastToken(self: *const PointerPayload) TokenIndex { | ||
| 1903 | return self.rpipe; | ||
| 1904 | } | ||
| 1905 | }; | ||
| 1906 | |||
| 1907 | pub const PointerIndexPayload = struct { | ||
| 1908 | base: Node = Node{ .tag = .PointerIndexPayload }, | ||
| 1909 | lpipe: TokenIndex, | ||
| 1910 | ptr_token: ?TokenIndex, | ||
| 1911 | value_symbol: *Node, | ||
| 1912 | index_symbol: ?*Node, | ||
| 1913 | rpipe: TokenIndex, | ||
| 1914 | |||
| 1915 | pub fn iterate(self: *const PointerIndexPayload, index: usize) ?*Node { | ||
| 1916 | var i = index; | ||
| 1917 | |||
| 1918 | if (i < 1) return self.value_symbol; | ||
| 1919 | i -= 1; | ||
| 1920 | |||
| 1921 | if (self.index_symbol) |index_symbol| { | ||
| 1922 | if (i < 1) return index_symbol; | ||
| 1923 | i -= 1; | ||
| 1924 | } | ||
| 1925 | |||
| 1926 | return null; | ||
| 1927 | } | ||
| 1928 | |||
| 1929 | pub fn firstToken(self: *const PointerIndexPayload) TokenIndex { | ||
| 1930 | return self.lpipe; | ||
| 1931 | } | ||
| 1932 | |||
| 1933 | pub fn lastToken(self: *const PointerIndexPayload) TokenIndex { | ||
| 1934 | return self.rpipe; | ||
| 1935 | } | ||
| 1936 | }; | ||
| 1937 | |||
| 1938 | pub const Else = struct { | ||
| 1939 | base: Node = Node{ .tag = .Else }, | ||
| 1940 | else_token: TokenIndex, | ||
| 1941 | payload: ?*Node, | ||
| 1942 | body: *Node, | ||
| 1943 | |||
| 1944 | pub fn iterate(self: *const Else, index: usize) ?*Node { | ||
| 1945 | var i = index; | ||
| 1946 | |||
| 1947 | if (self.payload) |payload| { | ||
| 1948 | if (i < 1) return payload; | ||
| 1949 | i -= 1; | ||
| 1950 | } | ||
| 1951 | |||
| 1952 | if (i < 1) return self.body; | ||
| 1953 | i -= 1; | ||
| 1954 | |||
| 1955 | return null; | ||
| 1956 | } | ||
| 1957 | |||
| 1958 | pub fn firstToken(self: *const Else) TokenIndex { | ||
| 1959 | return self.else_token; | ||
| 1960 | } | ||
| 1961 | |||
| 1962 | pub fn lastToken(self: *const Else) TokenIndex { | ||
| 1963 | return self.body.lastToken(); | ||
| 1964 | } | ||
| 1965 | }; | ||
| 1966 | |||
| 1967 | /// The cases node pointers are found in memory after Switch. | ||
| 1968 | /// They must be SwitchCase or SwitchElse nodes. | ||
| 1969 | pub const Switch = struct { | ||
| 1970 | base: Node = Node{ .tag = .Switch }, | ||
| 1971 | switch_token: TokenIndex, | ||
| 1972 | rbrace: TokenIndex, | ||
| 1973 | cases_len: NodeIndex, | ||
| 1974 | expr: *Node, | ||
| 1975 | |||
| 1976 | /// After this the caller must initialize the fields_and_decls list. | ||
| 1977 | pub fn alloc(allocator: *mem.Allocator, cases_len: NodeIndex) !*Switch { | ||
| 1978 | const bytes = try allocator.alignedAlloc(u8, @alignOf(Switch), sizeInBytes(cases_len)); | ||
| 1979 | return @ptrCast(*Switch, bytes.ptr); | ||
| 1980 | } | ||
| 1981 | |||
| 1982 | pub fn free(self: *Switch, allocator: *mem.Allocator) void { | ||
| 1983 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.cases_len)]; | ||
| 1984 | allocator.free(bytes); | ||
| 1985 | } | ||
| 1986 | |||
| 1987 | pub fn iterate(self: *const Switch, index: usize) ?*Node { | ||
| 1988 | var i = index; | ||
| 1989 | |||
| 1990 | if (i < 1) return self.expr; | ||
| 1991 | i -= 1; | ||
| 1992 | |||
| 1993 | if (i < self.cases_len) return self.casesConst()[i]; | ||
| 1994 | i -= self.cases_len; | ||
| 1995 | |||
| 1996 | return null; | ||
| 1997 | } | ||
| 1998 | |||
| 1999 | pub fn firstToken(self: *const Switch) TokenIndex { | ||
| 2000 | return self.switch_token; | ||
| 2001 | } | ||
| 2002 | |||
| 2003 | pub fn lastToken(self: *const Switch) TokenIndex { | ||
| 2004 | return self.rbrace; | ||
| 2005 | } | ||
| 2006 | |||
| 2007 | pub fn cases(self: *Switch) []*Node { | ||
| 2008 | const decls_start = @ptrCast([*]u8, self) + @sizeOf(Switch); | ||
| 2009 | return @ptrCast([*]*Node, decls_start)[0..self.cases_len]; | ||
| 2010 | } | ||
| 2011 | |||
| 2012 | pub fn casesConst(self: *const Switch) []const *Node { | ||
| 2013 | const decls_start = @ptrCast([*]const u8, self) + @sizeOf(Switch); | ||
| 2014 | return @ptrCast([*]const *Node, decls_start)[0..self.cases_len]; | ||
| 2015 | } | ||
| 2016 | |||
| 2017 | fn sizeInBytes(cases_len: NodeIndex) usize { | ||
| 2018 | return @sizeOf(Switch) + @sizeOf(*Node) * @as(usize, cases_len); | ||
| 2019 | } | ||
| 2020 | }; | ||
| 2021 | |||
| 2022 | /// Items sub-nodes appear in memory directly following SwitchCase. | ||
| 2023 | pub const SwitchCase = struct { | ||
| 2024 | base: Node = Node{ .tag = .SwitchCase }, | ||
| 2025 | arrow_token: TokenIndex, | ||
| 2026 | payload: ?*Node, | ||
| 2027 | expr: *Node, | ||
| 2028 | items_len: NodeIndex, | ||
| 2029 | |||
| 2030 | /// After this the caller must initialize the fields_and_decls list. | ||
| 2031 | pub fn alloc(allocator: *mem.Allocator, items_len: NodeIndex) !*SwitchCase { | ||
| 2032 | const bytes = try allocator.alignedAlloc(u8, @alignOf(SwitchCase), sizeInBytes(items_len)); | ||
| 2033 | return @ptrCast(*SwitchCase, bytes.ptr); | ||
| 2034 | } | ||
| 2035 | |||
| 2036 | pub fn free(self: *SwitchCase, allocator: *mem.Allocator) void { | ||
| 2037 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.items_len)]; | ||
| 2038 | allocator.free(bytes); | ||
| 2039 | } | ||
| 2040 | |||
| 2041 | pub fn iterate(self: *const SwitchCase, index: usize) ?*Node { | ||
| 2042 | var i = index; | ||
| 2043 | |||
| 2044 | if (i < self.items_len) return self.itemsConst()[i]; | ||
| 2045 | i -= self.items_len; | ||
| 2046 | |||
| 2047 | if (self.payload) |payload| { | ||
| 2048 | if (i < 1) return payload; | ||
| 2049 | i -= 1; | ||
| 2050 | } | ||
| 2051 | |||
| 2052 | if (i < 1) return self.expr; | ||
| 2053 | i -= 1; | ||
| 2054 | |||
| 2055 | return null; | ||
| 2056 | } | ||
| 2057 | |||
| 2058 | pub fn firstToken(self: *const SwitchCase) TokenIndex { | ||
| 2059 | return self.itemsConst()[0].firstToken(); | ||
| 2060 | } | ||
| 2061 | |||
| 2062 | pub fn lastToken(self: *const SwitchCase) TokenIndex { | ||
| 2063 | return self.expr.lastToken(); | ||
| 2064 | } | ||
| 2065 | |||
| 2066 | pub fn items(self: *SwitchCase) []*Node { | ||
| 2067 | const decls_start = @ptrCast([*]u8, self) + @sizeOf(SwitchCase); | ||
| 2068 | return @ptrCast([*]*Node, decls_start)[0..self.items_len]; | ||
| 2069 | } | ||
| 2070 | |||
| 2071 | pub fn itemsConst(self: *const SwitchCase) []const *Node { | ||
| 2072 | const decls_start = @ptrCast([*]const u8, self) + @sizeOf(SwitchCase); | ||
| 2073 | return @ptrCast([*]const *Node, decls_start)[0..self.items_len]; | ||
| 2074 | } | ||
| 2075 | |||
| 2076 | fn sizeInBytes(items_len: NodeIndex) usize { | ||
| 2077 | return @sizeOf(SwitchCase) + @sizeOf(*Node) * @as(usize, items_len); | ||
| 2078 | } | ||
| 2079 | }; | ||
| 2080 | |||
| 2081 | pub const SwitchElse = struct { | ||
| 2082 | base: Node = Node{ .tag = .SwitchElse }, | ||
| 2083 | token: TokenIndex, | ||
| 2084 | |||
| 2085 | pub fn iterate(self: *const SwitchElse, index: usize) ?*Node { | ||
| 2086 | return null; | ||
| 2087 | } | ||
| 2088 | |||
| 2089 | pub fn firstToken(self: *const SwitchElse) TokenIndex { | ||
| 2090 | return self.token; | ||
| 2091 | } | ||
| 2092 | |||
| 2093 | pub fn lastToken(self: *const SwitchElse) TokenIndex { | ||
| 2094 | return self.token; | ||
| 2095 | } | ||
| 2096 | }; | ||
| 2097 | |||
| 2098 | pub const While = struct { | ||
| 2099 | base: Node = Node{ .tag = .While }, | ||
| 2100 | label: ?TokenIndex, | ||
| 2101 | inline_token: ?TokenIndex, | ||
| 2102 | while_token: TokenIndex, | ||
| 2103 | condition: *Node, | ||
| 2104 | payload: ?*Node, | ||
| 2105 | continue_expr: ?*Node, | ||
| 2106 | body: *Node, | ||
| 2107 | @"else": ?*Else, | ||
| 2108 | |||
| 2109 | pub fn iterate(self: *const While, index: usize) ?*Node { | ||
| 2110 | var i = index; | ||
| 2111 | |||
| 2112 | if (i < 1) return self.condition; | ||
| 2113 | i -= 1; | ||
| 2114 | |||
| 2115 | if (self.payload) |payload| { | ||
| 2116 | if (i < 1) return payload; | ||
| 2117 | i -= 1; | ||
| 2118 | } | ||
| 2119 | |||
| 2120 | if (self.continue_expr) |continue_expr| { | ||
| 2121 | if (i < 1) return continue_expr; | ||
| 2122 | i -= 1; | ||
| 2123 | } | ||
| 2124 | |||
| 2125 | if (i < 1) return self.body; | ||
| 2126 | i -= 1; | ||
| 2127 | |||
| 2128 | if (self.@"else") |@"else"| { | ||
| 2129 | if (i < 1) return &@"else".base; | ||
| 2130 | i -= 1; | ||
| 2131 | } | ||
| 2132 | |||
| 2133 | return null; | ||
| 2134 | } | ||
| 2135 | |||
| 2136 | pub fn firstToken(self: *const While) TokenIndex { | ||
| 2137 | if (self.label) |label| { | ||
| 2138 | return label; | ||
| 2139 | } | ||
| 2140 | |||
| 2141 | if (self.inline_token) |inline_token| { | ||
| 2142 | return inline_token; | ||
| 2143 | } | ||
| 2144 | |||
| 2145 | return self.while_token; | ||
| 2146 | } | ||
| 2147 | |||
| 2148 | pub fn lastToken(self: *const While) TokenIndex { | ||
| 2149 | if (self.@"else") |@"else"| { | ||
| 2150 | return @"else".body.lastToken(); | ||
| 2151 | } | ||
| 2152 | |||
| 2153 | return self.body.lastToken(); | ||
| 2154 | } | ||
| 2155 | }; | ||
| 2156 | |||
| 2157 | pub const For = struct { | ||
| 2158 | base: Node = Node{ .tag = .For }, | ||
| 2159 | label: ?TokenIndex, | ||
| 2160 | inline_token: ?TokenIndex, | ||
| 2161 | for_token: TokenIndex, | ||
| 2162 | array_expr: *Node, | ||
| 2163 | payload: *Node, | ||
| 2164 | body: *Node, | ||
| 2165 | @"else": ?*Else, | ||
| 2166 | |||
| 2167 | pub fn iterate(self: *const For, index: usize) ?*Node { | ||
| 2168 | var i = index; | ||
| 2169 | |||
| 2170 | if (i < 1) return self.array_expr; | ||
| 2171 | i -= 1; | ||
| 2172 | |||
| 2173 | if (i < 1) return self.payload; | ||
| 2174 | i -= 1; | ||
| 2175 | |||
| 2176 | if (i < 1) return self.body; | ||
| 2177 | i -= 1; | ||
| 2178 | |||
| 2179 | if (self.@"else") |@"else"| { | ||
| 2180 | if (i < 1) return &@"else".base; | ||
| 2181 | i -= 1; | ||
| 2182 | } | ||
| 2183 | |||
| 2184 | return null; | ||
| 2185 | } | ||
| 2186 | |||
| 2187 | pub fn firstToken(self: *const For) TokenIndex { | ||
| 2188 | if (self.label) |label| { | ||
| 2189 | return label; | ||
| 2190 | } | ||
| 2191 | |||
| 2192 | if (self.inline_token) |inline_token| { | ||
| 2193 | return inline_token; | ||
| 2194 | } | ||
| 2195 | |||
| 2196 | return self.for_token; | ||
| 2197 | } | ||
| 2198 | |||
| 2199 | pub fn lastToken(self: *const For) TokenIndex { | ||
| 2200 | if (self.@"else") |@"else"| { | ||
| 2201 | return @"else".body.lastToken(); | ||
| 2202 | } | ||
| 2203 | |||
| 2204 | return self.body.lastToken(); | ||
| 2205 | } | ||
| 2206 | }; | ||
| 2207 | |||
| 2208 | pub const If = struct { | ||
| 2209 | base: Node = Node{ .tag = .If }, | ||
| 2210 | if_token: TokenIndex, | ||
| 2211 | condition: *Node, | ||
| 2212 | payload: ?*Node, | ||
| 2213 | body: *Node, | ||
| 2214 | @"else": ?*Else, | ||
| 2215 | |||
| 2216 | pub fn iterate(self: *const If, index: usize) ?*Node { | ||
| 2217 | var i = index; | ||
| 2218 | |||
| 2219 | if (i < 1) return self.condition; | ||
| 2220 | i -= 1; | ||
| 2221 | |||
| 2222 | if (self.payload) |payload| { | ||
| 2223 | if (i < 1) return payload; | ||
| 2224 | i -= 1; | ||
| 2225 | } | ||
| 2226 | |||
| 2227 | if (i < 1) return self.body; | ||
| 2228 | i -= 1; | ||
| 2229 | |||
| 2230 | if (self.@"else") |@"else"| { | ||
| 2231 | if (i < 1) return &@"else".base; | ||
| 2232 | i -= 1; | ||
| 2233 | } | ||
| 2234 | |||
| 2235 | return null; | ||
| 2236 | } | ||
| 2237 | |||
| 2238 | pub fn firstToken(self: *const If) TokenIndex { | ||
| 2239 | return self.if_token; | ||
| 2240 | } | ||
| 2241 | |||
| 2242 | pub fn lastToken(self: *const If) TokenIndex { | ||
| 2243 | if (self.@"else") |@"else"| { | ||
| 2244 | return @"else".body.lastToken(); | ||
| 2245 | } | ||
| 2246 | |||
| 2247 | return self.body.lastToken(); | ||
| 2248 | } | ||
| 2249 | }; | ||
| 2250 | |||
| 2251 | pub const Catch = struct { | ||
| 2252 | base: Node = Node{ .tag = .Catch }, | ||
| 2253 | op_token: TokenIndex, | ||
| 2254 | lhs: *Node, | ||
| 2255 | rhs: *Node, | ||
| 2256 | payload: ?*Node, | ||
| 2257 | |||
| 2258 | pub fn iterate(self: *const Catch, index: usize) ?*Node { | ||
| 2259 | var i = index; | ||
| 2260 | |||
| 2261 | if (i < 1) return self.lhs; | ||
| 2262 | i -= 1; | ||
| 2263 | |||
| 2264 | if (self.payload) |payload| { | ||
| 2265 | if (i < 1) return payload; | ||
| 2266 | i -= 1; | ||
| 2267 | } | ||
| 2268 | |||
| 2269 | if (i < 1) return self.rhs; | ||
| 2270 | i -= 1; | ||
| 2271 | |||
| 2272 | return null; | ||
| 2273 | } | ||
| 2274 | |||
| 2275 | pub fn firstToken(self: *const Catch) TokenIndex { | ||
| 2276 | return self.lhs.firstToken(); | ||
| 2277 | } | ||
| 2278 | |||
| 2279 | pub fn lastToken(self: *const Catch) TokenIndex { | ||
| 2280 | return self.rhs.lastToken(); | ||
| 2281 | } | ||
| 2282 | }; | ||
| 2283 | |||
| 2284 | pub const SimpleInfixOp = struct { | ||
| 2285 | base: Node, | ||
| 2286 | op_token: TokenIndex, | ||
| 2287 | lhs: *Node, | ||
| 2288 | rhs: *Node, | ||
| 2289 | |||
| 2290 | pub fn iterate(self: *const SimpleInfixOp, index: usize) ?*Node { | ||
| 2291 | var i = index; | ||
| 2292 | |||
| 2293 | if (i < 1) return self.lhs; | ||
| 2294 | i -= 1; | ||
| 2295 | |||
| 2296 | if (i < 1) return self.rhs; | ||
| 2297 | i -= 1; | ||
| 2298 | |||
| 2299 | return null; | ||
| 2300 | } | ||
| 2301 | |||
| 2302 | pub fn firstToken(self: *const SimpleInfixOp) TokenIndex { | ||
| 2303 | return self.lhs.firstToken(); | ||
| 2304 | } | ||
| 2305 | |||
| 2306 | pub fn lastToken(self: *const SimpleInfixOp) TokenIndex { | ||
| 2307 | return self.rhs.lastToken(); | ||
| 2308 | } | ||
| 2309 | }; | ||
| 2310 | |||
| 2311 | pub const SimplePrefixOp = struct { | ||
| 2312 | base: Node, | ||
| 2313 | op_token: TokenIndex, | ||
| 2314 | rhs: *Node, | ||
| 2315 | |||
| 2316 | const Self = @This(); | ||
| 2317 | |||
| 2318 | pub fn iterate(self: *const Self, index: usize) ?*Node { | ||
| 2319 | if (index == 0) return self.rhs; | ||
| 2320 | return null; | ||
| 2321 | } | ||
| 2322 | |||
| 2323 | pub fn firstToken(self: *const Self) TokenIndex { | ||
| 2324 | return self.op_token; | ||
| 2325 | } | ||
| 2326 | |||
| 2327 | pub fn lastToken(self: *const Self) TokenIndex { | ||
| 2328 | return self.rhs.lastToken(); | ||
| 2329 | } | ||
| 2330 | }; | ||
| 2331 | |||
| 2332 | pub const ArrayType = struct { | ||
| 2333 | base: Node = Node{ .tag = .ArrayType }, | ||
| 2334 | op_token: TokenIndex, | ||
| 2335 | rhs: *Node, | ||
| 2336 | len_expr: *Node, | ||
| 2337 | |||
| 2338 | pub fn iterate(self: *const ArrayType, index: usize) ?*Node { | ||
| 2339 | var i = index; | ||
| 2340 | |||
| 2341 | if (i < 1) return self.len_expr; | ||
| 2342 | i -= 1; | ||
| 2343 | |||
| 2344 | if (i < 1) return self.rhs; | ||
| 2345 | i -= 1; | ||
| 2346 | |||
| 2347 | return null; | ||
| 2348 | } | ||
| 2349 | |||
| 2350 | pub fn firstToken(self: *const ArrayType) TokenIndex { | ||
| 2351 | return self.op_token; | ||
| 2352 | } | ||
| 2353 | |||
| 2354 | pub fn lastToken(self: *const ArrayType) TokenIndex { | ||
| 2355 | return self.rhs.lastToken(); | ||
| 2356 | } | ||
| 2357 | }; | ||
| 2358 | |||
| 2359 | pub const ArrayTypeSentinel = struct { | ||
| 2360 | base: Node = Node{ .tag = .ArrayTypeSentinel }, | ||
| 2361 | op_token: TokenIndex, | ||
| 2362 | rhs: *Node, | ||
| 2363 | len_expr: *Node, | ||
| 2364 | sentinel: *Node, | ||
| 2365 | |||
| 2366 | pub fn iterate(self: *const ArrayTypeSentinel, index: usize) ?*Node { | ||
| 2367 | var i = index; | ||
| 2368 | |||
| 2369 | if (i < 1) return self.len_expr; | ||
| 2370 | i -= 1; | ||
| 2371 | |||
| 2372 | if (i < 1) return self.sentinel; | ||
| 2373 | i -= 1; | ||
| 2374 | |||
| 2375 | if (i < 1) return self.rhs; | ||
| 2376 | i -= 1; | ||
| 2377 | |||
| 2378 | return null; | ||
| 2379 | } | ||
| 2380 | |||
| 2381 | pub fn firstToken(self: *const ArrayTypeSentinel) TokenIndex { | ||
| 2382 | return self.op_token; | ||
| 2383 | } | ||
| 2384 | |||
| 2385 | pub fn lastToken(self: *const ArrayTypeSentinel) TokenIndex { | ||
| 2386 | return self.rhs.lastToken(); | ||
| 2387 | } | ||
| 2388 | }; | ||
| 2389 | |||
| 2390 | pub const PtrType = struct { | ||
| 2391 | base: Node = Node{ .tag = .PtrType }, | ||
| 2392 | op_token: TokenIndex, | ||
| 2393 | rhs: *Node, | ||
| 2394 | /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents | ||
| 2395 | /// one of these possibly-null things. Then we have them directly follow the PtrType in memory. | ||
| 2396 | ptr_info: PtrInfo = .{}, | ||
| 2397 | |||
| 2398 | pub fn iterate(self: *const PtrType, index: usize) ?*Node { | ||
| 2399 | var i = index; | ||
| 2400 | |||
| 2401 | if (self.ptr_info.sentinel) |sentinel| { | ||
| 2402 | if (i < 1) return sentinel; | ||
| 2403 | i -= 1; | ||
| 2404 | } | ||
| 2405 | |||
| 2406 | if (self.ptr_info.align_info) |align_info| { | ||
| 2407 | if (i < 1) return align_info.node; | ||
| 2408 | i -= 1; | ||
| 2409 | } | ||
| 2410 | |||
| 2411 | if (i < 1) return self.rhs; | ||
| 2412 | i -= 1; | ||
| 2413 | |||
| 2414 | return null; | ||
| 2415 | } | ||
| 2416 | |||
| 2417 | pub fn firstToken(self: *const PtrType) TokenIndex { | ||
| 2418 | return self.op_token; | ||
| 2419 | } | ||
| 2420 | |||
| 2421 | pub fn lastToken(self: *const PtrType) TokenIndex { | ||
| 2422 | return self.rhs.lastToken(); | ||
| 2423 | } | ||
| 2424 | }; | ||
| 2425 | |||
| 2426 | pub const SliceType = struct { | ||
| 2427 | base: Node = Node{ .tag = .SliceType }, | ||
| 2428 | op_token: TokenIndex, | ||
| 2429 | rhs: *Node, | ||
| 2430 | /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents | ||
| 2431 | /// one of these possibly-null things. Then we have them directly follow the SliceType in memory. | ||
| 2432 | ptr_info: PtrInfo = .{}, | ||
| 2433 | |||
| 2434 | pub fn iterate(self: *const SliceType, index: usize) ?*Node { | ||
| 2435 | var i = index; | ||
| 2436 | |||
| 2437 | if (self.ptr_info.sentinel) |sentinel| { | ||
| 2438 | if (i < 1) return sentinel; | ||
| 2439 | i -= 1; | ||
| 2440 | } | ||
| 2441 | |||
| 2442 | if (self.ptr_info.align_info) |align_info| { | ||
| 2443 | if (i < 1) return align_info.node; | ||
| 2444 | i -= 1; | ||
| 2445 | } | ||
| 2446 | |||
| 2447 | if (i < 1) return self.rhs; | ||
| 2448 | i -= 1; | ||
| 2449 | |||
| 2450 | return null; | ||
| 2451 | } | ||
| 2452 | |||
| 2453 | pub fn firstToken(self: *const SliceType) TokenIndex { | ||
| 2454 | return self.op_token; | ||
| 2455 | } | ||
| 2456 | |||
| 2457 | pub fn lastToken(self: *const SliceType) TokenIndex { | ||
| 2458 | return self.rhs.lastToken(); | ||
| 2459 | } | ||
| 2460 | }; | ||
| 2461 | |||
| 2462 | pub const FieldInitializer = struct { | ||
| 2463 | base: Node = Node{ .tag = .FieldInitializer }, | ||
| 2464 | period_token: TokenIndex, | ||
| 2465 | name_token: TokenIndex, | ||
| 2466 | expr: *Node, | ||
| 2467 | |||
| 2468 | pub fn iterate(self: *const FieldInitializer, index: usize) ?*Node { | ||
| 2469 | var i = index; | ||
| 2470 | |||
| 2471 | if (i < 1) return self.expr; | ||
| 2472 | i -= 1; | ||
| 2473 | |||
| 2474 | return null; | ||
| 2475 | } | ||
| 2476 | |||
| 2477 | pub fn firstToken(self: *const FieldInitializer) TokenIndex { | ||
| 2478 | return self.period_token; | ||
| 2479 | } | ||
| 2480 | |||
| 2481 | pub fn lastToken(self: *const FieldInitializer) TokenIndex { | ||
| 2482 | return self.expr.lastToken(); | ||
| 2483 | } | ||
| 2484 | }; | ||
| 2485 | |||
| 2486 | /// Elements occur directly in memory after ArrayInitializer. | ||
| 2487 | pub const ArrayInitializer = struct { | ||
| 2488 | base: Node = Node{ .tag = .ArrayInitializer }, | ||
| 2489 | rtoken: TokenIndex, | ||
| 2490 | list_len: NodeIndex, | ||
| 2491 | lhs: *Node, | ||
| 2492 | |||
| 2493 | /// After this the caller must initialize the fields_and_decls list. | ||
| 2494 | pub fn alloc(allocator: *mem.Allocator, list_len: NodeIndex) !*ArrayInitializer { | ||
| 2495 | const bytes = try allocator.alignedAlloc(u8, @alignOf(ArrayInitializer), sizeInBytes(list_len)); | ||
| 2496 | return @ptrCast(*ArrayInitializer, bytes.ptr); | ||
| 2497 | } | ||
| 2498 | |||
| 2499 | pub fn free(self: *ArrayInitializer, allocator: *mem.Allocator) void { | ||
| 2500 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.list_len)]; | ||
| 2501 | allocator.free(bytes); | ||
| 2502 | } | ||
| 2503 | |||
| 2504 | pub fn iterate(self: *const ArrayInitializer, index: usize) ?*Node { | ||
| 2505 | var i = index; | ||
| 2506 | |||
| 2507 | if (i < 1) return self.lhs; | ||
| 2508 | i -= 1; | ||
| 2509 | |||
| 2510 | if (i < self.list_len) return self.listConst()[i]; | ||
| 2511 | i -= self.list_len; | ||
| 2512 | |||
| 2513 | return null; | ||
| 2514 | } | ||
| 2515 | |||
| 2516 | pub fn firstToken(self: *const ArrayInitializer) TokenIndex { | ||
| 2517 | return self.lhs.firstToken(); | ||
| 2518 | } | ||
| 2519 | |||
| 2520 | pub fn lastToken(self: *const ArrayInitializer) TokenIndex { | ||
| 2521 | return self.rtoken; | ||
| 2522 | } | ||
| 2523 | |||
| 2524 | pub fn list(self: *ArrayInitializer) []*Node { | ||
| 2525 | const decls_start = @ptrCast([*]u8, self) + @sizeOf(ArrayInitializer); | ||
| 2526 | return @ptrCast([*]*Node, decls_start)[0..self.list_len]; | ||
| 2527 | } | ||
| 2528 | |||
| 2529 | pub fn listConst(self: *const ArrayInitializer) []const *Node { | ||
| 2530 | const decls_start = @ptrCast([*]const u8, self) + @sizeOf(ArrayInitializer); | ||
| 2531 | return @ptrCast([*]const *Node, decls_start)[0..self.list_len]; | ||
| 2532 | } | ||
| 2533 | |||
| 2534 | fn sizeInBytes(list_len: NodeIndex) usize { | ||
| 2535 | return @sizeOf(ArrayInitializer) + @sizeOf(*Node) * @as(usize, list_len); | ||
| 2536 | } | ||
| 2537 | }; | ||
| 2538 | |||
| 2539 | /// Elements occur directly in memory after ArrayInitializerDot. | ||
| 2540 | pub const ArrayInitializerDot = struct { | ||
| 2541 | base: Node = Node{ .tag = .ArrayInitializerDot }, | ||
| 2542 | dot: TokenIndex, | ||
| 2543 | rtoken: TokenIndex, | ||
| 2544 | list_len: NodeIndex, | ||
| 2545 | |||
| 2546 | /// After this the caller must initialize the fields_and_decls list. | ||
| 2547 | pub fn alloc(allocator: *mem.Allocator, list_len: NodeIndex) !*ArrayInitializerDot { | ||
| 2548 | const bytes = try allocator.alignedAlloc(u8, @alignOf(ArrayInitializerDot), sizeInBytes(list_len)); | ||
| 2549 | return @ptrCast(*ArrayInitializerDot, bytes.ptr); | ||
| 2550 | } | ||
| 2551 | |||
| 2552 | pub fn free(self: *ArrayInitializerDot, allocator: *mem.Allocator) void { | ||
| 2553 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.list_len)]; | ||
| 2554 | allocator.free(bytes); | ||
| 2555 | } | ||
| 2556 | |||
| 2557 | pub fn iterate(self: *const ArrayInitializerDot, index: usize) ?*Node { | ||
| 2558 | var i = index; | ||
| 2559 | |||
| 2560 | if (i < self.list_len) return self.listConst()[i]; | ||
| 2561 | i -= self.list_len; | ||
| 2562 | |||
| 2563 | return null; | ||
| 2564 | } | ||
| 2565 | |||
| 2566 | pub fn firstToken(self: *const ArrayInitializerDot) TokenIndex { | ||
| 2567 | return self.dot; | ||
| 2568 | } | ||
| 2569 | |||
| 2570 | pub fn lastToken(self: *const ArrayInitializerDot) TokenIndex { | ||
| 2571 | return self.rtoken; | ||
| 2572 | } | ||
| 2573 | |||
| 2574 | pub fn list(self: *ArrayInitializerDot) []*Node { | ||
| 2575 | const decls_start = @ptrCast([*]u8, self) + @sizeOf(ArrayInitializerDot); | ||
| 2576 | return @ptrCast([*]*Node, decls_start)[0..self.list_len]; | ||
| 2577 | } | ||
| 2578 | |||
| 2579 | pub fn listConst(self: *const ArrayInitializerDot) []const *Node { | ||
| 2580 | const decls_start = @ptrCast([*]const u8, self) + @sizeOf(ArrayInitializerDot); | ||
| 2581 | return @ptrCast([*]const *Node, decls_start)[0..self.list_len]; | ||
| 2582 | } | ||
| 2583 | |||
| 2584 | fn sizeInBytes(list_len: NodeIndex) usize { | ||
| 2585 | return @sizeOf(ArrayInitializerDot) + @sizeOf(*Node) * @as(usize, list_len); | ||
| 2586 | } | ||
| 2587 | }; | ||
| 2588 | |||
| 2589 | /// Elements occur directly in memory after StructInitializer. | ||
| 2590 | pub const StructInitializer = struct { | ||
| 2591 | base: Node = Node{ .tag = .StructInitializer }, | ||
| 2592 | rtoken: TokenIndex, | ||
| 2593 | list_len: NodeIndex, | ||
| 2594 | lhs: *Node, | ||
| 2595 | |||
| 2596 | /// After this the caller must initialize the fields_and_decls list. | ||
| 2597 | pub fn alloc(allocator: *mem.Allocator, list_len: NodeIndex) !*StructInitializer { | ||
| 2598 | const bytes = try allocator.alignedAlloc(u8, @alignOf(StructInitializer), sizeInBytes(list_len)); | ||
| 2599 | return @ptrCast(*StructInitializer, bytes.ptr); | ||
| 2600 | } | ||
| 2601 | |||
| 2602 | pub fn free(self: *StructInitializer, allocator: *mem.Allocator) void { | ||
| 2603 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.list_len)]; | ||
| 2604 | allocator.free(bytes); | ||
| 2605 | } | ||
| 2606 | |||
| 2607 | pub fn iterate(self: *const StructInitializer, index: usize) ?*Node { | ||
| 2608 | var i = index; | ||
| 2609 | |||
| 2610 | if (i < 1) return self.lhs; | ||
| 2611 | i -= 1; | ||
| 2612 | |||
| 2613 | if (i < self.list_len) return self.listConst()[i]; | ||
| 2614 | i -= self.list_len; | ||
| 2615 | |||
| 2616 | return null; | ||
| 2617 | } | ||
| 2618 | |||
| 2619 | pub fn firstToken(self: *const StructInitializer) TokenIndex { | ||
| 2620 | return self.lhs.firstToken(); | ||
| 2621 | } | ||
| 2622 | |||
| 2623 | pub fn lastToken(self: *const StructInitializer) TokenIndex { | ||
| 2624 | return self.rtoken; | ||
| 2625 | } | ||
| 2626 | |||
| 2627 | pub fn list(self: *StructInitializer) []*Node { | ||
| 2628 | const decls_start = @ptrCast([*]u8, self) + @sizeOf(StructInitializer); | ||
| 2629 | return @ptrCast([*]*Node, decls_start)[0..self.list_len]; | ||
| 2630 | } | ||
| 2631 | |||
| 2632 | pub fn listConst(self: *const StructInitializer) []const *Node { | ||
| 2633 | const decls_start = @ptrCast([*]const u8, self) + @sizeOf(StructInitializer); | ||
| 2634 | return @ptrCast([*]const *Node, decls_start)[0..self.list_len]; | ||
| 2635 | } | ||
| 2636 | |||
| 2637 | fn sizeInBytes(list_len: NodeIndex) usize { | ||
| 2638 | return @sizeOf(StructInitializer) + @sizeOf(*Node) * @as(usize, list_len); | ||
| 2639 | } | ||
| 2640 | }; | ||
| 2641 | |||
| 2642 | /// Elements occur directly in memory after StructInitializerDot. | ||
| 2643 | pub const StructInitializerDot = struct { | ||
| 2644 | base: Node = Node{ .tag = .StructInitializerDot }, | ||
| 2645 | dot: TokenIndex, | ||
| 2646 | rtoken: TokenIndex, | ||
| 2647 | list_len: NodeIndex, | ||
| 2648 | |||
| 2649 | /// After this the caller must initialize the fields_and_decls list. | ||
| 2650 | pub fn alloc(allocator: *mem.Allocator, list_len: NodeIndex) !*StructInitializerDot { | ||
| 2651 | const bytes = try allocator.alignedAlloc(u8, @alignOf(StructInitializerDot), sizeInBytes(list_len)); | ||
| 2652 | return @ptrCast(*StructInitializerDot, bytes.ptr); | ||
| 2653 | } | ||
| 2654 | |||
| 2655 | pub fn free(self: *StructInitializerDot, allocator: *mem.Allocator) void { | ||
| 2656 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.list_len)]; | ||
| 2657 | allocator.free(bytes); | ||
| 2658 | } | ||
| 2659 | |||
| 2660 | pub fn iterate(self: *const StructInitializerDot, index: usize) ?*Node { | ||
| 2661 | var i = index; | ||
| 2662 | |||
| 2663 | if (i < self.list_len) return self.listConst()[i]; | ||
| 2664 | i -= self.list_len; | ||
| 2665 | |||
| 2666 | return null; | ||
| 2667 | } | ||
| 2668 | |||
| 2669 | pub fn firstToken(self: *const StructInitializerDot) TokenIndex { | ||
| 2670 | return self.dot; | ||
| 2671 | } | ||
| 2672 | |||
| 2673 | pub fn lastToken(self: *const StructInitializerDot) TokenIndex { | ||
| 2674 | return self.rtoken; | ||
| 2675 | } | ||
| 2676 | |||
| 2677 | pub fn list(self: *StructInitializerDot) []*Node { | ||
| 2678 | const decls_start = @ptrCast([*]u8, self) + @sizeOf(StructInitializerDot); | ||
| 2679 | return @ptrCast([*]*Node, decls_start)[0..self.list_len]; | ||
| 2680 | } | ||
| 2681 | |||
| 2682 | pub fn listConst(self: *const StructInitializerDot) []const *Node { | ||
| 2683 | const decls_start = @ptrCast([*]const u8, self) + @sizeOf(StructInitializerDot); | ||
| 2684 | return @ptrCast([*]const *Node, decls_start)[0..self.list_len]; | ||
| 2685 | } | ||
| 2686 | |||
| 2687 | fn sizeInBytes(list_len: NodeIndex) usize { | ||
| 2688 | return @sizeOf(StructInitializerDot) + @sizeOf(*Node) * @as(usize, list_len); | ||
| 2689 | } | ||
| 2690 | }; | ||
| 2691 | |||
| 2692 | /// Parameter nodes directly follow Call in memory. | ||
| 2693 | pub const Call = struct { | ||
| 2694 | base: Node = Node{ .tag = .Call }, | ||
| 2695 | rtoken: TokenIndex, | ||
| 2696 | lhs: *Node, | ||
| 2697 | params_len: NodeIndex, | ||
| 2698 | async_token: ?TokenIndex, | ||
| 2699 | |||
| 2700 | /// After this the caller must initialize the fields_and_decls list. | ||
| 2701 | pub fn alloc(allocator: *mem.Allocator, params_len: NodeIndex) !*Call { | ||
| 2702 | const bytes = try allocator.alignedAlloc(u8, @alignOf(Call), sizeInBytes(params_len)); | ||
| 2703 | return @ptrCast(*Call, bytes.ptr); | ||
| 2704 | } | ||
| 2705 | |||
| 2706 | pub fn free(self: *Call, allocator: *mem.Allocator) void { | ||
| 2707 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.params_len)]; | ||
| 2708 | allocator.free(bytes); | ||
| 2709 | } | ||
| 2710 | |||
| 2711 | pub fn iterate(self: *const Call, index: usize) ?*Node { | ||
| 2712 | var i = index; | ||
| 2713 | |||
| 2714 | if (i < 1) return self.lhs; | ||
| 2715 | i -= 1; | ||
| 2716 | |||
| 2717 | if (i < self.params_len) return self.paramsConst()[i]; | ||
| 2718 | i -= self.params_len; | ||
| 2719 | |||
| 2720 | return null; | ||
| 2721 | } | ||
| 2722 | |||
| 2723 | pub fn firstToken(self: *const Call) TokenIndex { | ||
| 2724 | if (self.async_token) |async_token| return async_token; | ||
| 2725 | return self.lhs.firstToken(); | ||
| 2726 | } | ||
| 2727 | |||
| 2728 | pub fn lastToken(self: *const Call) TokenIndex { | ||
| 2729 | return self.rtoken; | ||
| 2730 | } | ||
| 2731 | |||
| 2732 | pub fn params(self: *Call) []*Node { | ||
| 2733 | const decls_start = @ptrCast([*]u8, self) + @sizeOf(Call); | ||
| 2734 | return @ptrCast([*]*Node, decls_start)[0..self.params_len]; | ||
| 2735 | } | ||
| 2736 | |||
| 2737 | pub fn paramsConst(self: *const Call) []const *Node { | ||
| 2738 | const decls_start = @ptrCast([*]const u8, self) + @sizeOf(Call); | ||
| 2739 | return @ptrCast([*]const *Node, decls_start)[0..self.params_len]; | ||
| 2740 | } | ||
| 2741 | |||
| 2742 | fn sizeInBytes(params_len: NodeIndex) usize { | ||
| 2743 | return @sizeOf(Call) + @sizeOf(*Node) * @as(usize, params_len); | ||
| 2744 | } | ||
| 2745 | }; | ||
| 2746 | |||
| 2747 | pub const ArrayAccess = struct { | ||
| 2748 | base: Node = Node{ .tag = .ArrayAccess }, | ||
| 2749 | rtoken: TokenIndex, | ||
| 2750 | lhs: *Node, | ||
| 2751 | index_expr: *Node, | ||
| 2752 | |||
| 2753 | pub fn iterate(self: *const ArrayAccess, index: usize) ?*Node { | ||
| 2754 | var i = index; | ||
| 2755 | |||
| 2756 | if (i < 1) return self.lhs; | ||
| 2757 | i -= 1; | ||
| 2758 | |||
| 2759 | if (i < 1) return self.index_expr; | ||
| 2760 | i -= 1; | 1834 | i -= 1; |
| 2761 | 1835 | switch (token_tags[i]) { | |
| 2762 | return null; | 1836 | .keyword_extern, .keyword_export => result.extern_export_token = i, |
| 2763 | } | 1837 | .keyword_pub => result.visib_token = i, |
| 2764 | 1838 | .string_literal => result.lib_name = i, | |
| 2765 | pub fn firstToken(self: *const ArrayAccess) TokenIndex { | 1839 | else => break, |
| 2766 | return self.lhs.firstToken(); | 1840 | } |
| 2767 | } | 1841 | } |
| 2768 | 1842 | const after_fn_token = info.fn_token + 1; | |
| 2769 | pub fn lastToken(self: *const ArrayAccess) TokenIndex { | 1843 | if (token_tags[after_fn_token] == .identifier) { |
| 2770 | return self.rtoken; | 1844 | result.name_token = after_fn_token; |
| 1845 | result.lparen = after_fn_token + 1; | ||
| 1846 | } else { | ||
| 1847 | result.lparen = after_fn_token; | ||
| 2771 | } | 1848 | } |
| 2772 | }; | 1849 | assert(token_tags[result.lparen] == .l_paren); |
| 2773 | 1850 | ||
| 2774 | pub const SimpleSuffixOp = struct { | 1851 | return result; |
| 2775 | base: Node, | 1852 | } |
| 2776 | rtoken: TokenIndex, | ||
| 2777 | lhs: *Node, | ||
| 2778 | |||
| 2779 | pub fn iterate(self: *const SimpleSuffixOp, index: usize) ?*Node { | ||
| 2780 | var i = index; | ||
| 2781 | 1853 | ||
| 2782 | if (i < 1) return self.lhs; | 1854 | fn fullStructInit(tree: Tree, info: full.StructInit.Ast) full.StructInit { |
| 2783 | i -= 1; | 1855 | const token_tags = tree.tokens.items(.tag); |
| 1856 | var result: full.StructInit = .{ | ||
| 1857 | .ast = info, | ||
| 1858 | }; | ||
| 1859 | return result; | ||
| 1860 | } | ||
| 2784 | 1861 | ||
| 2785 | return null; | 1862 | fn fullPtrType(tree: Tree, info: full.PtrType.Ast) full.PtrType { |
| 1863 | const token_tags = tree.tokens.items(.tag); | ||
| 1864 | // TODO: looks like stage1 isn't quite smart enough to handle enum | ||
| 1865 | // literals in some places here | ||
| 1866 | const Size = std.builtin.TypeInfo.Pointer.Size; | ||
| 1867 | const size: Size = switch (token_tags[info.main_token]) { | ||
| 1868 | .asterisk, | ||
| 1869 | .asterisk_asterisk, | ||
| 1870 | => switch (token_tags[info.main_token + 1]) { | ||
| 1871 | .r_bracket, .colon => .Many, | ||
| 1872 | .identifier => if (token_tags[info.main_token - 1] == .l_bracket) Size.C else .One, | ||
| 1873 | else => .One, | ||
| 1874 | }, | ||
| 1875 | .l_bracket => Size.Slice, | ||
| 1876 | else => unreachable, | ||
| 1877 | }; | ||
| 1878 | var result: full.PtrType = .{ | ||
| 1879 | .size = size, | ||
| 1880 | .allowzero_token = null, | ||
| 1881 | .const_token = null, | ||
| 1882 | .volatile_token = null, | ||
| 1883 | .ast = info, | ||
| 1884 | }; | ||
| 1885 | // We need to be careful that we don't iterate over any sub-expressions | ||
| 1886 | // here while looking for modifiers as that could result in false | ||
| 1887 | // positives. Therefore, start after a sentinel if there is one and | ||
| 1888 | // skip over any align node and bit range nodes. | ||
| 1889 | var i = if (info.sentinel != 0) tree.lastToken(info.sentinel) + 1 else info.main_token; | ||
| 1890 | const end = tree.firstToken(info.child_type); | ||
| 1891 | while (i < end) : (i += 1) { | ||
| 1892 | switch (token_tags[i]) { | ||
| 1893 | .keyword_allowzero => result.allowzero_token = i, | ||
| 1894 | .keyword_const => result.const_token = i, | ||
| 1895 | .keyword_volatile => result.volatile_token = i, | ||
| 1896 | .keyword_align => { | ||
| 1897 | assert(info.align_node != 0); | ||
| 1898 | if (info.bit_range_end != 0) { | ||
| 1899 | assert(info.bit_range_start != 0); | ||
| 1900 | i = tree.lastToken(info.bit_range_end) + 1; | ||
| 1901 | } else { | ||
| 1902 | i = tree.lastToken(info.align_node) + 1; | ||
| 1903 | } | ||
| 1904 | }, | ||
| 1905 | else => {}, | ||
| 1906 | } | ||
| 2786 | } | 1907 | } |
| 1908 | return result; | ||
| 1909 | } | ||
| 2787 | 1910 | ||
| 2788 | pub fn firstToken(self: *const SimpleSuffixOp) TokenIndex { | 1911 | fn fullContainerDecl(tree: Tree, info: full.ContainerDecl.Ast) full.ContainerDecl { |
| 2789 | return self.lhs.firstToken(); | 1912 | const token_tags = tree.tokens.items(.tag); |
| 1913 | var result: full.ContainerDecl = .{ | ||
| 1914 | .ast = info, | ||
| 1915 | .layout_token = null, | ||
| 1916 | }; | ||
| 1917 | switch (token_tags[info.main_token - 1]) { | ||
| 1918 | .keyword_extern, .keyword_packed => result.layout_token = info.main_token - 1, | ||
| 1919 | else => {}, | ||
| 2790 | } | 1920 | } |
| 1921 | return result; | ||
| 1922 | } | ||
| 2791 | 1923 | ||
| 2792 | pub fn lastToken(self: *const SimpleSuffixOp) TokenIndex { | 1924 | fn fullSwitchCase(tree: Tree, info: full.SwitchCase.Ast) full.SwitchCase { |
| 2793 | return self.rtoken; | 1925 | const token_tags = tree.tokens.items(.tag); |
| 1926 | var result: full.SwitchCase = .{ | ||
| 1927 | .ast = info, | ||
| 1928 | .payload_token = null, | ||
| 1929 | }; | ||
| 1930 | if (token_tags[info.arrow_token + 1] == .pipe) { | ||
| 1931 | result.payload_token = info.arrow_token + 2; | ||
| 2794 | } | 1932 | } |
| 2795 | }; | 1933 | return result; |
| 2796 | 1934 | } | |
| 2797 | pub const Slice = struct { | ||
| 2798 | base: Node = Node{ .tag = .Slice }, | ||
| 2799 | rtoken: TokenIndex, | ||
| 2800 | lhs: *Node, | ||
| 2801 | start: *Node, | ||
| 2802 | end: ?*Node, | ||
| 2803 | sentinel: ?*Node, | ||
| 2804 | |||
| 2805 | pub fn iterate(self: *const Slice, index: usize) ?*Node { | ||
| 2806 | var i = index; | ||
| 2807 | |||
| 2808 | if (i < 1) return self.lhs; | ||
| 2809 | i -= 1; | ||
| 2810 | |||
| 2811 | if (i < 1) return self.start; | ||
| 2812 | i -= 1; | ||
| 2813 | 1935 | ||
| 2814 | if (self.end) |end| { | 1936 | fn fullAsm(tree: Tree, info: full.Asm.Ast) full.Asm { |
| 2815 | if (i < 1) return end; | 1937 | const token_tags = tree.tokens.items(.tag); |
| 2816 | i -= 1; | 1938 | const node_tags = tree.nodes.items(.tag); |
| 1939 | var result: full.Asm = .{ | ||
| 1940 | .ast = info, | ||
| 1941 | .volatile_token = null, | ||
| 1942 | .inputs = &.{}, | ||
| 1943 | .outputs = &.{}, | ||
| 1944 | .first_clobber = null, | ||
| 1945 | }; | ||
| 1946 | if (token_tags[info.asm_token + 1] == .keyword_volatile) { | ||
| 1947 | result.volatile_token = info.asm_token + 1; | ||
| 1948 | } | ||
| 1949 | const outputs_end: usize = for (info.items) |item, i| { | ||
| 1950 | switch (node_tags[item]) { | ||
| 1951 | .asm_output => continue, | ||
| 1952 | else => break i, | ||
| 1953 | } | ||
| 1954 | } else info.items.len; | ||
| 1955 | |||
| 1956 | result.outputs = info.items[0..outputs_end]; | ||
| 1957 | result.inputs = info.items[outputs_end..]; | ||
| 1958 | |||
| 1959 | if (info.items.len == 0) { | ||
| 1960 | // asm ("foo" ::: "a", "b"); | ||
| 1961 | const template_token = tree.lastToken(info.template); | ||
| 1962 | if (token_tags[template_token + 1] == .colon and | ||
| 1963 | token_tags[template_token + 2] == .colon and | ||
| 1964 | token_tags[template_token + 3] == .colon and | ||
| 1965 | token_tags[template_token + 4] == .string_literal) | ||
| 1966 | { | ||
| 1967 | result.first_clobber = template_token + 4; | ||
| 1968 | } | ||
| 1969 | } else if (result.inputs.len != 0) { | ||
| 1970 | // asm ("foo" :: [_] "" (y) : "a", "b"); | ||
| 1971 | const last_input = result.inputs[result.inputs.len - 1]; | ||
| 1972 | const rparen = tree.lastToken(last_input); | ||
| 1973 | if (token_tags[rparen + 1] == .colon and | ||
| 1974 | token_tags[rparen + 2] == .string_literal) | ||
| 1975 | { | ||
| 1976 | result.first_clobber = rparen + 2; | ||
| 2817 | } | 1977 | } |
| 2818 | if (self.sentinel) |sentinel| { | 1978 | } else { |
| 2819 | if (i < 1) return sentinel; | 1979 | // asm ("foo" : [_] "" (x) :: "a", "b"); |
| 2820 | i -= 1; | 1980 | const last_output = result.outputs[result.outputs.len - 1]; |
| 1981 | const rparen = tree.lastToken(last_output); | ||
| 1982 | if (token_tags[rparen + 1] == .colon and | ||
| 1983 | token_tags[rparen + 2] == .colon and | ||
| 1984 | token_tags[rparen + 3] == .string_literal) | ||
| 1985 | { | ||
| 1986 | result.first_clobber = rparen + 3; | ||
| 2821 | } | 1987 | } |
| 2822 | |||
| 2823 | return null; | ||
| 2824 | } | ||
| 2825 | |||
| 2826 | pub fn firstToken(self: *const Slice) TokenIndex { | ||
| 2827 | return self.lhs.firstToken(); | ||
| 2828 | } | ||
| 2829 | |||
| 2830 | pub fn lastToken(self: *const Slice) TokenIndex { | ||
| 2831 | return self.rtoken; | ||
| 2832 | } | ||
| 2833 | }; | ||
| 2834 | |||
| 2835 | pub const GroupedExpression = struct { | ||
| 2836 | base: Node = Node{ .tag = .GroupedExpression }, | ||
| 2837 | lparen: TokenIndex, | ||
| 2838 | expr: *Node, | ||
| 2839 | rparen: TokenIndex, | ||
| 2840 | |||
| 2841 | pub fn iterate(self: *const GroupedExpression, index: usize) ?*Node { | ||
| 2842 | var i = index; | ||
| 2843 | |||
| 2844 | if (i < 1) return self.expr; | ||
| 2845 | i -= 1; | ||
| 2846 | |||
| 2847 | return null; | ||
| 2848 | } | ||
| 2849 | |||
| 2850 | pub fn firstToken(self: *const GroupedExpression) TokenIndex { | ||
| 2851 | return self.lparen; | ||
| 2852 | } | ||
| 2853 | |||
| 2854 | pub fn lastToken(self: *const GroupedExpression) TokenIndex { | ||
| 2855 | return self.rparen; | ||
| 2856 | } | 1988 | } |
| 2857 | }; | ||
| 2858 | 1989 | ||
| 2859 | /// Trailed in memory by possibly many things, with each optional thing | 1990 | return result; |
| 2860 | /// determined by a bit in `trailer_flags`. | 1991 | } |
| 2861 | /// Can be: return, break, continue | ||
| 2862 | pub const ControlFlowExpression = struct { | ||
| 2863 | base: Node, | ||
| 2864 | trailer_flags: TrailerFlags, | ||
| 2865 | ltoken: TokenIndex, | ||
| 2866 | |||
| 2867 | pub const TrailerFlags = std.meta.TrailerFlags(struct { | ||
| 2868 | rhs: *Node, | ||
| 2869 | label: TokenIndex, | ||
| 2870 | }); | ||
| 2871 | 1992 | ||
| 2872 | pub const RequiredFields = struct { | 1993 | fn fullWhile(tree: Tree, info: full.While.Ast) full.While { |
| 2873 | tag: Tag, | 1994 | const token_tags = tree.tokens.items(.tag); |
| 2874 | ltoken: TokenIndex, | 1995 | var result: full.While = .{ |
| 1996 | .ast = info, | ||
| 1997 | .inline_token = null, | ||
| 1998 | .label_token = null, | ||
| 1999 | .payload_token = null, | ||
| 2000 | .else_token = undefined, | ||
| 2001 | .error_token = null, | ||
| 2875 | }; | 2002 | }; |
| 2876 | 2003 | var tok_i = info.while_token - 1; | |
| 2877 | pub fn getRHS(self: *const ControlFlowExpression) ?*Node { | 2004 | if (token_tags[tok_i] == .keyword_inline) { |
| 2878 | return self.getTrailer(.rhs); | 2005 | result.inline_token = tok_i; |
| 2879 | } | 2006 | tok_i -= 1; |
| 2880 | |||
| 2881 | pub fn setRHS(self: *ControlFlowExpression, value: *Node) void { | ||
| 2882 | self.setTrailer(.rhs, value); | ||
| 2883 | } | ||
| 2884 | |||
| 2885 | pub fn getLabel(self: *const ControlFlowExpression) ?TokenIndex { | ||
| 2886 | return self.getTrailer(.label); | ||
| 2887 | } | ||
| 2888 | |||
| 2889 | pub fn setLabel(self: *ControlFlowExpression, value: TokenIndex) void { | ||
| 2890 | self.setTrailer(.label, value); | ||
| 2891 | } | ||
| 2892 | |||
| 2893 | fn getTrailer(self: *const ControlFlowExpression, comptime field: TrailerFlags.FieldEnum) ?TrailerFlags.Field(field) { | ||
| 2894 | const trailers_start = @ptrCast([*]const u8, self) + @sizeOf(ControlFlowExpression); | ||
| 2895 | return self.trailer_flags.get(trailers_start, field); | ||
| 2896 | } | ||
| 2897 | |||
| 2898 | fn setTrailer(self: *ControlFlowExpression, comptime field: TrailerFlags.FieldEnum, value: TrailerFlags.Field(field)) void { | ||
| 2899 | const trailers_start = @ptrCast([*]u8, self) + @sizeOf(ControlFlowExpression); | ||
| 2900 | self.trailer_flags.set(trailers_start, field, value); | ||
| 2901 | } | 2007 | } |
| 2902 | 2008 | if (token_tags[tok_i] == .colon and | |
| 2903 | pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: TrailerFlags.InitStruct) !*ControlFlowExpression { | 2009 | token_tags[tok_i - 1] == .identifier) |
| 2904 | const trailer_flags = TrailerFlags.init(trailers); | 2010 | { |
| 2905 | const bytes = try allocator.alignedAlloc(u8, @alignOf(ControlFlowExpression), sizeInBytes(trailer_flags)); | 2011 | result.label_token = tok_i - 1; |
| 2906 | const ctrl_flow_expr = @ptrCast(*ControlFlowExpression, bytes.ptr); | ||
| 2907 | ctrl_flow_expr.* = .{ | ||
| 2908 | .base = .{ .tag = required.tag }, | ||
| 2909 | .trailer_flags = trailer_flags, | ||
| 2910 | .ltoken = required.ltoken, | ||
| 2911 | }; | ||
| 2912 | const trailers_start = bytes.ptr + @sizeOf(ControlFlowExpression); | ||
| 2913 | trailer_flags.setMany(trailers_start, trailers); | ||
| 2914 | return ctrl_flow_expr; | ||
| 2915 | } | 2012 | } |
| 2916 | 2013 | const last_cond_token = tree.lastToken(info.cond_expr); | |
| 2917 | pub fn destroy(self: *ControlFlowExpression, allocator: *mem.Allocator) void { | 2014 | if (token_tags[last_cond_token + 2] == .pipe) { |
| 2918 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.trailer_flags)]; | 2015 | result.payload_token = last_cond_token + 3; |
| 2919 | allocator.free(bytes); | ||
| 2920 | } | 2016 | } |
| 2921 | 2017 | if (info.else_expr != 0) { | |
| 2922 | pub fn iterate(self: *const ControlFlowExpression, index: usize) ?*Node { | 2018 | // then_expr else |x| |
| 2923 | var i = index; | 2019 | // ^ ^ |
| 2924 | 2020 | result.else_token = tree.lastToken(info.then_expr) + 1; | |
| 2925 | if (self.getRHS()) |rhs| { | 2021 | if (token_tags[result.else_token + 1] == .pipe) { |
| 2926 | if (i < 1) return rhs; | 2022 | result.error_token = result.else_token + 2; |
| 2927 | i -= 1; | ||
| 2928 | } | 2023 | } |
| 2929 | |||
| 2930 | return null; | ||
| 2931 | } | 2024 | } |
| 2025 | return result; | ||
| 2026 | } | ||
| 2932 | 2027 | ||
| 2933 | pub fn firstToken(self: *const ControlFlowExpression) TokenIndex { | 2028 | fn fullCall(tree: Tree, info: full.Call.Ast) full.Call { |
| 2934 | return self.ltoken; | 2029 | const token_tags = tree.tokens.items(.tag); |
| 2030 | var result: full.Call = .{ | ||
| 2031 | .ast = info, | ||
| 2032 | .async_token = null, | ||
| 2033 | }; | ||
| 2034 | const maybe_async_token = tree.firstToken(info.fn_expr) - 1; | ||
| 2035 | if (token_tags[maybe_async_token] == .keyword_async) { | ||
| 2036 | result.async_token = maybe_async_token; | ||
| 2935 | } | 2037 | } |
| 2038 | return result; | ||
| 2039 | } | ||
| 2040 | }; | ||
| 2936 | 2041 | ||
| 2937 | pub fn lastToken(self: *const ControlFlowExpression) TokenIndex { | 2042 | /// Fully assembled AST node information. |
| 2938 | if (self.getRHS()) |rhs| { | 2043 | pub const full = struct { |
| 2939 | return rhs.lastToken(); | 2044 | pub const VarDecl = struct { |
| 2940 | } | 2045 | visib_token: ?TokenIndex, |
| 2941 | 2046 | extern_export_token: ?TokenIndex, | |
| 2942 | if (self.getLabel()) |label| { | 2047 | lib_name: ?TokenIndex, |
| 2943 | return label; | 2048 | threadlocal_token: ?TokenIndex, |
| 2944 | } | 2049 | comptime_token: ?TokenIndex, |
| 2945 | 2050 | ast: Ast, | |
| 2946 | return self.ltoken; | ||
| 2947 | } | ||
| 2948 | 2051 | ||
| 2949 | fn sizeInBytes(trailer_flags: TrailerFlags) usize { | 2052 | pub const Ast = struct { |
| 2950 | return @sizeOf(ControlFlowExpression) + trailer_flags.sizeInBytes(); | 2053 | mut_token: TokenIndex, |
| 2951 | } | 2054 | type_node: Node.Index, |
| 2055 | align_node: Node.Index, | ||
| 2056 | section_node: Node.Index, | ||
| 2057 | init_node: Node.Index, | ||
| 2058 | }; | ||
| 2952 | }; | 2059 | }; |
| 2953 | 2060 | ||
| 2954 | pub const Suspend = struct { | 2061 | pub const If = struct { |
| 2955 | base: Node = Node{ .tag = .Suspend }, | 2062 | /// Points to the first token after the `|`. Will either be an identifier or |
| 2956 | suspend_token: TokenIndex, | 2063 | /// a `*` (with an identifier immediately after it). |
| 2957 | body: ?*Node, | 2064 | payload_token: ?TokenIndex, |
| 2958 | 2065 | /// Points to the identifier after the `|`. | |
| 2959 | pub fn iterate(self: *const Suspend, index: usize) ?*Node { | 2066 | error_token: ?TokenIndex, |
| 2960 | var i = index; | 2067 | /// Populated only if else_expr != 0. |
| 2961 | 2068 | else_token: TokenIndex, | |
| 2962 | if (self.body) |body| { | 2069 | ast: Ast, |
| 2963 | if (i < 1) return body; | ||
| 2964 | i -= 1; | ||
| 2965 | } | ||
| 2966 | |||
| 2967 | return null; | ||
| 2968 | } | ||
| 2969 | |||
| 2970 | pub fn firstToken(self: *const Suspend) TokenIndex { | ||
| 2971 | return self.suspend_token; | ||
| 2972 | } | ||
| 2973 | |||
| 2974 | pub fn lastToken(self: *const Suspend) TokenIndex { | ||
| 2975 | if (self.body) |body| { | ||
| 2976 | return body.lastToken(); | ||
| 2977 | } | ||
| 2978 | 2070 | ||
| 2979 | return self.suspend_token; | 2071 | pub const Ast = struct { |
| 2980 | } | 2072 | if_token: TokenIndex, |
| 2073 | cond_expr: Node.Index, | ||
| 2074 | then_expr: Node.Index, | ||
| 2075 | else_expr: Node.Index, | ||
| 2076 | }; | ||
| 2981 | }; | 2077 | }; |
| 2982 | 2078 | ||
| 2983 | pub const EnumLiteral = struct { | 2079 | pub const While = struct { |
| 2984 | base: Node = Node{ .tag = .EnumLiteral }, | 2080 | ast: Ast, |
| 2985 | dot: TokenIndex, | 2081 | inline_token: ?TokenIndex, |
| 2986 | name: TokenIndex, | 2082 | label_token: ?TokenIndex, |
| 2987 | 2083 | payload_token: ?TokenIndex, | |
| 2988 | pub fn iterate(self: *const EnumLiteral, index: usize) ?*Node { | 2084 | error_token: ?TokenIndex, |
| 2989 | return null; | 2085 | /// Populated only if else_expr != 0. |
| 2990 | } | 2086 | else_token: TokenIndex, |
| 2991 | |||
| 2992 | pub fn firstToken(self: *const EnumLiteral) TokenIndex { | ||
| 2993 | return self.dot; | ||
| 2994 | } | ||
| 2995 | 2087 | ||
| 2996 | pub fn lastToken(self: *const EnumLiteral) TokenIndex { | 2088 | pub const Ast = struct { |
| 2997 | return self.name; | 2089 | while_token: TokenIndex, |
| 2998 | } | 2090 | cond_expr: Node.Index, |
| 2091 | cont_expr: Node.Index, | ||
| 2092 | then_expr: Node.Index, | ||
| 2093 | else_expr: Node.Index, | ||
| 2094 | }; | ||
| 2999 | }; | 2095 | }; |
| 3000 | 2096 | ||
| 3001 | /// Parameters are in memory following BuiltinCall. | 2097 | pub const ContainerField = struct { |
| 3002 | pub const BuiltinCall = struct { | 2098 | comptime_token: ?TokenIndex, |
| 3003 | base: Node = Node{ .tag = .BuiltinCall }, | 2099 | ast: Ast, |
| 3004 | params_len: NodeIndex, | ||
| 3005 | builtin_token: TokenIndex, | ||
| 3006 | rparen_token: TokenIndex, | ||
| 3007 | |||
| 3008 | /// After this the caller must initialize the fields_and_decls list. | ||
| 3009 | pub fn alloc(allocator: *mem.Allocator, params_len: NodeIndex) !*BuiltinCall { | ||
| 3010 | const bytes = try allocator.alignedAlloc(u8, @alignOf(BuiltinCall), sizeInBytes(params_len)); | ||
| 3011 | return @ptrCast(*BuiltinCall, bytes.ptr); | ||
| 3012 | } | ||
| 3013 | |||
| 3014 | pub fn free(self: *BuiltinCall, allocator: *mem.Allocator) void { | ||
| 3015 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.params_len)]; | ||
| 3016 | allocator.free(bytes); | ||
| 3017 | } | ||
| 3018 | 2100 | ||
| 3019 | pub fn iterate(self: *const BuiltinCall, index: usize) ?*Node { | 2101 | pub const Ast = struct { |
| 3020 | var i = index; | 2102 | name_token: TokenIndex, |
| 2103 | type_expr: Node.Index, | ||
| 2104 | value_expr: Node.Index, | ||
| 2105 | align_expr: Node.Index, | ||
| 2106 | }; | ||
| 2107 | }; | ||
| 3021 | 2108 | ||
| 3022 | if (i < self.params_len) return self.paramsConst()[i]; | 2109 | pub const FnProto = struct { |
| 3023 | i -= self.params_len; | 2110 | visib_token: ?TokenIndex, |
| 2111 | extern_export_token: ?TokenIndex, | ||
| 2112 | lib_name: ?TokenIndex, | ||
| 2113 | name_token: ?TokenIndex, | ||
| 2114 | lparen: TokenIndex, | ||
| 2115 | ast: Ast, | ||
| 3024 | 2116 | ||
| 3025 | return null; | 2117 | pub const Ast = struct { |
| 3026 | } | 2118 | fn_token: TokenIndex, |
| 2119 | return_type: Node.Index, | ||
| 2120 | params: []const Node.Index, | ||
| 2121 | align_expr: Node.Index, | ||
| 2122 | section_expr: Node.Index, | ||
| 2123 | callconv_expr: Node.Index, | ||
| 2124 | }; | ||
| 3027 | 2125 | ||
| 3028 | pub fn firstToken(self: *const BuiltinCall) TokenIndex { | 2126 | pub const Param = struct { |
| 3029 | return self.builtin_token; | 2127 | first_doc_comment: ?TokenIndex, |
| 3030 | } | 2128 | name_token: ?TokenIndex, |
| 2129 | comptime_noalias: ?TokenIndex, | ||
| 2130 | anytype_ellipsis3: ?TokenIndex, | ||
| 2131 | type_expr: Node.Index, | ||
| 2132 | }; | ||
| 3031 | 2133 | ||
| 3032 | pub fn lastToken(self: *const BuiltinCall) TokenIndex { | 2134 | /// Abstracts over the fact that anytype and ... are not included |
| 3033 | return self.rparen_token; | 2135 | /// in the params slice, since they are simple identifiers and |
| 3034 | } | 2136 | /// not sub-expressions. |
| 2137 | pub const Iterator = struct { | ||
| 2138 | tree: *const Tree, | ||
| 2139 | fn_proto: *const FnProto, | ||
| 2140 | param_i: usize, | ||
| 2141 | tok_i: TokenIndex, | ||
| 2142 | tok_flag: bool, | ||
| 2143 | |||
| 2144 | pub fn next(it: *Iterator) ?Param { | ||
| 2145 | const token_tags = it.tree.tokens.items(.tag); | ||
| 2146 | while (true) { | ||
| 2147 | var first_doc_comment: ?TokenIndex = null; | ||
| 2148 | var comptime_noalias: ?TokenIndex = null; | ||
| 2149 | var name_token: ?TokenIndex = null; | ||
| 2150 | if (!it.tok_flag) { | ||
| 2151 | if (it.param_i >= it.fn_proto.ast.params.len) { | ||
| 2152 | return null; | ||
| 2153 | } | ||
| 2154 | const param_type = it.fn_proto.ast.params[it.param_i]; | ||
| 2155 | var tok_i = it.tree.firstToken(param_type) - 1; | ||
| 2156 | while (true) : (tok_i -= 1) switch (token_tags[tok_i]) { | ||
| 2157 | .colon => continue, | ||
| 2158 | .identifier => name_token = tok_i, | ||
| 2159 | .doc_comment => first_doc_comment = tok_i, | ||
| 2160 | .keyword_comptime, .keyword_noalias => comptime_noalias = tok_i, | ||
| 2161 | else => break, | ||
| 2162 | }; | ||
| 2163 | it.param_i += 1; | ||
| 2164 | it.tok_i = it.tree.lastToken(param_type) + 1; | ||
| 2165 | it.tok_flag = true; | ||
| 2166 | return Param{ | ||
| 2167 | .first_doc_comment = first_doc_comment, | ||
| 2168 | .comptime_noalias = comptime_noalias, | ||
| 2169 | .name_token = name_token, | ||
| 2170 | .anytype_ellipsis3 = null, | ||
| 2171 | .type_expr = param_type, | ||
| 2172 | }; | ||
| 2173 | } | ||
| 2174 | // Look for anytype and ... params afterwards. | ||
| 2175 | if (token_tags[it.tok_i] == .comma) { | ||
| 2176 | it.tok_i += 1; | ||
| 2177 | } else { | ||
| 2178 | return null; | ||
| 2179 | } | ||
| 2180 | if (token_tags[it.tok_i] == .doc_comment) { | ||
| 2181 | first_doc_comment = it.tok_i; | ||
| 2182 | while (token_tags[it.tok_i] == .doc_comment) { | ||
| 2183 | it.tok_i += 1; | ||
| 2184 | } | ||
| 2185 | } | ||
| 2186 | switch (token_tags[it.tok_i]) { | ||
| 2187 | .ellipsis3 => { | ||
| 2188 | it.tok_flag = false; // Next iteration should return null. | ||
| 2189 | return Param{ | ||
| 2190 | .first_doc_comment = first_doc_comment, | ||
| 2191 | .comptime_noalias = null, | ||
| 2192 | .name_token = null, | ||
| 2193 | .anytype_ellipsis3 = it.tok_i, | ||
| 2194 | .type_expr = 0, | ||
| 2195 | }; | ||
| 2196 | }, | ||
| 2197 | .keyword_noalias, .keyword_comptime => { | ||
| 2198 | comptime_noalias = it.tok_i; | ||
| 2199 | it.tok_i += 1; | ||
| 2200 | }, | ||
| 2201 | else => {}, | ||
| 2202 | } | ||
| 2203 | if (token_tags[it.tok_i] == .identifier and | ||
| 2204 | token_tags[it.tok_i + 1] == .colon) | ||
| 2205 | { | ||
| 2206 | name_token = it.tok_i; | ||
| 2207 | it.tok_i += 2; | ||
| 2208 | } | ||
| 2209 | if (token_tags[it.tok_i] == .keyword_anytype) { | ||
| 2210 | it.tok_i += 1; | ||
| 2211 | return Param{ | ||
| 2212 | .first_doc_comment = first_doc_comment, | ||
| 2213 | .comptime_noalias = comptime_noalias, | ||
| 2214 | .name_token = name_token, | ||
| 2215 | .anytype_ellipsis3 = it.tok_i - 1, | ||
| 2216 | .type_expr = 0, | ||
| 2217 | }; | ||
| 2218 | } | ||
| 2219 | it.tok_flag = false; | ||
| 2220 | } | ||
| 2221 | } | ||
| 2222 | }; | ||
| 3035 | 2223 | ||
| 3036 | pub fn params(self: *BuiltinCall) []*Node { | 2224 | pub fn iterate(fn_proto: FnProto, tree: Tree) Iterator { |
| 3037 | const decls_start = @ptrCast([*]u8, self) + @sizeOf(BuiltinCall); | 2225 | return .{ |
| 3038 | return @ptrCast([*]*Node, decls_start)[0..self.params_len]; | 2226 | .tree = &tree, |
| 2227 | .fn_proto = &fn_proto, | ||
| 2228 | .param_i = 0, | ||
| 2229 | .tok_i = undefined, | ||
| 2230 | .tok_flag = false, | ||
| 2231 | }; | ||
| 3039 | } | 2232 | } |
| 2233 | }; | ||
| 3040 | 2234 | ||
| 3041 | pub fn paramsConst(self: *const BuiltinCall) []const *Node { | 2235 | pub const StructInit = struct { |
| 3042 | const decls_start = @ptrCast([*]const u8, self) + @sizeOf(BuiltinCall); | 2236 | ast: Ast, |
| 3043 | return @ptrCast([*]const *Node, decls_start)[0..self.params_len]; | ||
| 3044 | } | ||
| 3045 | 2237 | ||
| 3046 | fn sizeInBytes(params_len: NodeIndex) usize { | 2238 | pub const Ast = struct { |
| 3047 | return @sizeOf(BuiltinCall) + @sizeOf(*Node) * @as(usize, params_len); | 2239 | lbrace: TokenIndex, |
| 3048 | } | 2240 | fields: []const Node.Index, |
| 2241 | type_expr: Node.Index, | ||
| 2242 | }; | ||
| 3049 | }; | 2243 | }; |
| 3050 | 2244 | ||
| 3051 | /// The string literal tokens appear directly in memory after MultilineStringLiteral. | 2245 | pub const ArrayInit = struct { |
| 3052 | pub const MultilineStringLiteral = struct { | 2246 | ast: Ast, |
| 3053 | base: Node = Node{ .tag = .MultilineStringLiteral }, | ||
| 3054 | lines_len: TokenIndex, | ||
| 3055 | 2247 | ||
| 3056 | /// After this the caller must initialize the lines list. | 2248 | pub const Ast = struct { |
| 3057 | pub fn alloc(allocator: *mem.Allocator, lines_len: NodeIndex) !*MultilineStringLiteral { | 2249 | lbrace: TokenIndex, |
| 3058 | const bytes = try allocator.alignedAlloc(u8, @alignOf(MultilineStringLiteral), sizeInBytes(lines_len)); | 2250 | elements: []const Node.Index, |
| 3059 | return @ptrCast(*MultilineStringLiteral, bytes.ptr); | 2251 | type_expr: Node.Index, |
| 3060 | } | 2252 | }; |
| 2253 | }; | ||
| 3061 | 2254 | ||
| 3062 | pub fn free(self: *MultilineStringLiteral, allocator: *mem.Allocator) void { | 2255 | pub const ArrayType = struct { |
| 3063 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.lines_len)]; | 2256 | ast: Ast, |
| 3064 | allocator.free(bytes); | ||
| 3065 | } | ||
| 3066 | 2257 | ||
| 3067 | pub fn iterate(self: *const MultilineStringLiteral, index: usize) ?*Node { | 2258 | pub const Ast = struct { |
| 3068 | return null; | 2259 | lbracket: TokenIndex, |
| 3069 | } | 2260 | elem_count: Node.Index, |
| 2261 | sentinel: ?Node.Index, | ||
| 2262 | elem_type: Node.Index, | ||
| 2263 | }; | ||
| 2264 | }; | ||
| 3070 | 2265 | ||
| 3071 | pub fn firstToken(self: *const MultilineStringLiteral) TokenIndex { | 2266 | pub const PtrType = struct { |
| 3072 | return self.linesConst()[0]; | 2267 | size: std.builtin.TypeInfo.Pointer.Size, |
| 3073 | } | 2268 | allowzero_token: ?TokenIndex, |
| 2269 | const_token: ?TokenIndex, | ||
| 2270 | volatile_token: ?TokenIndex, | ||
| 2271 | ast: Ast, | ||
| 2272 | |||
| 2273 | pub const Ast = struct { | ||
| 2274 | main_token: TokenIndex, | ||
| 2275 | align_node: Node.Index, | ||
| 2276 | sentinel: Node.Index, | ||
| 2277 | bit_range_start: Node.Index, | ||
| 2278 | bit_range_end: Node.Index, | ||
| 2279 | child_type: Node.Index, | ||
| 2280 | }; | ||
| 2281 | }; | ||
| 3074 | 2282 | ||
| 3075 | pub fn lastToken(self: *const MultilineStringLiteral) TokenIndex { | 2283 | pub const Slice = struct { |
| 3076 | return self.linesConst()[self.lines_len - 1]; | 2284 | ast: Ast, |
| 3077 | } | ||
| 3078 | 2285 | ||
| 3079 | pub fn lines(self: *MultilineStringLiteral) []TokenIndex { | 2286 | pub const Ast = struct { |
| 3080 | const decls_start = @ptrCast([*]u8, self) + @sizeOf(MultilineStringLiteral); | 2287 | sliced: Node.Index, |
| 3081 | return @ptrCast([*]TokenIndex, decls_start)[0..self.lines_len]; | 2288 | lbracket: TokenIndex, |
| 3082 | } | 2289 | start: Node.Index, |
| 2290 | end: Node.Index, | ||
| 2291 | sentinel: Node.Index, | ||
| 2292 | }; | ||
| 2293 | }; | ||
| 3083 | 2294 | ||
| 3084 | pub fn linesConst(self: *const MultilineStringLiteral) []const TokenIndex { | 2295 | pub const ContainerDecl = struct { |
| 3085 | const decls_start = @ptrCast([*]const u8, self) + @sizeOf(MultilineStringLiteral); | 2296 | layout_token: ?TokenIndex, |
| 3086 | return @ptrCast([*]const TokenIndex, decls_start)[0..self.lines_len]; | 2297 | ast: Ast, |
| 3087 | } | 2298 | |
| 2299 | pub const Ast = struct { | ||
| 2300 | main_token: TokenIndex, | ||
| 2301 | /// Populated when main_token is Keyword_union. | ||
| 2302 | enum_token: ?TokenIndex, | ||
| 2303 | members: []const Node.Index, | ||
| 2304 | arg: Node.Index, | ||
| 2305 | }; | ||
| 2306 | }; | ||
| 3088 | 2307 | ||
| 3089 | fn sizeInBytes(lines_len: NodeIndex) usize { | 2308 | pub const SwitchCase = struct { |
| 3090 | return @sizeOf(MultilineStringLiteral) + @sizeOf(TokenIndex) * @as(usize, lines_len); | 2309 | /// Points to the first token after the `|`. Will either be an identifier or |
| 3091 | } | 2310 | /// a `*` (with an identifier immediately after it). |
| 2311 | payload_token: ?TokenIndex, | ||
| 2312 | ast: Ast, | ||
| 2313 | |||
| 2314 | pub const Ast = struct { | ||
| 2315 | /// If empty, this is an else case | ||
| 2316 | values: []const Node.Index, | ||
| 2317 | arrow_token: TokenIndex, | ||
| 2318 | target_expr: Node.Index, | ||
| 2319 | }; | ||
| 3092 | }; | 2320 | }; |
| 3093 | 2321 | ||
| 3094 | pub const Asm = struct { | 2322 | pub const Asm = struct { |
| 3095 | base: Node = Node{ .tag = .Asm }, | 2323 | ast: Ast, |
| 3096 | asm_token: TokenIndex, | ||
| 3097 | rparen: TokenIndex, | ||
| 3098 | volatile_token: ?TokenIndex, | 2324 | volatile_token: ?TokenIndex, |
| 3099 | template: *Node, | 2325 | first_clobber: ?TokenIndex, |
| 3100 | outputs: []Output, | 2326 | outputs: []const Node.Index, |
| 3101 | inputs: []Input, | 2327 | inputs: []const Node.Index, |
| 3102 | /// A clobber node must be a StringLiteral or MultilineStringLiteral. | 2328 | |
| 3103 | clobbers: []*Node, | 2329 | pub const Ast = struct { |
| 3104 | 2330 | asm_token: TokenIndex, | |
| 3105 | pub const Output = struct { | 2331 | template: Node.Index, |
| 3106 | lbracket: TokenIndex, | 2332 | items: []const Node.Index, |
| 3107 | symbolic_name: *Node, | ||
| 3108 | constraint: *Node, | ||
| 3109 | kind: Kind, | ||
| 3110 | rparen: TokenIndex, | 2333 | rparen: TokenIndex, |
| 3111 | |||
| 3112 | pub const Kind = union(enum) { | ||
| 3113 | Variable: *OneToken, | ||
| 3114 | Return: *Node, | ||
| 3115 | }; | ||
| 3116 | |||
| 3117 | pub fn iterate(self: *const Output, index: usize) ?*Node { | ||
| 3118 | var i = index; | ||
| 3119 | |||
| 3120 | if (i < 1) return self.symbolic_name; | ||
| 3121 | i -= 1; | ||
| 3122 | |||
| 3123 | if (i < 1) return self.constraint; | ||
| 3124 | i -= 1; | ||
| 3125 | |||
| 3126 | switch (self.kind) { | ||
| 3127 | .Variable => |variable_name| { | ||
| 3128 | if (i < 1) return &variable_name.base; | ||
| 3129 | i -= 1; | ||
| 3130 | }, | ||
| 3131 | .Return => |return_type| { | ||
| 3132 | if (i < 1) return return_type; | ||
| 3133 | i -= 1; | ||
| 3134 | }, | ||
| 3135 | } | ||
| 3136 | |||
| 3137 | return null; | ||
| 3138 | } | ||
| 3139 | |||
| 3140 | pub fn firstToken(self: *const Output) TokenIndex { | ||
| 3141 | return self.lbracket; | ||
| 3142 | } | ||
| 3143 | |||
| 3144 | pub fn lastToken(self: *const Output) TokenIndex { | ||
| 3145 | return self.rparen; | ||
| 3146 | } | ||
| 3147 | }; | 2334 | }; |
| 2335 | }; | ||
| 3148 | 2336 | ||
| 3149 | pub const Input = struct { | 2337 | pub const Call = struct { |
| 3150 | lbracket: TokenIndex, | 2338 | ast: Ast, |
| 3151 | symbolic_name: *Node, | 2339 | async_token: ?TokenIndex, |
| 3152 | constraint: *Node, | ||
| 3153 | expr: *Node, | ||
| 3154 | rparen: TokenIndex, | ||
| 3155 | |||
| 3156 | pub fn iterate(self: *const Input, index: usize) ?*Node { | ||
| 3157 | var i = index; | ||
| 3158 | |||
| 3159 | if (i < 1) return self.symbolic_name; | ||
| 3160 | i -= 1; | ||
| 3161 | 2340 | ||
| 3162 | if (i < 1) return self.constraint; | 2341 | pub const Ast = struct { |
| 3163 | i -= 1; | 2342 | lparen: TokenIndex, |
| 2343 | fn_expr: Node.Index, | ||
| 2344 | params: []const Node.Index, | ||
| 2345 | }; | ||
| 2346 | }; | ||
| 2347 | }; | ||
| 3164 | 2348 | ||
| 3165 | if (i < 1) return self.expr; | 2349 | pub const Error = struct { |
| 3166 | i -= 1; | 2350 | tag: Tag, |
| 2351 | token: TokenIndex, | ||
| 2352 | extra: union { | ||
| 2353 | none: void, | ||
| 2354 | expected_tag: Token.Tag, | ||
| 2355 | } = .{ .none = {} }, | ||
| 3167 | 2356 | ||
| 3168 | return null; | 2357 | pub const Tag = enum { |
| 3169 | } | 2358 | asterisk_after_ptr_deref, |
| 2359 | decl_between_fields, | ||
| 2360 | expected_block, | ||
| 2361 | expected_block_or_assignment, | ||
| 2362 | expected_block_or_expr, | ||
| 2363 | expected_block_or_field, | ||
| 2364 | expected_container_members, | ||
| 2365 | expected_expr, | ||
| 2366 | expected_expr_or_assignment, | ||
| 2367 | expected_fn, | ||
| 2368 | expected_inlinable, | ||
| 2369 | expected_labelable, | ||
| 2370 | expected_param_list, | ||
| 2371 | expected_prefix_expr, | ||
| 2372 | expected_primary_type_expr, | ||
| 2373 | expected_pub_item, | ||
| 2374 | expected_return_type, | ||
| 2375 | expected_semi_or_else, | ||
| 2376 | expected_semi_or_lbrace, | ||
| 2377 | expected_statement, | ||
| 2378 | expected_string_literal, | ||
| 2379 | expected_suffix_op, | ||
| 2380 | expected_type_expr, | ||
| 2381 | expected_var_decl, | ||
| 2382 | expected_var_decl_or_fn, | ||
| 2383 | expected_loop_payload, | ||
| 2384 | expected_container, | ||
| 2385 | extra_align_qualifier, | ||
| 2386 | extra_allowzero_qualifier, | ||
| 2387 | extra_const_qualifier, | ||
| 2388 | extra_volatile_qualifier, | ||
| 2389 | invalid_align, | ||
| 2390 | invalid_and, | ||
| 2391 | invalid_bit_range, | ||
| 2392 | invalid_token, | ||
| 2393 | same_line_doc_comment, | ||
| 2394 | unattached_doc_comment, | ||
| 2395 | |||
| 2396 | /// `expected_tag` is populated. | ||
| 2397 | expected_token, | ||
| 2398 | }; | ||
| 2399 | }; | ||
| 3170 | 2400 | ||
| 3171 | pub fn firstToken(self: *const Input) TokenIndex { | 2401 | pub const Node = struct { |
| 3172 | return self.lbracket; | 2402 | tag: Tag, |
| 3173 | } | 2403 | main_token: TokenIndex, |
| 2404 | data: Data, | ||
| 3174 | 2405 | ||
| 3175 | pub fn lastToken(self: *const Input) TokenIndex { | 2406 | pub const Index = u32; |
| 3176 | return self.rparen; | ||
| 3177 | } | ||
| 3178 | }; | ||
| 3179 | 2407 | ||
| 3180 | pub fn iterate(self: *const Asm, index: usize) ?*Node { | 2408 | comptime { |
| 3181 | var i = index; | 2409 | // Goal is to keep this under one byte for efficiency. |
| 2410 | assert(@sizeOf(Tag) == 1); | ||
| 2411 | } | ||
| 3182 | 2412 | ||
| 3183 | if (i < self.outputs.len * 3) switch (i % 3) { | 2413 | /// Note: The FooComma/FooSemicolon variants exist to ease the implementation of |
| 3184 | 0 => return self.outputs[i / 3].symbolic_name, | 2414 | /// Tree.lastToken() |
| 3185 | 1 => return self.outputs[i / 3].constraint, | 2415 | pub const Tag = enum { |
| 3186 | 2 => switch (self.outputs[i / 3].kind) { | 2416 | /// sub_list[lhs...rhs] |
| 3187 | .Variable => |variable_name| return &variable_name.base, | 2417 | root, |
| 3188 | .Return => |return_type| return return_type, | 2418 | /// `usingnamespace lhs;`. rhs unused. main_token is `usingnamespace`. |
| 3189 | }, | 2419 | @"usingnamespace", |
| 3190 | else => unreachable, | 2420 | /// lhs is test name token (must be string literal), if any. |
| 3191 | }; | 2421 | /// rhs is the body node. |
| 3192 | i -= self.outputs.len * 3; | 2422 | test_decl, |
| 2423 | /// lhs is the index into extra_data. | ||
| 2424 | /// rhs is the initialization expression, if any. | ||
| 2425 | /// main_token is `var` or `const`. | ||
| 2426 | global_var_decl, | ||
| 2427 | /// `var a: x align(y) = rhs` | ||
| 2428 | /// lhs is the index into extra_data. | ||
| 2429 | /// main_token is `var` or `const`. | ||
| 2430 | local_var_decl, | ||
| 2431 | /// `var a: lhs = rhs`. lhs and rhs may be unused. | ||
| 2432 | /// Can be local or global. | ||
| 2433 | /// main_token is `var` or `const`. | ||
| 2434 | simple_var_decl, | ||
| 2435 | /// `var a align(lhs) = rhs`. lhs and rhs may be unused. | ||
| 2436 | /// Can be local or global. | ||
| 2437 | /// main_token is `var` or `const`. | ||
| 2438 | aligned_var_decl, | ||
| 2439 | /// lhs is the identifier token payload if any, | ||
| 2440 | /// rhs is the deferred expression. | ||
| 2441 | @"errdefer", | ||
| 2442 | /// lhs is unused. | ||
| 2443 | /// rhs is the deferred expression. | ||
| 2444 | @"defer", | ||
| 2445 | /// lhs catch rhs | ||
| 2446 | /// lhs catch |err| rhs | ||
| 2447 | /// main_token is the `catch` keyword. | ||
| 2448 | /// payload is determined by looking at the next token after the `catch` keyword. | ||
| 2449 | @"catch", | ||
| 2450 | /// `lhs.a`. main_token is the dot. rhs is the identifier token index. | ||
| 2451 | field_access, | ||
| 2452 | /// `lhs.?`. main_token is the dot. rhs is the `?` token index. | ||
| 2453 | unwrap_optional, | ||
| 2454 | /// `lhs == rhs`. main_token is op. | ||
| 2455 | equal_equal, | ||
| 2456 | /// `lhs != rhs`. main_token is op. | ||
| 2457 | bang_equal, | ||
| 2458 | /// `lhs < rhs`. main_token is op. | ||
| 2459 | less_than, | ||
| 2460 | /// `lhs > rhs`. main_token is op. | ||
| 2461 | greater_than, | ||
| 2462 | /// `lhs <= rhs`. main_token is op. | ||
| 2463 | less_or_equal, | ||
| 2464 | /// `lhs >= rhs`. main_token is op. | ||
| 2465 | greater_or_equal, | ||
| 2466 | /// `lhs *= rhs`. main_token is op. | ||
| 2467 | assign_mul, | ||
| 2468 | /// `lhs /= rhs`. main_token is op. | ||
| 2469 | assign_div, | ||
| 2470 | /// `lhs *= rhs`. main_token is op. | ||
| 2471 | assign_mod, | ||
| 2472 | /// `lhs += rhs`. main_token is op. | ||
| 2473 | assign_add, | ||
| 2474 | /// `lhs -= rhs`. main_token is op. | ||
| 2475 | assign_sub, | ||
| 2476 | /// `lhs <<= rhs`. main_token is op. | ||
| 2477 | assign_bit_shift_left, | ||
| 2478 | /// `lhs >>= rhs`. main_token is op. | ||
| 2479 | assign_bit_shift_right, | ||
| 2480 | /// `lhs &= rhs`. main_token is op. | ||
| 2481 | assign_bit_and, | ||
| 2482 | /// `lhs ^= rhs`. main_token is op. | ||
| 2483 | assign_bit_xor, | ||
| 2484 | /// `lhs |= rhs`. main_token is op. | ||
| 2485 | assign_bit_or, | ||
| 2486 | /// `lhs *%= rhs`. main_token is op. | ||
| 2487 | assign_mul_wrap, | ||
| 2488 | /// `lhs +%= rhs`. main_token is op. | ||
| 2489 | assign_add_wrap, | ||
| 2490 | /// `lhs -%= rhs`. main_token is op. | ||
| 2491 | assign_sub_wrap, | ||
| 2492 | /// `lhs = rhs`. main_token is op. | ||
| 2493 | assign, | ||
| 2494 | /// `lhs || rhs`. main_token is the `||`. | ||
| 2495 | merge_error_sets, | ||
| 2496 | /// `lhs * rhs`. main_token is the `*`. | ||
| 2497 | mul, | ||
| 2498 | /// `lhs / rhs`. main_token is the `/`. | ||
| 2499 | div, | ||
| 2500 | /// `lhs % rhs`. main_token is the `%`. | ||
| 2501 | mod, | ||
| 2502 | /// `lhs ** rhs`. main_token is the `**`. | ||
| 2503 | array_mult, | ||
| 2504 | /// `lhs *% rhs`. main_token is the `*%`. | ||
| 2505 | mul_wrap, | ||
| 2506 | /// `lhs + rhs`. main_token is the `+`. | ||
| 2507 | add, | ||
| 2508 | /// `lhs - rhs`. main_token is the `-`. | ||
| 2509 | sub, | ||
| 2510 | /// `lhs ++ rhs`. main_token is the `++`. | ||
| 2511 | array_cat, | ||
| 2512 | /// `lhs +% rhs`. main_token is the `+%`. | ||
| 2513 | add_wrap, | ||
| 2514 | /// `lhs -% rhs`. main_token is the `-%`. | ||
| 2515 | sub_wrap, | ||
| 2516 | /// `lhs << rhs`. main_token is the `<<`. | ||
| 2517 | bit_shift_left, | ||
| 2518 | /// `lhs >> rhs`. main_token is the `>>`. | ||
| 2519 | bit_shift_right, | ||
| 2520 | /// `lhs & rhs`. main_token is the `&`. | ||
| 2521 | bit_and, | ||
| 2522 | /// `lhs ^ rhs`. main_token is the `^`. | ||
| 2523 | bit_xor, | ||
| 2524 | /// `lhs | rhs`. main_token is the `|`. | ||
| 2525 | bit_or, | ||
| 2526 | /// `lhs orelse rhs`. main_token is the `orelse`. | ||
| 2527 | @"orelse", | ||
| 2528 | /// `lhs and rhs`. main_token is the `and`. | ||
| 2529 | bool_and, | ||
| 2530 | /// `lhs or rhs`. main_token is the `or`. | ||
| 2531 | bool_or, | ||
| 2532 | /// `op lhs`. rhs unused. main_token is op. | ||
| 2533 | bool_not, | ||
| 2534 | /// `op lhs`. rhs unused. main_token is op. | ||
| 2535 | negation, | ||
| 2536 | /// `op lhs`. rhs unused. main_token is op. | ||
| 2537 | bit_not, | ||
| 2538 | /// `op lhs`. rhs unused. main_token is op. | ||
| 2539 | negation_wrap, | ||
| 2540 | /// `op lhs`. rhs unused. main_token is op. | ||
| 2541 | address_of, | ||
| 2542 | /// `op lhs`. rhs unused. main_token is op. | ||
| 2543 | @"try", | ||
| 2544 | /// `op lhs`. rhs unused. main_token is op. | ||
| 2545 | @"await", | ||
| 2546 | /// `?lhs`. rhs unused. main_token is the `?`. | ||
| 2547 | optional_type, | ||
| 2548 | /// `[lhs]rhs`. lhs can be omitted to make it a slice. | ||
| 2549 | array_type, | ||
| 2550 | /// `[lhs:a]b`. `array_type_sentinel[rhs]`. | ||
| 2551 | array_type_sentinel, | ||
| 2552 | /// `[*]align(lhs) rhs`. lhs can be omitted. | ||
| 2553 | /// `*align(lhs) rhs`. lhs can be omitted. | ||
| 2554 | /// `[]rhs`. | ||
| 2555 | /// main_token is the asterisk if a pointer or the lbracket if a slice | ||
| 2556 | /// main_token might be a ** token, which is shared with a parent/child | ||
| 2557 | /// pointer type and may require special handling. | ||
| 2558 | ptr_type_aligned, | ||
| 2559 | /// `[*:lhs]rhs`. lhs can be omitted. | ||
| 2560 | /// `*rhs`. | ||
| 2561 | /// `[:lhs]rhs`. | ||
| 2562 | /// main_token is the asterisk if a pointer or the lbracket if a slice | ||
| 2563 | /// main_token might be a ** token, which is shared with a parent/child | ||
| 2564 | /// pointer type and may require special handling. | ||
| 2565 | ptr_type_sentinel, | ||
| 2566 | /// lhs is index into ptr_type. rhs is the element type expression. | ||
| 2567 | /// main_token is the asterisk if a pointer or the lbracket if a slice | ||
| 2568 | /// main_token might be a ** token, which is shared with a parent/child | ||
| 2569 | /// pointer type and may require special handling. | ||
| 2570 | ptr_type, | ||
| 2571 | /// lhs is index into ptr_type_bit_range. rhs is the element type expression. | ||
| 2572 | /// main_token is the asterisk if a pointer or the lbracket if a slice | ||
| 2573 | /// main_token might be a ** token, which is shared with a parent/child | ||
| 2574 | /// pointer type and may require special handling. | ||
| 2575 | ptr_type_bit_range, | ||
| 2576 | /// `lhs[rhs..]` | ||
| 2577 | /// main_token is the lbracket. | ||
| 2578 | slice_open, | ||
| 2579 | /// `lhs[b..c]`. rhs is index into Slice | ||
| 2580 | /// main_token is the lbracket. | ||
| 2581 | slice, | ||
| 2582 | /// `lhs[b..c :d]`. rhs is index into SliceSentinel | ||
| 2583 | /// main_token is the lbracket. | ||
| 2584 | slice_sentinel, | ||
| 2585 | /// `lhs.*`. rhs is unused. | ||
| 2586 | deref, | ||
| 2587 | /// `lhs[rhs]`. | ||
| 2588 | array_access, | ||
| 2589 | /// `lhs{rhs}`. rhs can be omitted. | ||
| 2590 | array_init_one, | ||
| 2591 | /// `lhs{rhs,}`. rhs can *not* be omitted | ||
| 2592 | array_init_one_comma, | ||
| 2593 | /// `.{lhs, rhs}`. lhs and rhs can be omitted. | ||
| 2594 | array_init_dot_two, | ||
| 2595 | /// Same as `array_init_dot_two` except there is known to be a trailing comma | ||
| 2596 | /// before the final rbrace. | ||
| 2597 | array_init_dot_two_comma, | ||
| 2598 | /// `.{a, b}`. `sub_list[lhs..rhs]`. | ||
| 2599 | array_init_dot, | ||
| 2600 | /// Same as `array_init_dot` except there is known to be a trailing comma | ||
| 2601 | /// before the final rbrace. | ||
| 2602 | array_init_dot_comma, | ||
| 2603 | /// `lhs{a, b}`. `sub_range_list[rhs]`. lhs can be omitted which means `.{a, b}`. | ||
| 2604 | array_init, | ||
| 2605 | /// Same as `array_init` except there is known to be a trailing comma | ||
| 2606 | /// before the final rbrace. | ||
| 2607 | array_init_comma, | ||
| 2608 | /// `lhs{.a = rhs}`. rhs can be omitted making it empty. | ||
| 2609 | /// main_token is the lbrace. | ||
| 2610 | struct_init_one, | ||
| 2611 | /// `lhs{.a = rhs,}`. rhs can *not* be omitted. | ||
| 2612 | /// main_token is the lbrace. | ||
| 2613 | struct_init_one_comma, | ||
| 2614 | /// `.{.a = lhs, .b = rhs}`. lhs and rhs can be omitted. | ||
| 2615 | /// main_token is the lbrace. | ||
| 2616 | /// No trailing comma before the rbrace. | ||
| 2617 | struct_init_dot_two, | ||
| 2618 | /// Same as `struct_init_dot_two` except there is known to be a trailing comma | ||
| 2619 | /// before the final rbrace. | ||
| 2620 | struct_init_dot_two_comma, | ||
| 2621 | /// `.{.a = b, .c = d}`. `sub_list[lhs..rhs]`. | ||
| 2622 | /// main_token is the lbrace. | ||
| 2623 | struct_init_dot, | ||
| 2624 | /// Same as `struct_init_dot` except there is known to be a trailing comma | ||
| 2625 | /// before the final rbrace. | ||
| 2626 | struct_init_dot_comma, | ||
| 2627 | /// `lhs{.a = b, .c = d}`. `sub_range_list[rhs]`. | ||
| 2628 | /// lhs can be omitted which means `.{.a = b, .c = d}`. | ||
| 2629 | /// main_token is the lbrace. | ||
| 2630 | struct_init, | ||
| 2631 | /// Same as `struct_init` except there is known to be a trailing comma | ||
| 2632 | /// before the final rbrace. | ||
| 2633 | struct_init_comma, | ||
| 2634 | /// `lhs(rhs)`. rhs can be omitted. | ||
| 2635 | /// main_token is the lparen. | ||
| 2636 | call_one, | ||
| 2637 | /// `lhs(rhs,)`. rhs can be omitted. | ||
| 2638 | /// main_token is the lparen. | ||
| 2639 | call_one_comma, | ||
| 2640 | /// `async lhs(rhs)`. rhs can be omitted. | ||
| 2641 | async_call_one, | ||
| 2642 | /// `async lhs(rhs,)`. | ||
| 2643 | async_call_one_comma, | ||
| 2644 | /// `lhs(a, b, c)`. `SubRange[rhs]`. | ||
| 2645 | /// main_token is the `(`. | ||
| 2646 | call, | ||
| 2647 | /// `lhs(a, b, c,)`. `SubRange[rhs]`. | ||
| 2648 | /// main_token is the `(`. | ||
| 2649 | call_comma, | ||
| 2650 | /// `async lhs(a, b, c)`. `SubRange[rhs]`. | ||
| 2651 | /// main_token is the `(`. | ||
| 2652 | async_call, | ||
| 2653 | /// `async lhs(a, b, c,)`. `SubRange[rhs]`. | ||
| 2654 | /// main_token is the `(`. | ||
| 2655 | async_call_comma, | ||
| 2656 | /// `switch(lhs) {}`. `SubRange[rhs]`. | ||
| 2657 | @"switch", | ||
| 2658 | /// Same as switch except there is known to be a trailing comma | ||
| 2659 | /// before the final rbrace | ||
| 2660 | switch_comma, | ||
| 2661 | /// `lhs => rhs`. If lhs is omitted it means `else`. | ||
| 2662 | /// main_token is the `=>` | ||
| 2663 | switch_case_one, | ||
| 2664 | /// `a, b, c => rhs`. `SubRange[lhs]`. | ||
| 2665 | /// main_token is the `=>` | ||
| 2666 | switch_case, | ||
| 2667 | /// `lhs...rhs`. | ||
| 2668 | switch_range, | ||
| 2669 | /// `while (lhs) rhs`. | ||
| 2670 | /// `while (lhs) |x| rhs`. | ||
| 2671 | while_simple, | ||
| 2672 | /// `while (lhs) : (a) b`. `WhileCont[rhs]`. | ||
| 2673 | /// `while (lhs) : (a) b`. `WhileCont[rhs]`. | ||
| 2674 | while_cont, | ||
| 2675 | /// `while (lhs) : (a) b else c`. `While[rhs]`. | ||
| 2676 | /// `while (lhs) |x| : (a) b else c`. `While[rhs]`. | ||
| 2677 | /// `while (lhs) |x| : (a) b else |y| c`. `While[rhs]`. | ||
| 2678 | @"while", | ||
| 2679 | /// `for (lhs) rhs`. | ||
| 2680 | for_simple, | ||
| 2681 | /// `for (lhs) a else b`. `if_list[rhs]`. | ||
| 2682 | @"for", | ||
| 2683 | /// `if (lhs) rhs`. | ||
| 2684 | /// `if (lhs) |a| rhs`. | ||
| 2685 | if_simple, | ||
| 2686 | /// `if (lhs) a else b`. `If[rhs]`. | ||
| 2687 | /// `if (lhs) |x| a else b`. `If[rhs]`. | ||
| 2688 | /// `if (lhs) |x| a else |y| b`. `If[rhs]`. | ||
| 2689 | @"if", | ||
| 2690 | /// `suspend lhs`. lhs can be omitted. rhs is unused. | ||
| 2691 | @"suspend", | ||
| 2692 | /// `resume lhs`. rhs is unused. | ||
| 2693 | @"resume", | ||
| 2694 | /// `continue`. lhs is token index of label if any. rhs is unused. | ||
| 2695 | @"continue", | ||
| 2696 | /// `break :lhs rhs` | ||
| 2697 | /// both lhs and rhs may be omitted. | ||
| 2698 | @"break", | ||
| 2699 | /// `return lhs`. lhs can be omitted. rhs is unused. | ||
| 2700 | @"return", | ||
| 2701 | /// `fn(a: lhs) rhs`. lhs can be omitted. | ||
| 2702 | /// anytype and ... parameters are omitted from the AST tree. | ||
| 2703 | /// main_token is the `fn` keyword. | ||
| 2704 | /// extern function declarations use this tag. | ||
| 2705 | fn_proto_simple, | ||
| 2706 | /// `fn(a: b, c: d) rhs`. `sub_range_list[lhs]`. | ||
| 2707 | /// anytype and ... parameters are omitted from the AST tree. | ||
| 2708 | /// main_token is the `fn` keyword. | ||
| 2709 | /// extern function declarations use this tag. | ||
| 2710 | fn_proto_multi, | ||
| 2711 | /// `fn(a: b) rhs linksection(e) callconv(f)`. `FnProtoOne[lhs]`. | ||
| 2712 | /// zero or one parameters. | ||
| 2713 | /// anytype and ... parameters are omitted from the AST tree. | ||
| 2714 | /// main_token is the `fn` keyword. | ||
| 2715 | /// extern function declarations use this tag. | ||
| 2716 | fn_proto_one, | ||
| 2717 | /// `fn(a: b, c: d) rhs linksection(e) callconv(f)`. `FnProto[lhs]`. | ||
| 2718 | /// anytype and ... parameters are omitted from the AST tree. | ||
| 2719 | /// main_token is the `fn` keyword. | ||
| 2720 | /// extern function declarations use this tag. | ||
| 2721 | fn_proto, | ||
| 2722 | /// lhs is the fn_proto. | ||
| 2723 | /// rhs is the function body block. | ||
| 2724 | /// Note that extern function declarations use the fn_proto tags rather | ||
| 2725 | /// than this one. | ||
| 2726 | fn_decl, | ||
| 2727 | /// `anyframe->rhs`. main_token is `anyframe`. `lhs` is arrow token index. | ||
| 2728 | anyframe_type, | ||
| 2729 | /// Both lhs and rhs unused. | ||
| 2730 | anyframe_literal, | ||
| 2731 | /// Both lhs and rhs unused. | ||
| 2732 | char_literal, | ||
| 2733 | /// Both lhs and rhs unused. | ||
| 2734 | integer_literal, | ||
| 2735 | /// Both lhs and rhs unused. | ||
| 2736 | float_literal, | ||
| 2737 | /// Both lhs and rhs unused. | ||
| 2738 | false_literal, | ||
| 2739 | /// Both lhs and rhs unused. | ||
| 2740 | true_literal, | ||
| 2741 | /// Both lhs and rhs unused. | ||
| 2742 | null_literal, | ||
| 2743 | /// Both lhs and rhs unused. | ||
| 2744 | undefined_literal, | ||
| 2745 | /// Both lhs and rhs unused. | ||
| 2746 | unreachable_literal, | ||
| 2747 | /// Both lhs and rhs unused. | ||
| 2748 | /// Most identifiers will not have explicit AST nodes, however for expressions | ||
| 2749 | /// which could be one of many different kinds of AST nodes, there will be an | ||
| 2750 | /// identifier AST node for it. | ||
| 2751 | identifier, | ||
| 2752 | /// lhs is the dot token index, rhs unused, main_token is the identifier. | ||
| 2753 | enum_literal, | ||
| 2754 | /// main_token is the string literal token | ||
| 2755 | /// Both lhs and rhs unused. | ||
| 2756 | string_literal, | ||
| 2757 | /// main_token is the first token index (redundant with lhs) | ||
| 2758 | /// lhs is the first token index; rhs is the last token index. | ||
| 2759 | /// Could be a series of multiline_string_literal_line tokens, or a single | ||
| 2760 | /// string_literal token. | ||
| 2761 | multiline_string_literal, | ||
| 2762 | /// `(lhs)`. main_token is the `(`; rhs is the token index of the `)`. | ||
| 2763 | grouped_expression, | ||
| 2764 | /// `@a(lhs, rhs)`. lhs and rhs may be omitted. | ||
| 2765 | /// main_token is the builtin token. | ||
| 2766 | builtin_call_two, | ||
| 2767 | /// Same as builtin_call_two but there is known to be a trailing comma before the rparen. | ||
| 2768 | builtin_call_two_comma, | ||
| 2769 | /// `@a(b, c)`. `sub_list[lhs..rhs]`. | ||
| 2770 | /// main_token is the builtin token. | ||
| 2771 | builtin_call, | ||
| 2772 | /// Same as builtin_call but there is known to be a trailing comma before the rparen. | ||
| 2773 | builtin_call_comma, | ||
| 2774 | /// `error{a, b}`. | ||
| 2775 | /// rhs is the rbrace, lhs is unused. | ||
| 2776 | error_set_decl, | ||
| 2777 | /// `struct {}`, `union {}`, `opaque {}`, `enum {}`. `extra_data[lhs..rhs]`. | ||
| 2778 | /// main_token is `struct`, `union`, `opaque`, `enum` keyword. | ||
| 2779 | container_decl, | ||
| 2780 | /// Same as ContainerDecl but there is known to be a trailing comma | ||
| 2781 | /// or semicolon before the rbrace. | ||
| 2782 | container_decl_trailing, | ||
| 2783 | /// `struct {lhs, rhs}`, `union {lhs, rhs}`, `opaque {lhs, rhs}`, `enum {lhs, rhs}`. | ||
| 2784 | /// lhs or rhs can be omitted. | ||
| 2785 | /// main_token is `struct`, `union`, `opaque`, `enum` keyword. | ||
| 2786 | container_decl_two, | ||
| 2787 | /// Same as ContainerDeclTwo except there is known to be a trailing comma | ||
| 2788 | /// or semicolon before the rbrace. | ||
| 2789 | container_decl_two_trailing, | ||
| 2790 | /// `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`. | ||
| 2791 | container_decl_arg, | ||
| 2792 | /// Same as container_decl_arg but there is known to be a trailing | ||
| 2793 | /// comma or semicolon before the rbrace. | ||
| 2794 | container_decl_arg_trailing, | ||
| 2795 | /// `union(enum) {}`. `sub_list[lhs..rhs]`. | ||
| 2796 | /// Note that tagged unions with explicitly provided enums are represented | ||
| 2797 | /// by `container_decl_arg`. | ||
| 2798 | tagged_union, | ||
| 2799 | /// Same as tagged_union but there is known to be a trailing comma | ||
| 2800 | /// or semicolon before the rbrace. | ||
| 2801 | tagged_union_trailing, | ||
| 2802 | /// `union(enum) {lhs, rhs}`. lhs or rhs may be omitted. | ||
| 2803 | /// Note that tagged unions with explicitly provided enums are represented | ||
| 2804 | /// by `container_decl_arg`. | ||
| 2805 | tagged_union_two, | ||
| 2806 | /// Same as tagged_union_two but there is known to be a trailing comma | ||
| 2807 | /// or semicolon before the rbrace. | ||
| 2808 | tagged_union_two_trailing, | ||
| 2809 | /// `union(enum(lhs)) {}`. `SubRange[rhs]`. | ||
| 2810 | tagged_union_enum_tag, | ||
| 2811 | /// Same as tagged_union_enum_tag but there is known to be a trailing comma | ||
| 2812 | /// or semicolon before the rbrace. | ||
| 2813 | tagged_union_enum_tag_trailing, | ||
| 2814 | /// `a: lhs = rhs,`. lhs and rhs can be omitted. | ||
| 2815 | /// main_token is the field name identifier. | ||
| 2816 | /// lastToken() does not include the possible trailing comma. | ||
| 2817 | container_field_init, | ||
| 2818 | /// `a: lhs align(rhs),`. rhs can be omitted. | ||
| 2819 | /// main_token is the field name identifier. | ||
| 2820 | /// lastToken() does not include the possible trailing comma. | ||
| 2821 | container_field_align, | ||
| 2822 | /// `a: lhs align(c) = d,`. `container_field_list[rhs]`. | ||
| 2823 | /// main_token is the field name identifier. | ||
| 2824 | /// lastToken() does not include the possible trailing comma. | ||
| 2825 | container_field, | ||
| 2826 | /// `anytype`. both lhs and rhs unused. | ||
| 2827 | /// Used by `ContainerField`. | ||
| 2828 | @"anytype", | ||
| 2829 | /// `comptime lhs`. rhs unused. | ||
| 2830 | @"comptime", | ||
| 2831 | /// `nosuspend lhs`. rhs unused. | ||
| 2832 | @"nosuspend", | ||
| 2833 | /// `{lhs rhs}`. rhs or lhs can be omitted. | ||
| 2834 | /// main_token points at the lbrace. | ||
| 2835 | block_two, | ||
| 2836 | /// Same as block_two but there is known to be a semicolon before the rbrace. | ||
| 2837 | block_two_semicolon, | ||
| 2838 | /// `{}`. `sub_list[lhs..rhs]`. | ||
| 2839 | /// main_token points at the lbrace. | ||
| 2840 | block, | ||
| 2841 | /// Same as block but there is known to be a semicolon before the rbrace. | ||
| 2842 | block_semicolon, | ||
| 2843 | /// `asm(lhs)`. rhs is the token index of the rparen. | ||
| 2844 | asm_simple, | ||
| 2845 | /// `asm(lhs, a)`. `Asm[rhs]`. | ||
| 2846 | @"asm", | ||
| 2847 | /// `[a] "b" (c)`. lhs is 0, rhs is token index of the rparen. | ||
| 2848 | /// `[a] "b" (-> lhs)`. rhs is token index of the rparen. | ||
| 2849 | /// main_token is `a`. | ||
| 2850 | asm_output, | ||
| 2851 | /// `[a] "b" (lhs)`. rhs is token index of the rparen. | ||
| 2852 | /// main_token is `a`. | ||
| 2853 | asm_input, | ||
| 2854 | /// `error.a`. lhs is token index of `.`. rhs is token index of `a`. | ||
| 2855 | error_value, | ||
| 2856 | /// `lhs!rhs`. main_token is the `!`. | ||
| 2857 | error_union, | ||
| 2858 | |||
| 2859 | pub fn isContainerField(tag: Tag) bool { | ||
| 2860 | return switch (tag) { | ||
| 2861 | .container_field_init, | ||
| 2862 | .container_field_align, | ||
| 2863 | .container_field, | ||
| 2864 | => true, | ||
| 3193 | 2865 | ||
| 3194 | if (i < self.inputs.len * 3) switch (i % 3) { | 2866 | else => false, |
| 3195 | 0 => return self.inputs[i / 3].symbolic_name, | ||
| 3196 | 1 => return self.inputs[i / 3].constraint, | ||
| 3197 | 2 => return self.inputs[i / 3].expr, | ||
| 3198 | else => unreachable, | ||
| 3199 | }; | 2867 | }; |
| 3200 | i -= self.inputs.len * 3; | ||
| 3201 | |||
| 3202 | return null; | ||
| 3203 | } | 2868 | } |
| 2869 | }; | ||
| 3204 | 2870 | ||
| 3205 | pub fn firstToken(self: *const Asm) TokenIndex { | 2871 | pub const Data = struct { |
| 3206 | return self.asm_token; | 2872 | lhs: Index, |
| 3207 | } | 2873 | rhs: Index, |
| 2874 | }; | ||
| 3208 | 2875 | ||
| 3209 | pub fn lastToken(self: *const Asm) TokenIndex { | 2876 | pub const LocalVarDecl = struct { |
| 3210 | return self.rparen; | 2877 | type_node: Index, |
| 3211 | } | 2878 | align_node: Index, |
| 3212 | }; | 2879 | }; |
| 3213 | 2880 | ||
| 3214 | /// TODO remove from the Node base struct | 2881 | pub const ArrayTypeSentinel = struct { |
| 3215 | /// TODO actually maybe remove entirely in favor of iterating backward from Node.firstToken() | 2882 | elem_type: Index, |
| 3216 | /// and forwards to find same-line doc comments. | 2883 | sentinel: Index, |
| 3217 | pub const DocComment = struct { | 2884 | }; |
| 3218 | base: Node = Node{ .tag = .DocComment }, | ||
| 3219 | /// Points to the first doc comment token. API users are expected to iterate over the | ||
| 3220 | /// tokens array, looking for more doc comments, ignoring line comments, and stopping | ||
| 3221 | /// at the first other token. | ||
| 3222 | first_line: TokenIndex, | ||
| 3223 | |||
| 3224 | pub fn iterate(self: *const DocComment, index: usize) ?*Node { | ||
| 3225 | return null; | ||
| 3226 | } | ||
| 3227 | 2885 | ||
| 3228 | pub fn firstToken(self: *const DocComment) TokenIndex { | 2886 | pub const PtrType = struct { |
| 3229 | return self.first_line; | 2887 | sentinel: Index, |
| 3230 | } | 2888 | align_node: Index, |
| 2889 | }; | ||
| 3231 | 2890 | ||
| 3232 | /// Returns the first doc comment line. Be careful, this may not be the desired behavior, | 2891 | pub const PtrTypeBitRange = struct { |
| 3233 | /// which would require the tokens array. | 2892 | sentinel: Index, |
| 3234 | pub fn lastToken(self: *const DocComment) TokenIndex { | 2893 | align_node: Index, |
| 3235 | return self.first_line; | 2894 | bit_range_start: Index, |
| 3236 | } | 2895 | bit_range_end: Index, |
| 3237 | }; | 2896 | }; |
| 3238 | 2897 | ||
| 3239 | pub const TestDecl = struct { | 2898 | pub const SubRange = struct { |
| 3240 | base: Node = Node{ .tag = .TestDecl }, | 2899 | /// Index into sub_list. |
| 3241 | doc_comments: ?*DocComment, | 2900 | start: Index, |
| 3242 | test_token: TokenIndex, | 2901 | /// Index into sub_list. |
| 3243 | name: ?*Node, | 2902 | end: Index, |
| 3244 | body_node: *Node, | 2903 | }; |
| 3245 | 2904 | ||
| 3246 | pub fn iterate(self: *const TestDecl, index: usize) ?*Node { | 2905 | pub const If = struct { |
| 3247 | var i = index; | 2906 | then_expr: Index, |
| 2907 | else_expr: Index, | ||
| 2908 | }; | ||
| 3248 | 2909 | ||
| 3249 | if (i < 1) return self.body_node; | 2910 | pub const ContainerField = struct { |
| 3250 | i -= 1; | 2911 | value_expr: Index, |
| 2912 | align_expr: Index, | ||
| 2913 | }; | ||
| 3251 | 2914 | ||
| 3252 | return null; | 2915 | pub const GlobalVarDecl = struct { |
| 3253 | } | 2916 | type_node: Index, |
| 2917 | align_node: Index, | ||
| 2918 | section_node: Index, | ||
| 2919 | }; | ||
| 3254 | 2920 | ||
| 3255 | pub fn firstToken(self: *const TestDecl) TokenIndex { | 2921 | pub const Slice = struct { |
| 3256 | return self.test_token; | 2922 | start: Index, |
| 3257 | } | 2923 | end: Index, |
| 2924 | }; | ||
| 3258 | 2925 | ||
| 3259 | pub fn lastToken(self: *const TestDecl) TokenIndex { | 2926 | pub const SliceSentinel = struct { |
| 3260 | return self.body_node.lastToken(); | 2927 | start: Index, |
| 3261 | } | 2928 | end: Index, |
| 2929 | sentinel: Index, | ||
| 2930 | }; | ||
| 2931 | |||
| 2932 | pub const While = struct { | ||
| 2933 | cont_expr: Index, | ||
| 2934 | then_expr: Index, | ||
| 2935 | else_expr: Index, | ||
| 3262 | }; | 2936 | }; |
| 3263 | }; | ||
| 3264 | 2937 | ||
| 3265 | pub const PtrInfo = struct { | 2938 | pub const WhileCont = struct { |
| 3266 | allowzero_token: ?TokenIndex = null, | 2939 | cont_expr: Index, |
| 3267 | align_info: ?Align = null, | 2940 | then_expr: Index, |
| 3268 | const_token: ?TokenIndex = null, | 2941 | }; |
| 3269 | volatile_token: ?TokenIndex = null, | ||
| 3270 | sentinel: ?*Node = null, | ||
| 3271 | 2942 | ||
| 3272 | pub const Align = struct { | 2943 | pub const FnProtoOne = struct { |
| 3273 | node: *Node, | 2944 | /// Populated if there is exactly 1 parameter. Otherwise there are 0 parameters. |
| 3274 | bit_range: ?BitRange = null, | 2945 | param: Index, |
| 2946 | /// Populated if align(A) is present. | ||
| 2947 | align_expr: Index, | ||
| 2948 | /// Populated if linksection(A) is present. | ||
| 2949 | section_expr: Index, | ||
| 2950 | /// Populated if callconv(A) is present. | ||
| 2951 | callconv_expr: Index, | ||
| 2952 | }; | ||
| 3275 | 2953 | ||
| 3276 | pub const BitRange = struct { | 2954 | pub const FnProto = struct { |
| 3277 | start: *Node, | 2955 | params_start: Index, |
| 3278 | end: *Node, | 2956 | params_end: Index, |
| 3279 | }; | 2957 | /// Populated if align(A) is present. |
| 2958 | align_expr: Index, | ||
| 2959 | /// Populated if linksection(A) is present. | ||
| 2960 | section_expr: Index, | ||
| 2961 | /// Populated if callconv(A) is present. | ||
| 2962 | callconv_expr: Index, | ||
| 3280 | }; | 2963 | }; |
| 3281 | }; | ||
| 3282 | 2964 | ||
| 3283 | test "iterate" { | 2965 | pub const Asm = struct { |
| 3284 | var root = Node.Root{ | 2966 | items_start: Index, |
| 3285 | .base = Node{ .tag = Node.Tag.Root }, | 2967 | items_end: Index, |
| 3286 | .decls_len = 0, | 2968 | /// Needed to make lastToken() work. |
| 3287 | .eof_token = 0, | 2969 | rparen: TokenIndex, |
| 3288 | }; | 2970 | }; |
| 3289 | var base = &root.base; | 2971 | }; |
| 3290 | testing.expect(base.iterate(0) == null); | ||
| 3291 | } |
lib/std/zig/parse.zig+3505-2912| ... | @@ -11,85 +11,181 @@ const Node = ast.Node; | ... | @@ -11,85 +11,181 @@ const Node = ast.Node; |
| 11 | const Tree = ast.Tree; | 11 | const Tree = ast.Tree; |
| 12 | const AstError = ast.Error; | 12 | const AstError = ast.Error; |
| 13 | const TokenIndex = ast.TokenIndex; | 13 | const TokenIndex = ast.TokenIndex; |
| 14 | const NodeIndex = ast.NodeIndex; | ||
| 15 | const Token = std.zig.Token; | 14 | const Token = std.zig.Token; |
| 16 | 15 | ||
| 17 | pub const Error = error{ParseError} || Allocator.Error; | 16 | pub const Error = error{ParseError} || Allocator.Error; |
| 18 | 17 | ||
| 19 | /// Result should be freed with tree.deinit() when there are | 18 | /// Result should be freed with tree.deinit() when there are |
| 20 | /// no more references to any of the tokens or nodes. | 19 | /// no more references to any of the tokens or nodes. |
| 21 | pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!*Tree { | 20 | pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!Tree { |
| 22 | var token_ids = std.ArrayList(Token.Id).init(gpa); | 21 | var tokens = ast.TokenList{}; |
| 23 | defer token_ids.deinit(); | 22 | defer tokens.deinit(gpa); |
| 24 | var token_locs = std.ArrayList(Token.Loc).init(gpa); | ||
| 25 | defer token_locs.deinit(); | ||
| 26 | 23 | ||
| 27 | // Empirically, the zig std lib has an 8:1 ratio of source bytes to token count. | 24 | // Empirically, the zig std lib has an 8:1 ratio of source bytes to token count. |
| 28 | const estimated_token_count = source.len / 8; | 25 | const estimated_token_count = source.len / 8; |
| 29 | try token_ids.ensureCapacity(estimated_token_count); | 26 | try tokens.ensureCapacity(gpa, estimated_token_count); |
| 30 | try token_locs.ensureCapacity(estimated_token_count); | ||
| 31 | 27 | ||
| 32 | var tokenizer = std.zig.Tokenizer.init(source); | 28 | var tokenizer = std.zig.Tokenizer.init(source); |
| 33 | while (true) { | 29 | while (true) { |
| 34 | const token = tokenizer.next(); | 30 | const token = tokenizer.next(); |
| 35 | try token_ids.append(token.id); | 31 | try tokens.append(gpa, .{ |
| 36 | try token_locs.append(token.loc); | 32 | .tag = token.tag, |
| 37 | if (token.id == .Eof) break; | 33 | .start = @intCast(u32, token.loc.start), |
| 34 | }); | ||
| 35 | if (token.tag == .eof) break; | ||
| 38 | } | 36 | } |
| 39 | 37 | ||
| 40 | var parser: Parser = .{ | 38 | var parser: Parser = .{ |
| 41 | .source = source, | 39 | .source = source, |
| 42 | .arena = std.heap.ArenaAllocator.init(gpa), | ||
| 43 | .gpa = gpa, | 40 | .gpa = gpa, |
| 44 | .token_ids = token_ids.items, | 41 | .token_tags = tokens.items(.tag), |
| 45 | .token_locs = token_locs.items, | 42 | .token_starts = tokens.items(.start), |
| 46 | .errors = .{}, | 43 | .errors = .{}, |
| 44 | .nodes = .{}, | ||
| 45 | .extra_data = .{}, | ||
| 47 | .tok_i = 0, | 46 | .tok_i = 0, |
| 48 | }; | 47 | }; |
| 49 | defer parser.errors.deinit(gpa); | 48 | defer parser.errors.deinit(gpa); |
| 50 | errdefer parser.arena.deinit(); | 49 | defer parser.nodes.deinit(gpa); |
| 51 | 50 | defer parser.extra_data.deinit(gpa); | |
| 52 | while (token_ids.items[parser.tok_i] == .LineComment) parser.tok_i += 1; | 51 | |
| 53 | 52 | // Empirically, Zig source code has a 2:1 ratio of tokens to AST nodes. | |
| 54 | const root_node = try parser.parseRoot(); | 53 | // Make sure at least 1 so we can use appendAssumeCapacity on the root node below. |
| 54 | const estimated_node_count = (tokens.len + 2) / 2; | ||
| 55 | try parser.nodes.ensureCapacity(gpa, estimated_node_count); | ||
| 56 | |||
| 57 | // Root node must be index 0. | ||
| 58 | // Root <- skip ContainerMembers eof | ||
| 59 | parser.nodes.appendAssumeCapacity(.{ | ||
| 60 | .tag = .root, | ||
| 61 | .main_token = 0, | ||
| 62 | .data = .{ | ||
| 63 | .lhs = undefined, | ||
| 64 | .rhs = undefined, | ||
| 65 | }, | ||
| 66 | }); | ||
| 67 | const root_members = try parser.parseContainerMembers(); | ||
| 68 | const root_decls = try root_members.toSpan(&parser); | ||
| 69 | if (parser.token_tags[parser.tok_i] != .eof) { | ||
| 70 | try parser.warnExpected(.eof); | ||
| 71 | } | ||
| 72 | parser.nodes.items(.data)[0] = .{ | ||
| 73 | .lhs = root_decls.start, | ||
| 74 | .rhs = root_decls.end, | ||
| 75 | }; | ||
| 55 | 76 | ||
| 56 | const tree = try parser.arena.allocator.create(Tree); | 77 | // TODO experiment with compacting the MultiArrayList slices here |
| 57 | tree.* = .{ | 78 | return Tree{ |
| 58 | .gpa = gpa, | ||
| 59 | .source = source, | 79 | .source = source, |
| 60 | .token_ids = token_ids.toOwnedSlice(), | 80 | .tokens = tokens.toOwnedSlice(), |
| 61 | .token_locs = token_locs.toOwnedSlice(), | 81 | .nodes = parser.nodes.toOwnedSlice(), |
| 82 | .extra_data = parser.extra_data.toOwnedSlice(gpa), | ||
| 62 | .errors = parser.errors.toOwnedSlice(gpa), | 83 | .errors = parser.errors.toOwnedSlice(gpa), |
| 63 | .root_node = root_node, | ||
| 64 | .arena = parser.arena.state, | ||
| 65 | }; | 84 | }; |
| 66 | return tree; | ||
| 67 | } | 85 | } |
| 68 | 86 | ||
| 87 | const null_node: Node.Index = 0; | ||
| 88 | |||
| 69 | /// Represents in-progress parsing, will be converted to an ast.Tree after completion. | 89 | /// Represents in-progress parsing, will be converted to an ast.Tree after completion. |
| 70 | const Parser = struct { | 90 | const Parser = struct { |
| 71 | arena: std.heap.ArenaAllocator, | ||
| 72 | gpa: *Allocator, | 91 | gpa: *Allocator, |
| 73 | source: []const u8, | 92 | source: []const u8, |
| 74 | token_ids: []const Token.Id, | 93 | token_tags: []const Token.Tag, |
| 75 | token_locs: []const Token.Loc, | 94 | token_starts: []const ast.ByteOffset, |
| 76 | tok_i: TokenIndex, | 95 | tok_i: TokenIndex, |
| 77 | errors: std.ArrayListUnmanaged(AstError), | 96 | errors: std.ArrayListUnmanaged(AstError), |
| 97 | nodes: ast.NodeList, | ||
| 98 | extra_data: std.ArrayListUnmanaged(Node.Index), | ||
| 99 | |||
| 100 | const SmallSpan = union(enum) { | ||
| 101 | zero_or_one: Node.Index, | ||
| 102 | multi: []Node.Index, | ||
| 78 | 103 | ||
| 79 | /// Root <- skip ContainerMembers eof | 104 | fn deinit(self: SmallSpan, gpa: *Allocator) void { |
| 80 | fn parseRoot(p: *Parser) Allocator.Error!*Node.Root { | 105 | switch (self) { |
| 81 | const decls = try parseContainerMembers(p, true); | 106 | .zero_or_one => {}, |
| 82 | defer p.gpa.free(decls); | 107 | .multi => |list| gpa.free(list), |
| 108 | } | ||
| 109 | } | ||
| 110 | }; | ||
| 83 | 111 | ||
| 84 | // parseContainerMembers will try to skip as much | 112 | const Members = struct { |
| 85 | // invalid tokens as it can so this can only be the EOF | 113 | len: usize, |
| 86 | const eof_token = p.eatToken(.Eof).?; | 114 | lhs: Node.Index, |
| 115 | rhs: Node.Index, | ||
| 116 | trailing: bool, | ||
| 87 | 117 | ||
| 88 | const decls_len = @intCast(NodeIndex, decls.len); | 118 | fn toSpan(self: Members, p: *Parser) !Node.SubRange { |
| 89 | const node = try Node.Root.create(&p.arena.allocator, decls_len, eof_token); | 119 | if (self.len <= 2) { |
| 90 | std.mem.copy(*Node, node.decls(), decls); | 120 | const nodes = [2]Node.Index{ self.lhs, self.rhs }; |
| 121 | return p.listToSpan(nodes[0..self.len]); | ||
| 122 | } else { | ||
| 123 | return Node.SubRange{ .start = self.lhs, .end = self.rhs }; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | }; | ||
| 91 | 127 | ||
| 92 | return node; | 128 | fn listToSpan(p: *Parser, list: []const Node.Index) !Node.SubRange { |
| 129 | try p.extra_data.appendSlice(p.gpa, list); | ||
| 130 | return Node.SubRange{ | ||
| 131 | .start = @intCast(Node.Index, p.extra_data.items.len - list.len), | ||
| 132 | .end = @intCast(Node.Index, p.extra_data.items.len), | ||
| 133 | }; | ||
| 134 | } | ||
| 135 | |||
| 136 | fn addNode(p: *Parser, elem: ast.NodeList.Elem) Allocator.Error!Node.Index { | ||
| 137 | const result = @intCast(Node.Index, p.nodes.len); | ||
| 138 | try p.nodes.append(p.gpa, elem); | ||
| 139 | return result; | ||
| 140 | } | ||
| 141 | |||
| 142 | fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index { | ||
| 143 | const fields = std.meta.fields(@TypeOf(extra)); | ||
| 144 | try p.extra_data.ensureCapacity(p.gpa, p.extra_data.items.len + fields.len); | ||
| 145 | const result = @intCast(u32, p.extra_data.items.len); | ||
| 146 | inline for (fields) |field| { | ||
| 147 | comptime assert(field.field_type == Node.Index); | ||
| 148 | p.extra_data.appendAssumeCapacity(@field(extra, field.name)); | ||
| 149 | } | ||
| 150 | return result; | ||
| 151 | } | ||
| 152 | |||
| 153 | fn warn(p: *Parser, tag: ast.Error.Tag) error{OutOfMemory}!void { | ||
| 154 | @setCold(true); | ||
| 155 | try p.warnMsg(.{ .tag = tag, .token = p.tok_i }); | ||
| 156 | } | ||
| 157 | |||
| 158 | fn warnExpected(p: *Parser, expected_token: Token.Tag) error{OutOfMemory}!void { | ||
| 159 | @setCold(true); | ||
| 160 | try p.warnMsg(.{ | ||
| 161 | .tag = .expected_token, | ||
| 162 | .token = p.tok_i, | ||
| 163 | .extra = .{ .expected_tag = expected_token }, | ||
| 164 | }); | ||
| 165 | } | ||
| 166 | fn warnMsg(p: *Parser, msg: ast.Error) error{OutOfMemory}!void { | ||
| 167 | @setCold(true); | ||
| 168 | try p.errors.append(p.gpa, msg); | ||
| 169 | } | ||
| 170 | |||
| 171 | fn fail(p: *Parser, tag: ast.Error.Tag) error{ ParseError, OutOfMemory } { | ||
| 172 | @setCold(true); | ||
| 173 | return p.failMsg(.{ .tag = tag, .token = p.tok_i }); | ||
| 174 | } | ||
| 175 | |||
| 176 | fn failExpected(p: *Parser, expected_token: Token.Tag) error{ ParseError, OutOfMemory } { | ||
| 177 | @setCold(true); | ||
| 178 | return p.failMsg(.{ | ||
| 179 | .tag = .expected_token, | ||
| 180 | .token = p.tok_i, | ||
| 181 | .extra = .{ .expected_tag = expected_token }, | ||
| 182 | }); | ||
| 183 | } | ||
| 184 | |||
| 185 | fn failMsg(p: *Parser, msg: ast.Error) error{ ParseError, OutOfMemory } { | ||
| 186 | @setCold(true); | ||
| 187 | try p.warnMsg(msg); | ||
| 188 | return error.ParseError; | ||
| 93 | } | 189 | } |
| 94 | 190 | ||
| 95 | /// ContainerMembers | 191 | /// ContainerMembers |
| ... | @@ -99,176 +195,226 @@ const Parser = struct { | ... | @@ -99,176 +195,226 @@ const Parser = struct { |
| 99 | /// / ContainerField COMMA ContainerMembers | 195 | /// / ContainerField COMMA ContainerMembers |
| 100 | /// / ContainerField | 196 | /// / ContainerField |
| 101 | /// / | 197 | /// / |
| 102 | fn parseContainerMembers(p: *Parser, top_level: bool) ![]*Node { | 198 | /// TopLevelComptime <- KEYWORD_comptime BlockExpr |
| 103 | var list = std.ArrayList(*Node).init(p.gpa); | 199 | fn parseContainerMembers(p: *Parser) !Members { |
| 200 | var list = std.ArrayList(Node.Index).init(p.gpa); | ||
| 104 | defer list.deinit(); | 201 | defer list.deinit(); |
| 105 | 202 | ||
| 106 | var field_state: union(enum) { | 203 | var field_state: union(enum) { |
| 107 | /// no fields have been seen | 204 | /// No fields have been seen. |
| 108 | none, | 205 | none, |
| 109 | /// currently parsing fields | 206 | /// Currently parsing fields. |
| 110 | seen, | 207 | seen, |
| 111 | /// saw fields and then a declaration after them. | 208 | /// Saw fields and then a declaration after them. |
| 112 | /// payload is first token of previous declaration. | 209 | /// Payload is first token of previous declaration. |
| 113 | end: TokenIndex, | 210 | end: Node.Index, |
| 114 | /// ther was a declaration between fields, don't report more errors | 211 | /// There was a declaration between fields, don't report more errors. |
| 115 | err, | 212 | err, |
| 116 | } = .none; | 213 | } = .none; |
| 117 | 214 | ||
| 118 | while (true) { | 215 | // Skip container doc comments. |
| 119 | if (try p.parseContainerDocComments()) |node| { | 216 | while (p.eatToken(.container_doc_comment)) |_| {} |
| 120 | try list.append(node); | ||
| 121 | continue; | ||
| 122 | } | ||
| 123 | |||
| 124 | const doc_comments = try p.parseDocComment(); | ||
| 125 | 217 | ||
| 126 | if (p.parseTestDecl() catch |err| switch (err) { | 218 | var trailing = false; |
| 127 | error.OutOfMemory => return error.OutOfMemory, | 219 | while (true) { |
| 128 | error.ParseError => { | 220 | const doc_comment = try p.eatDocComments(); |
| 129 | p.findNextContainerMember(); | 221 | |
| 130 | continue; | 222 | switch (p.token_tags[p.tok_i]) { |
| 223 | .keyword_test => { | ||
| 224 | const test_decl_node = try p.expectTestDeclRecoverable(); | ||
| 225 | if (test_decl_node != 0) { | ||
| 226 | if (field_state == .seen) { | ||
| 227 | field_state = .{ .end = test_decl_node }; | ||
| 228 | } | ||
| 229 | try list.append(test_decl_node); | ||
| 230 | } | ||
| 231 | trailing = false; | ||
| 131 | }, | 232 | }, |
| 132 | }) |node| { | 233 | .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) { |
| 133 | if (field_state == .seen) { | 234 | .identifier => { |
| 134 | field_state = .{ .end = node.firstToken() }; | 235 | p.tok_i += 1; |
| 135 | } | 236 | const container_field = try p.expectContainerFieldRecoverable(); |
| 136 | node.cast(Node.TestDecl).?.doc_comments = doc_comments; | 237 | if (container_field != 0) { |
| 137 | try list.append(node); | 238 | switch (field_state) { |
| 138 | continue; | 239 | .none => field_state = .seen, |
| 139 | } | 240 | .err, .seen => {}, |
| 140 | 241 | .end => |node| { | |
| 141 | if (p.parseTopLevelComptime() catch |err| switch (err) { | 242 | try p.warnMsg(.{ |
| 142 | error.OutOfMemory => return error.OutOfMemory, | 243 | .tag = .decl_between_fields, |
| 143 | error.ParseError => { | 244 | .token = p.nodes.items(.main_token)[node], |
| 144 | p.findNextContainerMember(); | 245 | }); |
| 145 | continue; | 246 | // Continue parsing; error will be reported later. |
| 247 | field_state = .err; | ||
| 248 | }, | ||
| 249 | } | ||
| 250 | try list.append(container_field); | ||
| 251 | switch (p.token_tags[p.tok_i]) { | ||
| 252 | .comma => { | ||
| 253 | p.tok_i += 1; | ||
| 254 | trailing = true; | ||
| 255 | continue; | ||
| 256 | }, | ||
| 257 | .r_brace, .eof => { | ||
| 258 | trailing = false; | ||
| 259 | break; | ||
| 260 | }, | ||
| 261 | else => {}, | ||
| 262 | } | ||
| 263 | // There is not allowed to be a decl after a field with no comma. | ||
| 264 | // Report error but recover parser. | ||
| 265 | try p.warnExpected(.comma); | ||
| 266 | p.findNextContainerMember(); | ||
| 267 | } | ||
| 268 | }, | ||
| 269 | .l_brace => { | ||
| 270 | const comptime_token = p.nextToken(); | ||
| 271 | const block = p.parseBlock() catch |err| switch (err) { | ||
| 272 | error.OutOfMemory => return error.OutOfMemory, | ||
| 273 | error.ParseError => blk: { | ||
| 274 | p.findNextContainerMember(); | ||
| 275 | break :blk null_node; | ||
| 276 | }, | ||
| 277 | }; | ||
| 278 | if (block != 0) { | ||
| 279 | const comptime_node = try p.addNode(.{ | ||
| 280 | .tag = .@"comptime", | ||
| 281 | .main_token = comptime_token, | ||
| 282 | .data = .{ | ||
| 283 | .lhs = block, | ||
| 284 | .rhs = undefined, | ||
| 285 | }, | ||
| 286 | }); | ||
| 287 | if (field_state == .seen) { | ||
| 288 | field_state = .{ .end = comptime_node }; | ||
| 289 | } | ||
| 290 | try list.append(comptime_node); | ||
| 291 | } | ||
| 292 | trailing = false; | ||
| 293 | }, | ||
| 294 | else => { | ||
| 295 | p.tok_i += 1; | ||
| 296 | try p.warn(.expected_block_or_field); | ||
| 297 | }, | ||
| 146 | }, | 298 | }, |
| 147 | }) |node| { | 299 | .keyword_pub => { |
| 148 | if (field_state == .seen) { | 300 | p.tok_i += 1; |
| 149 | field_state = .{ .end = node.firstToken() }; | 301 | const top_level_decl = try p.expectTopLevelDeclRecoverable(); |
| 150 | } | 302 | if (top_level_decl != 0) { |
| 151 | node.cast(Node.Comptime).?.doc_comments = doc_comments; | 303 | if (field_state == .seen) { |
| 152 | try list.append(node); | 304 | field_state = .{ .end = top_level_decl }; |
| 153 | continue; | 305 | } |
| 154 | } | 306 | try list.append(top_level_decl); |
| 155 | 307 | } | |
| 156 | const visib_token = p.eatToken(.Keyword_pub); | 308 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; |
| 157 | |||
| 158 | if (p.parseTopLevelDecl(doc_comments, visib_token) catch |err| switch (err) { | ||
| 159 | error.OutOfMemory => return error.OutOfMemory, | ||
| 160 | error.ParseError => { | ||
| 161 | p.findNextContainerMember(); | ||
| 162 | continue; | ||
| 163 | }, | 309 | }, |
| 164 | }) |node| { | 310 | .keyword_usingnamespace => { |
| 165 | if (field_state == .seen) { | 311 | const node = try p.expectUsingNamespaceRecoverable(); |
| 166 | field_state = .{ .end = visib_token orelse node.firstToken() }; | 312 | if (node != 0) { |
| 167 | } | 313 | if (field_state == .seen) { |
| 168 | try list.append(node); | 314 | field_state = .{ .end = node }; |
| 169 | continue; | 315 | } |
| 170 | } | 316 | try list.append(node); |
| 171 | 317 | } | |
| 172 | if (visib_token != null) { | 318 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; |
| 173 | try p.errors.append(p.gpa, .{ | 319 | }, |
| 174 | .ExpectedPubItem = .{ .token = p.tok_i }, | 320 | .keyword_const, |
| 175 | }); | 321 | .keyword_var, |
| 176 | // ignore this pub | 322 | .keyword_threadlocal, |
| 177 | continue; | 323 | .keyword_export, |
| 178 | } | 324 | .keyword_extern, |
| 179 | 325 | .keyword_inline, | |
| 180 | if (p.parseContainerField() catch |err| switch (err) { | 326 | .keyword_noinline, |
| 181 | error.OutOfMemory => return error.OutOfMemory, | 327 | .keyword_fn, |
| 182 | error.ParseError => { | 328 | => { |
| 183 | // attempt to recover | 329 | const top_level_decl = try p.expectTopLevelDeclRecoverable(); |
| 184 | p.findNextContainerMember(); | 330 | if (top_level_decl != 0) { |
| 185 | continue; | 331 | if (field_state == .seen) { |
| 332 | field_state = .{ .end = top_level_decl }; | ||
| 333 | } | ||
| 334 | try list.append(top_level_decl); | ||
| 335 | } | ||
| 336 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | ||
| 337 | }, | ||
| 338 | .identifier => { | ||
| 339 | const container_field = try p.expectContainerFieldRecoverable(); | ||
| 340 | if (container_field != 0) { | ||
| 341 | switch (field_state) { | ||
| 342 | .none => field_state = .seen, | ||
| 343 | .err, .seen => {}, | ||
| 344 | .end => |node| { | ||
| 345 | try p.warnMsg(.{ | ||
| 346 | .tag = .decl_between_fields, | ||
| 347 | .token = p.nodes.items(.main_token)[node], | ||
| 348 | }); | ||
| 349 | // Continue parsing; error will be reported later. | ||
| 350 | field_state = .err; | ||
| 351 | }, | ||
| 352 | } | ||
| 353 | try list.append(container_field); | ||
| 354 | switch (p.token_tags[p.tok_i]) { | ||
| 355 | .comma => { | ||
| 356 | p.tok_i += 1; | ||
| 357 | trailing = true; | ||
| 358 | continue; | ||
| 359 | }, | ||
| 360 | .r_brace, .eof => { | ||
| 361 | trailing = false; | ||
| 362 | break; | ||
| 363 | }, | ||
| 364 | else => {}, | ||
| 365 | } | ||
| 366 | // There is not allowed to be a decl after a field with no comma. | ||
| 367 | // Report error but recover parser. | ||
| 368 | try p.warnExpected(.comma); | ||
| 369 | p.findNextContainerMember(); | ||
| 370 | } | ||
| 186 | }, | 371 | }, |
| 187 | }) |node| { | 372 | .eof, .r_brace => { |
| 188 | switch (field_state) { | 373 | if (doc_comment) |tok| { |
| 189 | .none => field_state = .seen, | 374 | try p.warnMsg(.{ |
| 190 | .err, .seen => {}, | 375 | .tag = .unattached_doc_comment, |
| 191 | .end => |tok| { | 376 | .token = tok, |
| 192 | try p.errors.append(p.gpa, .{ | ||
| 193 | .DeclBetweenFields = .{ .token = tok }, | ||
| 194 | }); | 377 | }); |
| 195 | // continue parsing, error will be reported later | ||
| 196 | field_state = .err; | ||
| 197 | }, | ||
| 198 | } | ||
| 199 | |||
| 200 | const field = node.cast(Node.ContainerField).?; | ||
| 201 | field.doc_comments = doc_comments; | ||
| 202 | try list.append(node); | ||
| 203 | const comma = p.eatToken(.Comma) orelse { | ||
| 204 | // try to continue parsing | ||
| 205 | const index = p.tok_i; | ||
| 206 | p.findNextContainerMember(); | ||
| 207 | const next = p.token_ids[p.tok_i]; | ||
| 208 | switch (next) { | ||
| 209 | .Eof => { | ||
| 210 | // no invalid tokens were found | ||
| 211 | if (index == p.tok_i) break; | ||
| 212 | |||
| 213 | // Invalid tokens, add error and exit | ||
| 214 | try p.errors.append(p.gpa, .{ | ||
| 215 | .ExpectedToken = .{ .token = index, .expected_id = .Comma }, | ||
| 216 | }); | ||
| 217 | break; | ||
| 218 | }, | ||
| 219 | else => { | ||
| 220 | if (next == .RBrace) { | ||
| 221 | if (!top_level) break; | ||
| 222 | _ = p.nextToken(); | ||
| 223 | } | ||
| 224 | |||
| 225 | // add error and continue | ||
| 226 | try p.errors.append(p.gpa, .{ | ||
| 227 | .ExpectedToken = .{ .token = index, .expected_id = .Comma }, | ||
| 228 | }); | ||
| 229 | continue; | ||
| 230 | }, | ||
| 231 | } | 378 | } |
| 232 | }; | 379 | break; |
| 233 | if (try p.parseAppendedDocComment(comma)) |appended_comment| | ||
| 234 | field.doc_comments = appended_comment; | ||
| 235 | continue; | ||
| 236 | } | ||
| 237 | |||
| 238 | // Dangling doc comment | ||
| 239 | if (doc_comments != null) { | ||
| 240 | try p.errors.append(p.gpa, .{ | ||
| 241 | .UnattachedDocComment = .{ .token = doc_comments.?.firstToken() }, | ||
| 242 | }); | ||
| 243 | } | ||
| 244 | |||
| 245 | const next = p.token_ids[p.tok_i]; | ||
| 246 | switch (next) { | ||
| 247 | .Eof => break, | ||
| 248 | .Keyword_comptime => { | ||
| 249 | _ = p.nextToken(); | ||
| 250 | try p.errors.append(p.gpa, .{ | ||
| 251 | .ExpectedBlockOrField = .{ .token = p.tok_i }, | ||
| 252 | }); | ||
| 253 | }, | 380 | }, |
| 254 | else => { | 381 | else => { |
| 255 | const index = p.tok_i; | 382 | try p.warn(.expected_container_members); |
| 256 | if (next == .RBrace) { | 383 | // This was likely not supposed to end yet; try to find the next declaration. |
| 257 | if (!top_level) break; | ||
| 258 | _ = p.nextToken(); | ||
| 259 | } | ||
| 260 | |||
| 261 | // this was likely not supposed to end yet, | ||
| 262 | // try to find the next declaration | ||
| 263 | p.findNextContainerMember(); | 384 | p.findNextContainerMember(); |
| 264 | try p.errors.append(p.gpa, .{ | ||
| 265 | .ExpectedContainerMembers = .{ .token = index }, | ||
| 266 | }); | ||
| 267 | }, | 385 | }, |
| 268 | } | 386 | } |
| 269 | } | 387 | } |
| 270 | 388 | ||
| 271 | return list.toOwnedSlice(); | 389 | switch (list.items.len) { |
| 390 | 0 => return Members{ | ||
| 391 | .len = 0, | ||
| 392 | .lhs = 0, | ||
| 393 | .rhs = 0, | ||
| 394 | .trailing = trailing, | ||
| 395 | }, | ||
| 396 | 1 => return Members{ | ||
| 397 | .len = 1, | ||
| 398 | .lhs = list.items[0], | ||
| 399 | .rhs = 0, | ||
| 400 | .trailing = trailing, | ||
| 401 | }, | ||
| 402 | 2 => return Members{ | ||
| 403 | .len = 2, | ||
| 404 | .lhs = list.items[0], | ||
| 405 | .rhs = list.items[1], | ||
| 406 | .trailing = trailing, | ||
| 407 | }, | ||
| 408 | else => { | ||
| 409 | const span = try p.listToSpan(list.items); | ||
| 410 | return Members{ | ||
| 411 | .len = list.items.len, | ||
| 412 | .lhs = span.start, | ||
| 413 | .rhs = span.end, | ||
| 414 | .trailing = trailing, | ||
| 415 | }; | ||
| 416 | }, | ||
| 417 | } | ||
| 272 | } | 418 | } |
| 273 | 419 | ||
| 274 | /// Attempts to find next container member by searching for certain tokens | 420 | /// Attempts to find next container member by searching for certain tokens |
| ... | @@ -276,47 +422,52 @@ const Parser = struct { | ... | @@ -276,47 +422,52 @@ const Parser = struct { |
| 276 | var level: u32 = 0; | 422 | var level: u32 = 0; |
| 277 | while (true) { | 423 | while (true) { |
| 278 | const tok = p.nextToken(); | 424 | const tok = p.nextToken(); |
| 279 | switch (p.token_ids[tok]) { | 425 | switch (p.token_tags[tok]) { |
| 280 | // any of these can start a new top level declaration | 426 | // Any of these can start a new top level declaration. |
| 281 | .Keyword_test, | 427 | .keyword_test, |
| 282 | .Keyword_comptime, | 428 | .keyword_comptime, |
| 283 | .Keyword_pub, | 429 | .keyword_pub, |
| 284 | .Keyword_export, | 430 | .keyword_export, |
| 285 | .Keyword_extern, | 431 | .keyword_extern, |
| 286 | .Keyword_inline, | 432 | .keyword_inline, |
| 287 | .Keyword_noinline, | 433 | .keyword_noinline, |
| 288 | .Keyword_usingnamespace, | 434 | .keyword_usingnamespace, |
| 289 | .Keyword_threadlocal, | 435 | .keyword_threadlocal, |
| 290 | .Keyword_const, | 436 | .keyword_const, |
| 291 | .Keyword_var, | 437 | .keyword_var, |
| 292 | .Keyword_fn, | 438 | .keyword_fn, |
| 293 | .Identifier, | ||
| 294 | => { | 439 | => { |
| 295 | if (level == 0) { | 440 | if (level == 0) { |
| 296 | p.putBackToken(tok); | 441 | p.tok_i -= 1; |
| 442 | return; | ||
| 443 | } | ||
| 444 | }, | ||
| 445 | .identifier => { | ||
| 446 | if (p.token_tags[tok + 1] == .comma and level == 0) { | ||
| 447 | p.tok_i -= 1; | ||
| 297 | return; | 448 | return; |
| 298 | } | 449 | } |
| 299 | }, | 450 | }, |
| 300 | .Comma, .Semicolon => { | 451 | .comma, .semicolon => { |
| 301 | // this decl was likely meant to end here | 452 | // this decl was likely meant to end here |
| 302 | if (level == 0) { | 453 | if (level == 0) { |
| 303 | return; | 454 | return; |
| 304 | } | 455 | } |
| 305 | }, | 456 | }, |
| 306 | .LParen, .LBracket, .LBrace => level += 1, | 457 | .l_paren, .l_bracket, .l_brace => level += 1, |
| 307 | .RParen, .RBracket => { | 458 | .r_paren, .r_bracket => { |
| 308 | if (level != 0) level -= 1; | 459 | if (level != 0) level -= 1; |
| 309 | }, | 460 | }, |
| 310 | .RBrace => { | 461 | .r_brace => { |
| 311 | if (level == 0) { | 462 | if (level == 0) { |
| 312 | // end of container, exit | 463 | // end of container, exit |
| 313 | p.putBackToken(tok); | 464 | p.tok_i -= 1; |
| 314 | return; | 465 | return; |
| 315 | } | 466 | } |
| 316 | level -= 1; | 467 | level -= 1; |
| 317 | }, | 468 | }, |
| 318 | .Eof => { | 469 | .eof => { |
| 319 | p.putBackToken(tok); | 470 | p.tok_i -= 1; |
| 320 | return; | 471 | return; |
| 321 | }, | 472 | }, |
| 322 | else => {}, | 473 | else => {}, |
| ... | @@ -329,22 +480,22 @@ const Parser = struct { | ... | @@ -329,22 +480,22 @@ const Parser = struct { |
| 329 | var level: u32 = 0; | 480 | var level: u32 = 0; |
| 330 | while (true) { | 481 | while (true) { |
| 331 | const tok = p.nextToken(); | 482 | const tok = p.nextToken(); |
| 332 | switch (p.token_ids[tok]) { | 483 | switch (p.token_tags[tok]) { |
| 333 | .LBrace => level += 1, | 484 | .l_brace => level += 1, |
| 334 | .RBrace => { | 485 | .r_brace => { |
| 335 | if (level == 0) { | 486 | if (level == 0) { |
| 336 | p.putBackToken(tok); | 487 | p.tok_i -= 1; |
| 337 | return; | 488 | return; |
| 338 | } | 489 | } |
| 339 | level -= 1; | 490 | level -= 1; |
| 340 | }, | 491 | }, |
| 341 | .Semicolon => { | 492 | .semicolon => { |
| 342 | if (level == 0) { | 493 | if (level == 0) { |
| 343 | return; | 494 | return; |
| 344 | } | 495 | } |
| 345 | }, | 496 | }, |
| 346 | .Eof => { | 497 | .eof => { |
| 347 | p.putBackToken(tok); | 498 | p.tok_i -= 1; |
| 348 | return; | 499 | return; |
| 349 | }, | 500 | }, |
| 350 | else => {}, | 501 | else => {}, |
| ... | @@ -352,335 +503,337 @@ const Parser = struct { | ... | @@ -352,335 +503,337 @@ const Parser = struct { |
| 352 | } | 503 | } |
| 353 | } | 504 | } |
| 354 | 505 | ||
| 355 | /// Eat a multiline container doc comment | 506 | /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE? Block |
| 356 | fn parseContainerDocComments(p: *Parser) !?*Node { | 507 | fn expectTestDecl(p: *Parser) !Node.Index { |
| 357 | if (p.eatToken(.ContainerDocComment)) |first_line| { | 508 | const test_token = p.assertToken(.keyword_test); |
| 358 | while (p.eatToken(.ContainerDocComment)) |_| {} | 509 | const name_token = p.eatToken(.string_literal); |
| 359 | const node = try p.arena.allocator.create(Node.DocComment); | 510 | const block_node = try p.parseBlock(); |
| 360 | node.* = .{ .first_line = first_line }; | 511 | if (block_node == 0) return p.fail(.expected_block); |
| 361 | return &node.base; | 512 | return p.addNode(.{ |
| 362 | } | 513 | .tag = .test_decl, |
| 363 | return null; | 514 | .main_token = test_token, |
| 364 | } | 515 | .data = .{ |
| 365 | 516 | .lhs = name_token orelse 0, | |
| 366 | /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE Block | 517 | .rhs = block_node, |
| 367 | fn parseTestDecl(p: *Parser) !?*Node { | 518 | }, |
| 368 | const test_token = p.eatToken(.Keyword_test) orelse return null; | ||
| 369 | const name_node = try p.parseStringLiteralSingle(); | ||
| 370 | const block_node = (try p.parseBlock(null)) orelse { | ||
| 371 | try p.errors.append(p.gpa, .{ .ExpectedLBrace = .{ .token = p.tok_i } }); | ||
| 372 | return error.ParseError; | ||
| 373 | }; | ||
| 374 | |||
| 375 | const test_node = try p.arena.allocator.create(Node.TestDecl); | ||
| 376 | test_node.* = .{ | ||
| 377 | .doc_comments = null, | ||
| 378 | .test_token = test_token, | ||
| 379 | .name = name_node, | ||
| 380 | .body_node = block_node, | ||
| 381 | }; | ||
| 382 | return &test_node.base; | ||
| 383 | } | ||
| 384 | |||
| 385 | /// TopLevelComptime <- KEYWORD_comptime BlockExpr | ||
| 386 | fn parseTopLevelComptime(p: *Parser) !?*Node { | ||
| 387 | const tok = p.eatToken(.Keyword_comptime) orelse return null; | ||
| 388 | const lbrace = p.eatToken(.LBrace) orelse { | ||
| 389 | p.putBackToken(tok); | ||
| 390 | return null; | ||
| 391 | }; | ||
| 392 | p.putBackToken(lbrace); | ||
| 393 | const block_node = try p.expectNode(parseBlockExpr, .{ | ||
| 394 | .ExpectedLabelOrLBrace = .{ .token = p.tok_i }, | ||
| 395 | }); | 519 | }); |
| 520 | } | ||
| 396 | 521 | ||
| 397 | const comptime_node = try p.arena.allocator.create(Node.Comptime); | 522 | fn expectTestDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index { |
| 398 | comptime_node.* = .{ | 523 | return p.expectTestDecl() catch |err| switch (err) { |
| 399 | .doc_comments = null, | 524 | error.OutOfMemory => return error.OutOfMemory, |
| 400 | .comptime_token = tok, | 525 | error.ParseError => { |
| 401 | .expr = block_node, | 526 | p.findNextContainerMember(); |
| 527 | return null_node; | ||
| 528 | }, | ||
| 402 | }; | 529 | }; |
| 403 | return &comptime_node.base; | ||
| 404 | } | 530 | } |
| 405 | 531 | ||
| 406 | /// TopLevelDecl | 532 | /// TopLevelDecl |
| 407 | /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) | 533 | /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) |
| 408 | /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl | 534 | /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl |
| 409 | /// / KEYWORD_usingnamespace Expr SEMICOLON | 535 | /// / KEYWORD_usingnamespace Expr SEMICOLON |
| 410 | fn parseTopLevelDecl(p: *Parser, doc_comments: ?*Node.DocComment, visib_token: ?TokenIndex) !?*Node { | 536 | fn expectTopLevelDecl(p: *Parser) !Node.Index { |
| 411 | var lib_name: ?*Node = null; | 537 | const extern_export_inline_token = p.nextToken(); |
| 412 | const extern_export_inline_token = blk: { | 538 | var expect_fn: bool = false; |
| 413 | if (p.eatToken(.Keyword_export)) |token| break :blk token; | 539 | var expect_var_or_fn: bool = false; |
| 414 | if (p.eatToken(.Keyword_extern)) |token| { | 540 | switch (p.token_tags[extern_export_inline_token]) { |
| 415 | lib_name = try p.parseStringLiteralSingle(); | 541 | .keyword_extern => { |
| 416 | break :blk token; | 542 | _ = p.eatToken(.string_literal); |
| 543 | expect_var_or_fn = true; | ||
| 544 | }, | ||
| 545 | .keyword_export => expect_var_or_fn = true, | ||
| 546 | .keyword_inline, .keyword_noinline => expect_fn = true, | ||
| 547 | else => p.tok_i -= 1, | ||
| 548 | } | ||
| 549 | const fn_proto = try p.parseFnProto(); | ||
| 550 | if (fn_proto != 0) { | ||
| 551 | switch (p.token_tags[p.tok_i]) { | ||
| 552 | .semicolon => { | ||
| 553 | p.tok_i += 1; | ||
| 554 | return fn_proto; | ||
| 555 | }, | ||
| 556 | .l_brace => { | ||
| 557 | const body_block = try p.parseBlock(); | ||
| 558 | assert(body_block != 0); | ||
| 559 | return p.addNode(.{ | ||
| 560 | .tag = .fn_decl, | ||
| 561 | .main_token = p.nodes.items(.main_token)[fn_proto], | ||
| 562 | .data = .{ | ||
| 563 | .lhs = fn_proto, | ||
| 564 | .rhs = body_block, | ||
| 565 | }, | ||
| 566 | }); | ||
| 567 | }, | ||
| 568 | else => { | ||
| 569 | // Since parseBlock only return error.ParseError on | ||
| 570 | // a missing '}' we can assume this function was | ||
| 571 | // supposed to end here. | ||
| 572 | try p.warn(.expected_semi_or_lbrace); | ||
| 573 | return null_node; | ||
| 574 | }, | ||
| 417 | } | 575 | } |
| 418 | if (p.eatToken(.Keyword_inline)) |token| break :blk token; | ||
| 419 | if (p.eatToken(.Keyword_noinline)) |token| break :blk token; | ||
| 420 | break :blk null; | ||
| 421 | }; | ||
| 422 | |||
| 423 | if (try p.parseFnProto(.top_level, .{ | ||
| 424 | .doc_comments = doc_comments, | ||
| 425 | .visib_token = visib_token, | ||
| 426 | .extern_export_inline_token = extern_export_inline_token, | ||
| 427 | .lib_name = lib_name, | ||
| 428 | })) |node| { | ||
| 429 | return node; | ||
| 430 | } | 576 | } |
| 431 | 577 | if (expect_fn) { | |
| 432 | if (extern_export_inline_token) |token| { | 578 | try p.warn(.expected_fn); |
| 433 | if (p.token_ids[token] == .Keyword_inline or | 579 | return error.ParseError; |
| 434 | p.token_ids[token] == .Keyword_noinline) | ||
| 435 | { | ||
| 436 | try p.errors.append(p.gpa, .{ | ||
| 437 | .ExpectedFn = .{ .token = p.tok_i }, | ||
| 438 | }); | ||
| 439 | return error.ParseError; | ||
| 440 | } | ||
| 441 | } | 580 | } |
| 442 | 581 | ||
| 443 | const thread_local_token = p.eatToken(.Keyword_threadlocal); | 582 | const thread_local_token = p.eatToken(.keyword_threadlocal); |
| 444 | 583 | const var_decl = try p.parseVarDecl(); | |
| 445 | if (try p.parseVarDecl(.{ | 584 | if (var_decl != 0) { |
| 446 | .doc_comments = doc_comments, | 585 | const semicolon_token = try p.expectToken(.semicolon); |
| 447 | .visib_token = visib_token, | 586 | return var_decl; |
| 448 | .thread_local_token = thread_local_token, | ||
| 449 | .extern_export_token = extern_export_inline_token, | ||
| 450 | .lib_name = lib_name, | ||
| 451 | })) |node| { | ||
| 452 | return node; | ||
| 453 | } | 587 | } |
| 454 | |||
| 455 | if (thread_local_token != null) { | 588 | if (thread_local_token != null) { |
| 456 | try p.errors.append(p.gpa, .{ | 589 | return p.fail(.expected_var_decl); |
| 457 | .ExpectedVarDecl = .{ .token = p.tok_i }, | ||
| 458 | }); | ||
| 459 | // ignore this and try again; | ||
| 460 | return error.ParseError; | ||
| 461 | } | 590 | } |
| 462 | 591 | if (expect_var_or_fn) { | |
| 463 | if (extern_export_inline_token) |token| { | 592 | return p.fail(.expected_var_decl_or_fn); |
| 464 | try p.errors.append(p.gpa, .{ | ||
| 465 | .ExpectedVarDeclOrFn = .{ .token = p.tok_i }, | ||
| 466 | }); | ||
| 467 | // ignore this and try again; | ||
| 468 | return error.ParseError; | ||
| 469 | } | 593 | } |
| 594 | if (p.token_tags[p.tok_i] != .keyword_usingnamespace) { | ||
| 595 | return p.fail(.expected_pub_item); | ||
| 596 | } | ||
| 597 | return p.expectUsingNamespace(); | ||
| 598 | } | ||
| 470 | 599 | ||
| 471 | const use_token = p.eatToken(.Keyword_usingnamespace) orelse return null; | 600 | fn expectTopLevelDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index { |
| 472 | const expr = try p.expectNode(parseExpr, .{ | 601 | return p.expectTopLevelDecl() catch |err| switch (err) { |
| 473 | .ExpectedExpr = .{ .token = p.tok_i }, | 602 | error.OutOfMemory => return error.OutOfMemory, |
| 474 | }); | 603 | error.ParseError => { |
| 475 | const semicolon_token = try p.expectToken(.Semicolon); | 604 | p.findNextContainerMember(); |
| 476 | 605 | return null_node; | |
| 477 | const node = try p.arena.allocator.create(Node.Use); | 606 | }, |
| 478 | node.* = .{ | ||
| 479 | .doc_comments = doc_comments orelse try p.parseAppendedDocComment(semicolon_token), | ||
| 480 | .visib_token = visib_token, | ||
| 481 | .use_token = use_token, | ||
| 482 | .expr = expr, | ||
| 483 | .semicolon_token = semicolon_token, | ||
| 484 | }; | 607 | }; |
| 608 | } | ||
| 485 | 609 | ||
| 486 | return &node.base; | 610 | fn expectUsingNamespace(p: *Parser) !Node.Index { |
| 611 | const usingnamespace_token = p.assertToken(.keyword_usingnamespace); | ||
| 612 | const expr = try p.expectExpr(); | ||
| 613 | const semicolon_token = try p.expectToken(.semicolon); | ||
| 614 | return p.addNode(.{ | ||
| 615 | .tag = .@"usingnamespace", | ||
| 616 | .main_token = usingnamespace_token, | ||
| 617 | .data = .{ | ||
| 618 | .lhs = expr, | ||
| 619 | .rhs = undefined, | ||
| 620 | }, | ||
| 621 | }); | ||
| 487 | } | 622 | } |
| 488 | 623 | ||
| 489 | /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr) | 624 | fn expectUsingNamespaceRecoverable(p: *Parser) error{OutOfMemory}!Node.Index { |
| 490 | fn parseFnProto(p: *Parser, level: enum { top_level, as_type }, fields: struct { | 625 | return p.expectUsingNamespace() catch |err| switch (err) { |
| 491 | doc_comments: ?*Node.DocComment = null, | 626 | error.OutOfMemory => return error.OutOfMemory, |
| 492 | visib_token: ?TokenIndex = null, | 627 | error.ParseError => { |
| 493 | extern_export_inline_token: ?TokenIndex = null, | 628 | p.findNextContainerMember(); |
| 494 | lib_name: ?*Node = null, | 629 | return null_node; |
| 495 | }) !?*Node { | 630 | }, |
| 496 | // TODO: Remove once extern/async/inline fn rewriting is | ||
| 497 | var is_extern_prototype: ?void = null; | ||
| 498 | var is_async: ?void = null; | ||
| 499 | var is_inline: ?void = null; | ||
| 500 | if (fields.extern_export_inline_token != null and | ||
| 501 | p.token_ids[fields.extern_export_inline_token.?] == .Keyword_inline) | ||
| 502 | { | ||
| 503 | is_inline = {}; | ||
| 504 | } | ||
| 505 | const cc_token: ?TokenIndex = blk: { | ||
| 506 | if (p.eatToken(.Keyword_extern)) |token| { | ||
| 507 | is_extern_prototype = {}; | ||
| 508 | break :blk token; | ||
| 509 | } | ||
| 510 | if (p.eatToken(.Keyword_async)) |token| { | ||
| 511 | is_async = {}; | ||
| 512 | break :blk token; | ||
| 513 | } | ||
| 514 | break :blk null; | ||
| 515 | }; | ||
| 516 | const fn_token = p.eatToken(.Keyword_fn) orelse { | ||
| 517 | if (cc_token) |token| | ||
| 518 | p.putBackToken(token); | ||
| 519 | return null; | ||
| 520 | }; | 631 | }; |
| 521 | const name_token = p.eatToken(.Identifier); | 632 | } |
| 522 | const lparen = try p.expectToken(.LParen); | 633 | |
| 634 | /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr) | ||
| 635 | fn parseFnProto(p: *Parser) !Node.Index { | ||
| 636 | const fn_token = p.eatToken(.keyword_fn) orelse return null_node; | ||
| 637 | _ = p.eatToken(.identifier); | ||
| 523 | const params = try p.parseParamDeclList(); | 638 | const params = try p.parseParamDeclList(); |
| 524 | defer p.gpa.free(params); | 639 | defer params.deinit(p.gpa); |
| 525 | const var_args_token = p.eatToken(.Ellipsis3); | ||
| 526 | const rparen = try p.expectToken(.RParen); | ||
| 527 | const align_expr = try p.parseByteAlign(); | 640 | const align_expr = try p.parseByteAlign(); |
| 528 | const section_expr = try p.parseLinkSection(); | 641 | const section_expr = try p.parseLinkSection(); |
| 529 | const callconv_expr = try p.parseCallconv(); | 642 | const callconv_expr = try p.parseCallconv(); |
| 530 | const exclamation_token = p.eatToken(.Bang); | 643 | const bang_token = p.eatToken(.bang); |
| 531 | 644 | ||
| 532 | const return_type_expr = (try p.parseAnyType()) orelse | 645 | const return_type_expr = try p.parseTypeExpr(); |
| 533 | try p.expectNodeRecoverable(parseTypeExpr, .{ | 646 | if (return_type_expr == 0) { |
| 534 | // most likely the user forgot to specify the return type. | 647 | // most likely the user forgot to specify the return type. |
| 535 | // Mark return type as invalid and try to continue. | 648 | // Mark return type as invalid and try to continue. |
| 536 | .ExpectedReturnType = .{ .token = p.tok_i }, | 649 | try p.warn(.expected_return_type); |
| 537 | }); | 650 | } |
| 538 | 651 | ||
| 539 | // TODO https://github.com/ziglang/zig/issues/3750 | 652 | if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) { |
| 540 | const R = Node.FnProto.ReturnType; | 653 | switch (params) { |
| 541 | const return_type = if (return_type_expr == null) | 654 | .zero_or_one => |param| return p.addNode(.{ |
| 542 | R{ .Invalid = rparen } | 655 | .tag = .fn_proto_simple, |
| 543 | else if (exclamation_token != null) | 656 | .main_token = fn_token, |
| 544 | R{ .InferErrorSet = return_type_expr.? } | 657 | .data = .{ |
| 545 | else | 658 | .lhs = param, |
| 546 | R{ .Explicit = return_type_expr.? }; | 659 | .rhs = return_type_expr, |
| 547 | 660 | }, | |
| 548 | const body_node: ?*Node = switch (level) { | 661 | }), |
| 549 | .top_level => blk: { | 662 | .multi => |list| { |
| 550 | if (p.eatToken(.Semicolon)) |_| { | 663 | const span = try p.listToSpan(list); |
| 551 | break :blk null; | 664 | return p.addNode(.{ |
| 552 | } | 665 | .tag = .fn_proto_multi, |
| 553 | const body_block = (try p.parseBlock(null)) orelse { | 666 | .main_token = fn_token, |
| 554 | // Since parseBlock only return error.ParseError on | 667 | .data = .{ |
| 555 | // a missing '}' we can assume this function was | 668 | .lhs = try p.addExtra(Node.SubRange{ |
| 556 | // supposed to end here. | 669 | .start = span.start, |
| 557 | try p.errors.append(p.gpa, .{ .ExpectedSemiOrLBrace = .{ .token = p.tok_i } }); | 670 | .end = span.end, |
| 558 | break :blk null; | 671 | }), |
| 559 | }; | 672 | .rhs = return_type_expr, |
| 560 | break :blk body_block; | 673 | }, |
| 674 | }); | ||
| 675 | }, | ||
| 676 | } | ||
| 677 | } | ||
| 678 | switch (params) { | ||
| 679 | .zero_or_one => |param| return p.addNode(.{ | ||
| 680 | .tag = .fn_proto_one, | ||
| 681 | .main_token = fn_token, | ||
| 682 | .data = .{ | ||
| 683 | .lhs = try p.addExtra(Node.FnProtoOne{ | ||
| 684 | .param = param, | ||
| 685 | .align_expr = align_expr, | ||
| 686 | .section_expr = section_expr, | ||
| 687 | .callconv_expr = callconv_expr, | ||
| 688 | }), | ||
| 689 | .rhs = return_type_expr, | ||
| 690 | }, | ||
| 691 | }), | ||
| 692 | .multi => |list| { | ||
| 693 | const span = try p.listToSpan(list); | ||
| 694 | return p.addNode(.{ | ||
| 695 | .tag = .fn_proto, | ||
| 696 | .main_token = fn_token, | ||
| 697 | .data = .{ | ||
| 698 | .lhs = try p.addExtra(Node.FnProto{ | ||
| 699 | .params_start = span.start, | ||
| 700 | .params_end = span.end, | ||
| 701 | .align_expr = align_expr, | ||
| 702 | .section_expr = section_expr, | ||
| 703 | .callconv_expr = callconv_expr, | ||
| 704 | }), | ||
| 705 | .rhs = return_type_expr, | ||
| 706 | }, | ||
| 707 | }); | ||
| 561 | }, | 708 | }, |
| 562 | .as_type => null, | 709 | } |
| 563 | }; | ||
| 564 | |||
| 565 | const fn_proto_node = try Node.FnProto.create(&p.arena.allocator, .{ | ||
| 566 | .params_len = params.len, | ||
| 567 | .fn_token = fn_token, | ||
| 568 | .return_type = return_type, | ||
| 569 | }, .{ | ||
| 570 | .doc_comments = fields.doc_comments, | ||
| 571 | .visib_token = fields.visib_token, | ||
| 572 | .name_token = name_token, | ||
| 573 | .var_args_token = var_args_token, | ||
| 574 | .extern_export_inline_token = fields.extern_export_inline_token, | ||
| 575 | .body_node = body_node, | ||
| 576 | .lib_name = fields.lib_name, | ||
| 577 | .align_expr = align_expr, | ||
| 578 | .section_expr = section_expr, | ||
| 579 | .callconv_expr = callconv_expr, | ||
| 580 | .is_extern_prototype = is_extern_prototype, | ||
| 581 | .is_async = is_async, | ||
| 582 | .is_inline = is_inline, | ||
| 583 | }); | ||
| 584 | std.mem.copy(Node.FnProto.ParamDecl, fn_proto_node.params(), params); | ||
| 585 | |||
| 586 | return &fn_proto_node.base; | ||
| 587 | } | 710 | } |
| 588 | 711 | ||
| 589 | /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON | 712 | /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON |
| 590 | fn parseVarDecl(p: *Parser, fields: struct { | 713 | fn parseVarDecl(p: *Parser) !Node.Index { |
| 591 | doc_comments: ?*Node.DocComment = null, | 714 | const mut_token = p.eatToken(.keyword_const) orelse |
| 592 | visib_token: ?TokenIndex = null, | 715 | p.eatToken(.keyword_var) orelse |
| 593 | thread_local_token: ?TokenIndex = null, | 716 | return null_node; |
| 594 | extern_export_token: ?TokenIndex = null, | ||
| 595 | lib_name: ?*Node = null, | ||
| 596 | comptime_token: ?TokenIndex = null, | ||
| 597 | }) !?*Node { | ||
| 598 | const mut_token = p.eatToken(.Keyword_const) orelse | ||
| 599 | p.eatToken(.Keyword_var) orelse | ||
| 600 | return null; | ||
| 601 | 717 | ||
| 602 | const name_token = try p.expectToken(.Identifier); | 718 | _ = try p.expectToken(.identifier); |
| 603 | const type_node = if (p.eatToken(.Colon) != null) | 719 | const type_node: Node.Index = if (p.eatToken(.colon) == null) 0 else try p.expectTypeExpr(); |
| 604 | try p.expectNode(parseTypeExpr, .{ | ||
| 605 | .ExpectedTypeExpr = .{ .token = p.tok_i }, | ||
| 606 | }) | ||
| 607 | else | ||
| 608 | null; | ||
| 609 | const align_node = try p.parseByteAlign(); | 720 | const align_node = try p.parseByteAlign(); |
| 610 | const section_node = try p.parseLinkSection(); | 721 | const section_node = try p.parseLinkSection(); |
| 611 | const eq_token = p.eatToken(.Equal); | 722 | const init_node: Node.Index = if (p.eatToken(.equal) == null) 0 else try p.expectExpr(); |
| 612 | const init_node = if (eq_token != null) blk: { | 723 | if (section_node == 0) { |
| 613 | break :blk try p.expectNode(parseExpr, .{ | 724 | if (align_node == 0) { |
| 614 | .ExpectedExpr = .{ .token = p.tok_i }, | 725 | return p.addNode(.{ |
| 726 | .tag = .simple_var_decl, | ||
| 727 | .main_token = mut_token, | ||
| 728 | .data = .{ | ||
| 729 | .lhs = type_node, | ||
| 730 | .rhs = init_node, | ||
| 731 | }, | ||
| 732 | }); | ||
| 733 | } else if (type_node == 0) { | ||
| 734 | return p.addNode(.{ | ||
| 735 | .tag = .aligned_var_decl, | ||
| 736 | .main_token = mut_token, | ||
| 737 | .data = .{ | ||
| 738 | .lhs = align_node, | ||
| 739 | .rhs = init_node, | ||
| 740 | }, | ||
| 741 | }); | ||
| 742 | } else { | ||
| 743 | return p.addNode(.{ | ||
| 744 | .tag = .local_var_decl, | ||
| 745 | .main_token = mut_token, | ||
| 746 | .data = .{ | ||
| 747 | .lhs = try p.addExtra(Node.LocalVarDecl{ | ||
| 748 | .type_node = type_node, | ||
| 749 | .align_node = align_node, | ||
| 750 | }), | ||
| 751 | .rhs = init_node, | ||
| 752 | }, | ||
| 753 | }); | ||
| 754 | } | ||
| 755 | } else { | ||
| 756 | return p.addNode(.{ | ||
| 757 | .tag = .global_var_decl, | ||
| 758 | .main_token = mut_token, | ||
| 759 | .data = .{ | ||
| 760 | .lhs = try p.addExtra(Node.GlobalVarDecl{ | ||
| 761 | .type_node = type_node, | ||
| 762 | .align_node = align_node, | ||
| 763 | .section_node = section_node, | ||
| 764 | }), | ||
| 765 | .rhs = init_node, | ||
| 766 | }, | ||
| 615 | }); | 767 | }); |
| 616 | } else null; | 768 | } |
| 617 | const semicolon_token = try p.expectToken(.Semicolon); | ||
| 618 | |||
| 619 | const doc_comments = fields.doc_comments orelse try p.parseAppendedDocComment(semicolon_token); | ||
| 620 | |||
| 621 | const node = try Node.VarDecl.create(&p.arena.allocator, .{ | ||
| 622 | .mut_token = mut_token, | ||
| 623 | .name_token = name_token, | ||
| 624 | .semicolon_token = semicolon_token, | ||
| 625 | }, .{ | ||
| 626 | .doc_comments = doc_comments, | ||
| 627 | .visib_token = fields.visib_token, | ||
| 628 | .thread_local_token = fields.thread_local_token, | ||
| 629 | .eq_token = eq_token, | ||
| 630 | .comptime_token = fields.comptime_token, | ||
| 631 | .extern_export_token = fields.extern_export_token, | ||
| 632 | .lib_name = fields.lib_name, | ||
| 633 | .type_node = type_node, | ||
| 634 | .align_node = align_node, | ||
| 635 | .section_node = section_node, | ||
| 636 | .init_node = init_node, | ||
| 637 | }); | ||
| 638 | return &node.base; | ||
| 639 | } | 769 | } |
| 640 | 770 | ||
| 641 | /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? | 771 | /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? |
| 642 | fn parseContainerField(p: *Parser) !?*Node { | 772 | fn expectContainerField(p: *Parser) !Node.Index { |
| 643 | const comptime_token = p.eatToken(.Keyword_comptime); | 773 | const comptime_token = p.eatToken(.keyword_comptime); |
| 644 | const name_token = p.eatToken(.Identifier) orelse { | 774 | const name_token = p.assertToken(.identifier); |
| 645 | if (comptime_token) |t| p.putBackToken(t); | 775 | |
| 646 | return null; | 776 | var align_expr: Node.Index = 0; |
| 647 | }; | 777 | var type_expr: Node.Index = 0; |
| 648 | 778 | if (p.eatToken(.colon)) |_| { | |
| 649 | var align_expr: ?*Node = null; | 779 | if (p.eatToken(.keyword_anytype)) |anytype_tok| { |
| 650 | var type_expr: ?*Node = null; | 780 | type_expr = try p.addNode(.{ |
| 651 | if (p.eatToken(.Colon)) |_| { | 781 | .tag = .@"anytype", |
| 652 | if (p.eatToken(.Keyword_anytype) orelse p.eatToken(.Keyword_var)) |anytype_tok| { | 782 | .main_token = anytype_tok, |
| 653 | const node = try p.arena.allocator.create(Node.OneToken); | 783 | .data = .{ |
| 654 | node.* = .{ | 784 | .lhs = undefined, |
| 655 | .base = .{ .tag = .AnyType }, | 785 | .rhs = undefined, |
| 656 | .token = anytype_tok, | 786 | }, |
| 657 | }; | ||
| 658 | type_expr = &node.base; | ||
| 659 | } else { | ||
| 660 | type_expr = try p.expectNode(parseTypeExpr, .{ | ||
| 661 | .ExpectedTypeExpr = .{ .token = p.tok_i }, | ||
| 662 | }); | 787 | }); |
| 788 | } else { | ||
| 789 | type_expr = try p.expectTypeExpr(); | ||
| 663 | align_expr = try p.parseByteAlign(); | 790 | align_expr = try p.parseByteAlign(); |
| 664 | } | 791 | } |
| 665 | } | 792 | } |
| 666 | 793 | ||
| 667 | const value_expr = if (p.eatToken(.Equal)) |_| | 794 | const value_expr: Node.Index = if (p.eatToken(.equal) == null) 0 else try p.expectExpr(); |
| 668 | try p.expectNode(parseExpr, .{ | 795 | |
| 669 | .ExpectedExpr = .{ .token = p.tok_i }, | 796 | if (align_expr == 0) { |
| 670 | }) | 797 | return p.addNode(.{ |
| 671 | else | 798 | .tag = .container_field_init, |
| 672 | null; | 799 | .main_token = name_token, |
| 673 | 800 | .data = .{ | |
| 674 | const node = try p.arena.allocator.create(Node.ContainerField); | 801 | .lhs = type_expr, |
| 675 | node.* = .{ | 802 | .rhs = value_expr, |
| 676 | .doc_comments = null, | 803 | }, |
| 677 | .comptime_token = comptime_token, | 804 | }); |
| 678 | .name_token = name_token, | 805 | } else if (value_expr == 0) { |
| 679 | .type_expr = type_expr, | 806 | return p.addNode(.{ |
| 680 | .value_expr = value_expr, | 807 | .tag = .container_field_align, |
| 681 | .align_expr = align_expr, | 808 | .main_token = name_token, |
| 809 | .data = .{ | ||
| 810 | .lhs = type_expr, | ||
| 811 | .rhs = align_expr, | ||
| 812 | }, | ||
| 813 | }); | ||
| 814 | } else { | ||
| 815 | return p.addNode(.{ | ||
| 816 | .tag = .container_field, | ||
| 817 | .main_token = name_token, | ||
| 818 | .data = .{ | ||
| 819 | .lhs = type_expr, | ||
| 820 | .rhs = try p.addExtra(Node.ContainerField{ | ||
| 821 | .value_expr = value_expr, | ||
| 822 | .align_expr = align_expr, | ||
| 823 | }), | ||
| 824 | }, | ||
| 825 | }); | ||
| 826 | } | ||
| 827 | } | ||
| 828 | |||
| 829 | fn expectContainerFieldRecoverable(p: *Parser) error{OutOfMemory}!Node.Index { | ||
| 830 | return p.expectContainerField() catch |err| switch (err) { | ||
| 831 | error.OutOfMemory => return error.OutOfMemory, | ||
| 832 | error.ParseError => { | ||
| 833 | p.findNextContainerMember(); | ||
| 834 | return null_node; | ||
| 835 | }, | ||
| 682 | }; | 836 | }; |
| 683 | return &node.base; | ||
| 684 | } | 837 | } |
| 685 | 838 | ||
| 686 | /// Statement | 839 | /// Statement |
| ... | @@ -694,833 +847,1632 @@ const Parser = struct { | ... | @@ -694,833 +847,1632 @@ const Parser = struct { |
| 694 | /// / LabeledStatement | 847 | /// / LabeledStatement |
| 695 | /// / SwitchExpr | 848 | /// / SwitchExpr |
| 696 | /// / AssignExpr SEMICOLON | 849 | /// / AssignExpr SEMICOLON |
| 697 | fn parseStatement(p: *Parser) Error!?*Node { | 850 | fn parseStatement(p: *Parser) Error!Node.Index { |
| 698 | const comptime_token = p.eatToken(.Keyword_comptime); | 851 | const comptime_token = p.eatToken(.keyword_comptime); |
| 699 | 852 | ||
| 700 | if (try p.parseVarDecl(.{ | 853 | const var_decl = try p.parseVarDecl(); |
| 701 | .comptime_token = comptime_token, | 854 | if (var_decl != 0) { |
| 702 | })) |node| { | 855 | _ = try p.expectTokenRecoverable(.semicolon); |
| 703 | return node; | 856 | return var_decl; |
| 704 | } | 857 | } |
| 705 | 858 | ||
| 706 | if (comptime_token) |token| { | 859 | if (comptime_token) |token| { |
| 707 | const block_expr = try p.expectNode(parseBlockExprStatement, .{ | 860 | return p.addNode(.{ |
| 708 | .ExpectedBlockOrAssignment = .{ .token = p.tok_i }, | 861 | .tag = .@"comptime", |
| 709 | }); | 862 | .main_token = token, |
| 710 | 863 | .data = .{ | |
| 711 | const node = try p.arena.allocator.create(Node.Comptime); | 864 | .lhs = try p.expectBlockExprStatement(), |
| 712 | node.* = .{ | 865 | .rhs = undefined, |
| 713 | .doc_comments = null, | 866 | }, |
| 714 | .comptime_token = token, | ||
| 715 | .expr = block_expr, | ||
| 716 | }; | ||
| 717 | return &node.base; | ||
| 718 | } | ||
| 719 | |||
| 720 | if (p.eatToken(.Keyword_nosuspend)) |nosuspend_token| { | ||
| 721 | const block_expr = try p.expectNode(parseBlockExprStatement, .{ | ||
| 722 | .ExpectedBlockOrAssignment = .{ .token = p.tok_i }, | ||
| 723 | }); | 867 | }); |
| 724 | |||
| 725 | const node = try p.arena.allocator.create(Node.Nosuspend); | ||
| 726 | node.* = .{ | ||
| 727 | .nosuspend_token = nosuspend_token, | ||
| 728 | .expr = block_expr, | ||
| 729 | }; | ||
| 730 | return &node.base; | ||
| 731 | } | 868 | } |
| 732 | 869 | ||
| 733 | if (p.eatToken(.Keyword_suspend)) |suspend_token| { | 870 | switch (p.token_tags[p.tok_i]) { |
| 734 | const semicolon = p.eatToken(.Semicolon); | 871 | .keyword_nosuspend => { |
| 735 | 872 | return p.addNode(.{ | |
| 736 | const body_node = if (semicolon == null) blk: { | 873 | .tag = .@"nosuspend", |
| 737 | break :blk try p.expectNode(parseBlockExprStatement, .{ | 874 | .main_token = p.nextToken(), |
| 738 | .ExpectedBlockOrExpression = .{ .token = p.tok_i }, | 875 | .data = .{ |
| 876 | .lhs = try p.expectBlockExprStatement(), | ||
| 877 | .rhs = undefined, | ||
| 878 | }, | ||
| 739 | }); | 879 | }); |
| 740 | } else null; | 880 | }, |
| 741 | 881 | .keyword_suspend => { | |
| 742 | const node = try p.arena.allocator.create(Node.Suspend); | 882 | const token = p.nextToken(); |
| 743 | node.* = .{ | 883 | const block_expr: Node.Index = if (p.eatToken(.semicolon) != null) |
| 744 | .suspend_token = suspend_token, | 884 | 0 |
| 745 | .body = body_node, | 885 | else |
| 746 | }; | 886 | try p.expectBlockExprStatement(); |
| 747 | return &node.base; | 887 | return p.addNode(.{ |
| 888 | .tag = .@"suspend", | ||
| 889 | .main_token = token, | ||
| 890 | .data = .{ | ||
| 891 | .lhs = block_expr, | ||
| 892 | .rhs = undefined, | ||
| 893 | }, | ||
| 894 | }); | ||
| 895 | }, | ||
| 896 | .keyword_defer => return p.addNode(.{ | ||
| 897 | .tag = .@"defer", | ||
| 898 | .main_token = p.nextToken(), | ||
| 899 | .data = .{ | ||
| 900 | .lhs = undefined, | ||
| 901 | .rhs = try p.expectBlockExprStatement(), | ||
| 902 | }, | ||
| 903 | }), | ||
| 904 | .keyword_errdefer => return p.addNode(.{ | ||
| 905 | .tag = .@"errdefer", | ||
| 906 | .main_token = p.nextToken(), | ||
| 907 | .data = .{ | ||
| 908 | .lhs = try p.parsePayload(), | ||
| 909 | .rhs = try p.expectBlockExprStatement(), | ||
| 910 | }, | ||
| 911 | }), | ||
| 912 | .keyword_switch => return p.expectSwitchExpr(), | ||
| 913 | .keyword_if => return p.expectIfStatement(), | ||
| 914 | else => {}, | ||
| 748 | } | 915 | } |
| 749 | 916 | ||
| 750 | const defer_token = p.eatToken(.Keyword_defer) orelse p.eatToken(.Keyword_errdefer); | 917 | const labeled_statement = try p.parseLabeledStatement(); |
| 751 | if (defer_token) |token| { | 918 | if (labeled_statement != 0) return labeled_statement; |
| 752 | const payload = if (p.token_ids[token] == .Keyword_errdefer) | ||
| 753 | try p.parsePayload() | ||
| 754 | else | ||
| 755 | null; | ||
| 756 | const expr_node = try p.expectNode(parseBlockExprStatement, .{ | ||
| 757 | .ExpectedBlockOrExpression = .{ .token = p.tok_i }, | ||
| 758 | }); | ||
| 759 | const node = try p.arena.allocator.create(Node.Defer); | ||
| 760 | node.* = .{ | ||
| 761 | .defer_token = token, | ||
| 762 | .expr = expr_node, | ||
| 763 | .payload = payload, | ||
| 764 | }; | ||
| 765 | return &node.base; | ||
| 766 | } | ||
| 767 | 919 | ||
| 768 | if (try p.parseIfStatement()) |node| return node; | 920 | const assign_expr = try p.parseAssignExpr(); |
| 769 | if (try p.parseLabeledStatement()) |node| return node; | 921 | if (assign_expr != 0) { |
| 770 | if (try p.parseSwitchExpr()) |node| return node; | 922 | _ = try p.expectTokenRecoverable(.semicolon); |
| 771 | if (try p.parseAssignExpr()) |node| { | 923 | return assign_expr; |
| 772 | _ = try p.expectTokenRecoverable(.Semicolon); | ||
| 773 | return node; | ||
| 774 | } | 924 | } |
| 775 | 925 | ||
| 776 | return null; | 926 | return null_node; |
| 777 | } | 927 | } |
| 778 | 928 | ||
| 779 | /// IfStatement | 929 | fn expectStatement(p: *Parser) !Node.Index { |
| 780 | /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? | 930 | const statement = try p.parseStatement(); |
| 781 | /// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) | 931 | if (statement == 0) { |
| 782 | fn parseIfStatement(p: *Parser) !?*Node { | 932 | return p.fail(.expected_statement); |
| 783 | const if_node = (try p.parseIfPrefix()) orelse return null; | 933 | } |
| 784 | const if_prefix = if_node.cast(Node.If).?; | 934 | return statement; |
| 785 | 935 | } | |
| 786 | const block_expr = (try p.parseBlockExpr()); | ||
| 787 | const assign_expr = if (block_expr == null) | ||
| 788 | try p.expectNode(parseAssignExpr, .{ | ||
| 789 | .ExpectedBlockOrAssignment = .{ .token = p.tok_i }, | ||
| 790 | }) | ||
| 791 | else | ||
| 792 | null; | ||
| 793 | |||
| 794 | const semicolon = if (assign_expr != null) p.eatToken(.Semicolon) else null; | ||
| 795 | |||
| 796 | const else_node = if (semicolon == null) blk: { | ||
| 797 | const else_token = p.eatToken(.Keyword_else) orelse break :blk null; | ||
| 798 | const payload = try p.parsePayload(); | ||
| 799 | const else_body = try p.expectNode(parseStatement, .{ | ||
| 800 | .InvalidToken = .{ .token = p.tok_i }, | ||
| 801 | }); | ||
| 802 | 936 | ||
| 803 | const node = try p.arena.allocator.create(Node.Else); | 937 | /// If a parse error occurs, reports an error, but then finds the next statement |
| 804 | node.* = .{ | 938 | /// and returns that one instead. If a parse error occurs but there is no following |
| 805 | .else_token = else_token, | 939 | /// statement, returns 0. |
| 806 | .payload = payload, | 940 | fn expectStatementRecoverable(p: *Parser) error{OutOfMemory}!Node.Index { |
| 807 | .body = else_body, | 941 | while (true) { |
| 942 | return p.expectStatement() catch |err| switch (err) { | ||
| 943 | error.OutOfMemory => return error.OutOfMemory, | ||
| 944 | error.ParseError => { | ||
| 945 | p.findNextStmt(); // Try to skip to the next statement. | ||
| 946 | if (p.token_tags[p.tok_i] == .r_brace) return null_node; | ||
| 947 | continue; | ||
| 948 | }, | ||
| 808 | }; | 949 | }; |
| 809 | |||
| 810 | break :blk node; | ||
| 811 | } else null; | ||
| 812 | |||
| 813 | if (block_expr) |body| { | ||
| 814 | if_prefix.body = body; | ||
| 815 | if_prefix.@"else" = else_node; | ||
| 816 | return if_node; | ||
| 817 | } | 950 | } |
| 951 | } | ||
| 818 | 952 | ||
| 819 | if (assign_expr) |body| { | 953 | /// IfStatement |
| 820 | if_prefix.body = body; | 954 | /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? |
| 821 | if (semicolon != null) return if_node; | 955 | /// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) |
| 822 | if (else_node != null) { | 956 | fn expectIfStatement(p: *Parser) !Node.Index { |
| 823 | if_prefix.@"else" = else_node; | 957 | const if_token = p.assertToken(.keyword_if); |
| 824 | return if_node; | 958 | _ = try p.expectToken(.l_paren); |
| 959 | const condition = try p.expectExpr(); | ||
| 960 | _ = try p.expectToken(.r_paren); | ||
| 961 | const then_payload = try p.parsePtrPayload(); | ||
| 962 | |||
| 963 | // TODO propose to change the syntax so that semicolons are always required | ||
| 964 | // inside if statements, even if there is an `else`. | ||
| 965 | var else_required = false; | ||
| 966 | const then_expr = blk: { | ||
| 967 | const block_expr = try p.parseBlockExpr(); | ||
| 968 | if (block_expr != 0) break :blk block_expr; | ||
| 969 | const assign_expr = try p.parseAssignExpr(); | ||
| 970 | if (assign_expr == 0) { | ||
| 971 | return p.fail(.expected_block_or_assignment); | ||
| 825 | } | 972 | } |
| 826 | try p.errors.append(p.gpa, .{ | 973 | if (p.eatToken(.semicolon)) |_| { |
| 827 | .ExpectedSemiOrElse = .{ .token = p.tok_i }, | 974 | return p.addNode(.{ |
| 975 | .tag = .if_simple, | ||
| 976 | .main_token = if_token, | ||
| 977 | .data = .{ | ||
| 978 | .lhs = condition, | ||
| 979 | .rhs = assign_expr, | ||
| 980 | }, | ||
| 981 | }); | ||
| 982 | } | ||
| 983 | else_required = true; | ||
| 984 | break :blk assign_expr; | ||
| 985 | }; | ||
| 986 | const else_token = p.eatToken(.keyword_else) orelse { | ||
| 987 | if (else_required) { | ||
| 988 | try p.warn(.expected_semi_or_else); | ||
| 989 | } | ||
| 990 | return p.addNode(.{ | ||
| 991 | .tag = .if_simple, | ||
| 992 | .main_token = if_token, | ||
| 993 | .data = .{ | ||
| 994 | .lhs = condition, | ||
| 995 | .rhs = then_expr, | ||
| 996 | }, | ||
| 828 | }); | 997 | }); |
| 829 | } | 998 | }; |
| 830 | 999 | const else_payload = try p.parsePayload(); | |
| 831 | return if_node; | 1000 | const else_expr = try p.expectStatement(); |
| 1001 | return p.addNode(.{ | ||
| 1002 | .tag = .@"if", | ||
| 1003 | .main_token = if_token, | ||
| 1004 | .data = .{ | ||
| 1005 | .lhs = condition, | ||
| 1006 | .rhs = try p.addExtra(Node.If{ | ||
| 1007 | .then_expr = then_expr, | ||
| 1008 | .else_expr = else_expr, | ||
| 1009 | }), | ||
| 1010 | }, | ||
| 1011 | }); | ||
| 832 | } | 1012 | } |
| 833 | 1013 | ||
| 834 | /// LabeledStatement <- BlockLabel? (Block / LoopStatement) | 1014 | /// LabeledStatement <- BlockLabel? (Block / LoopStatement) |
| 835 | fn parseLabeledStatement(p: *Parser) !?*Node { | 1015 | fn parseLabeledStatement(p: *Parser) !Node.Index { |
| 836 | var colon: TokenIndex = undefined; | 1016 | const label_token = p.parseBlockLabel(); |
| 837 | const label_token = p.parseBlockLabel(&colon); | 1017 | const block = try p.parseBlock(); |
| 838 | 1018 | if (block != 0) return block; | |
| 839 | if (try p.parseBlock(label_token)) |node| return node; | ||
| 840 | |||
| 841 | if (try p.parseLoopStatement()) |node| { | ||
| 842 | if (node.cast(Node.For)) |for_node| { | ||
| 843 | for_node.label = label_token; | ||
| 844 | } else if (node.cast(Node.While)) |while_node| { | ||
| 845 | while_node.label = label_token; | ||
| 846 | } else unreachable; | ||
| 847 | return node; | ||
| 848 | } | ||
| 849 | 1019 | ||
| 850 | if (label_token != null) { | 1020 | const loop_stmt = try p.parseLoopStatement(); |
| 851 | try p.errors.append(p.gpa, .{ | 1021 | if (loop_stmt != 0) return loop_stmt; |
| 852 | .ExpectedLabelable = .{ .token = p.tok_i }, | 1022 | |
| 853 | }); | 1023 | if (label_token != 0) { |
| 854 | return error.ParseError; | 1024 | return p.fail(.expected_labelable); |
| 855 | } | 1025 | } |
| 856 | 1026 | ||
| 857 | return null; | 1027 | return null_node; |
| 858 | } | 1028 | } |
| 859 | 1029 | ||
| 860 | /// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) | 1030 | /// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) |
| 861 | fn parseLoopStatement(p: *Parser) !?*Node { | 1031 | fn parseLoopStatement(p: *Parser) !Node.Index { |
| 862 | const inline_token = p.eatToken(.Keyword_inline); | 1032 | const inline_token = p.eatToken(.keyword_inline); |
| 863 | 1033 | ||
| 864 | if (try p.parseForStatement()) |node| { | 1034 | const for_statement = try p.parseForStatement(); |
| 865 | node.cast(Node.For).?.inline_token = inline_token; | 1035 | if (for_statement != 0) return for_statement; |
| 866 | return node; | ||
| 867 | } | ||
| 868 | 1036 | ||
| 869 | if (try p.parseWhileStatement()) |node| { | 1037 | const while_statement = try p.parseWhileStatement(); |
| 870 | node.cast(Node.While).?.inline_token = inline_token; | 1038 | if (while_statement != 0) return while_statement; |
| 871 | return node; | 1039 | |
| 872 | } | 1040 | if (inline_token == null) return null_node; |
| 873 | if (inline_token == null) return null; | ||
| 874 | 1041 | ||
| 875 | // If we've seen "inline", there should have been a "for" or "while" | 1042 | // If we've seen "inline", there should have been a "for" or "while" |
| 876 | try p.errors.append(p.gpa, .{ | 1043 | return p.fail(.expected_inlinable); |
| 877 | .ExpectedInlinable = .{ .token = p.tok_i }, | ||
| 878 | }); | ||
| 879 | return error.ParseError; | ||
| 880 | } | 1044 | } |
| 881 | 1045 | ||
| 1046 | /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload | ||
| 882 | /// ForStatement | 1047 | /// ForStatement |
| 883 | /// <- ForPrefix BlockExpr ( KEYWORD_else Statement )? | 1048 | /// <- ForPrefix BlockExpr ( KEYWORD_else Statement )? |
| 884 | /// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) | 1049 | /// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) |
| 885 | fn parseForStatement(p: *Parser) !?*Node { | 1050 | fn parseForStatement(p: *Parser) !Node.Index { |
| 886 | const node = (try p.parseForPrefix()) orelse return null; | 1051 | const for_token = p.eatToken(.keyword_for) orelse return null_node; |
| 887 | const for_prefix = node.cast(Node.For).?; | 1052 | _ = try p.expectToken(.l_paren); |
| 888 | 1053 | const array_expr = try p.expectExpr(); | |
| 889 | if (try p.parseBlockExpr()) |block_expr_node| { | 1054 | _ = try p.expectToken(.r_paren); |
| 890 | for_prefix.body = block_expr_node; | 1055 | const found_payload = try p.parsePtrIndexPayload(); |
| 891 | 1056 | if (found_payload == 0) try p.warn(.expected_loop_payload); | |
| 892 | if (p.eatToken(.Keyword_else)) |else_token| { | 1057 | |
| 893 | const statement_node = try p.expectNode(parseStatement, .{ | 1058 | // TODO propose to change the syntax so that semicolons are always required |
| 894 | .InvalidToken = .{ .token = p.tok_i }, | 1059 | // inside while statements, even if there is an `else`. |
| 1060 | var else_required = false; | ||
| 1061 | const then_expr = blk: { | ||
| 1062 | const block_expr = try p.parseBlockExpr(); | ||
| 1063 | if (block_expr != 0) break :blk block_expr; | ||
| 1064 | const assign_expr = try p.parseAssignExpr(); | ||
| 1065 | if (assign_expr == 0) { | ||
| 1066 | return p.fail(.expected_block_or_assignment); | ||
| 1067 | } | ||
| 1068 | if (p.eatToken(.semicolon)) |_| { | ||
| 1069 | return p.addNode(.{ | ||
| 1070 | .tag = .for_simple, | ||
| 1071 | .main_token = for_token, | ||
| 1072 | .data = .{ | ||
| 1073 | .lhs = array_expr, | ||
| 1074 | .rhs = assign_expr, | ||
| 1075 | }, | ||
| 895 | }); | 1076 | }); |
| 896 | |||
| 897 | const else_node = try p.arena.allocator.create(Node.Else); | ||
| 898 | else_node.* = .{ | ||
| 899 | .else_token = else_token, | ||
| 900 | .payload = null, | ||
| 901 | .body = statement_node, | ||
| 902 | }; | ||
| 903 | for_prefix.@"else" = else_node; | ||
| 904 | |||
| 905 | return node; | ||
| 906 | } | 1077 | } |
| 907 | 1078 | else_required = true; | |
| 908 | return node; | 1079 | break :blk assign_expr; |
| 909 | } | 1080 | }; |
| 910 | 1081 | const else_token = p.eatToken(.keyword_else) orelse { | |
| 911 | for_prefix.body = try p.expectNode(parseAssignExpr, .{ | 1082 | if (else_required) { |
| 912 | .ExpectedBlockOrAssignment = .{ .token = p.tok_i }, | 1083 | try p.warn(.expected_semi_or_else); |
| 913 | }); | 1084 | } |
| 914 | 1085 | return p.addNode(.{ | |
| 915 | if (p.eatToken(.Semicolon) != null) return node; | 1086 | .tag = .for_simple, |
| 916 | 1087 | .main_token = for_token, | |
| 917 | if (p.eatToken(.Keyword_else)) |else_token| { | 1088 | .data = .{ |
| 918 | const statement_node = try p.expectNode(parseStatement, .{ | 1089 | .lhs = array_expr, |
| 919 | .ExpectedStatement = .{ .token = p.tok_i }, | 1090 | .rhs = then_expr, |
| 1091 | }, | ||
| 920 | }); | 1092 | }); |
| 921 | 1093 | }; | |
| 922 | const else_node = try p.arena.allocator.create(Node.Else); | 1094 | return p.addNode(.{ |
| 923 | else_node.* = .{ | 1095 | .tag = .@"for", |
| 924 | .else_token = else_token, | 1096 | .main_token = for_token, |
| 925 | .payload = null, | 1097 | .data = .{ |
| 926 | .body = statement_node, | 1098 | .lhs = array_expr, |
| 927 | }; | 1099 | .rhs = try p.addExtra(Node.If{ |
| 928 | for_prefix.@"else" = else_node; | 1100 | .then_expr = then_expr, |
| 929 | return node; | 1101 | .else_expr = try p.expectStatement(), |
| 930 | } | 1102 | }), |
| 931 | 1103 | }, | |
| 932 | try p.errors.append(p.gpa, .{ | ||
| 933 | .ExpectedSemiOrElse = .{ .token = p.tok_i }, | ||
| 934 | }); | 1104 | }); |
| 935 | |||
| 936 | return node; | ||
| 937 | } | 1105 | } |
| 938 | 1106 | ||
| 1107 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? | ||
| 939 | /// WhileStatement | 1108 | /// WhileStatement |
| 940 | /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? | 1109 | /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? |
| 941 | /// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) | 1110 | /// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) |
| 942 | fn parseWhileStatement(p: *Parser) !?*Node { | 1111 | fn parseWhileStatement(p: *Parser) !Node.Index { |
| 943 | const node = (try p.parseWhilePrefix()) orelse return null; | 1112 | const while_token = p.eatToken(.keyword_while) orelse return null_node; |
| 944 | const while_prefix = node.cast(Node.While).?; | 1113 | _ = try p.expectToken(.l_paren); |
| 945 | 1114 | const condition = try p.expectExpr(); | |
| 946 | if (try p.parseBlockExpr()) |block_expr_node| { | 1115 | _ = try p.expectToken(.r_paren); |
| 947 | while_prefix.body = block_expr_node; | 1116 | const then_payload = try p.parsePtrPayload(); |
| 948 | 1117 | const cont_expr = try p.parseWhileContinueExpr(); | |
| 949 | if (p.eatToken(.Keyword_else)) |else_token| { | 1118 | |
| 950 | const payload = try p.parsePayload(); | 1119 | // TODO propose to change the syntax so that semicolons are always required |
| 951 | 1120 | // inside while statements, even if there is an `else`. | |
| 952 | const statement_node = try p.expectNode(parseStatement, .{ | 1121 | var else_required = false; |
| 953 | .InvalidToken = .{ .token = p.tok_i }, | 1122 | const then_expr = blk: { |
| 1123 | const block_expr = try p.parseBlockExpr(); | ||
| 1124 | if (block_expr != 0) break :blk block_expr; | ||
| 1125 | const assign_expr = try p.parseAssignExpr(); | ||
| 1126 | if (assign_expr == 0) { | ||
| 1127 | return p.fail(.expected_block_or_assignment); | ||
| 1128 | } | ||
| 1129 | if (p.eatToken(.semicolon)) |_| { | ||
| 1130 | if (cont_expr == 0) { | ||
| 1131 | return p.addNode(.{ | ||
| 1132 | .tag = .while_simple, | ||
| 1133 | .main_token = while_token, | ||
| 1134 | .data = .{ | ||
| 1135 | .lhs = condition, | ||
| 1136 | .rhs = assign_expr, | ||
| 1137 | }, | ||
| 1138 | }); | ||
| 1139 | } else { | ||
| 1140 | return p.addNode(.{ | ||
| 1141 | .tag = .while_cont, | ||
| 1142 | .main_token = while_token, | ||
| 1143 | .data = .{ | ||
| 1144 | .lhs = condition, | ||
| 1145 | .rhs = try p.addExtra(Node.WhileCont{ | ||
| 1146 | .cont_expr = cont_expr, | ||
| 1147 | .then_expr = assign_expr, | ||
| 1148 | }), | ||
| 1149 | }, | ||
| 1150 | }); | ||
| 1151 | } | ||
| 1152 | } | ||
| 1153 | else_required = true; | ||
| 1154 | break :blk assign_expr; | ||
| 1155 | }; | ||
| 1156 | const else_token = p.eatToken(.keyword_else) orelse { | ||
| 1157 | if (else_required) { | ||
| 1158 | try p.warn(.expected_semi_or_else); | ||
| 1159 | } | ||
| 1160 | if (cont_expr == 0) { | ||
| 1161 | return p.addNode(.{ | ||
| 1162 | .tag = .while_simple, | ||
| 1163 | .main_token = while_token, | ||
| 1164 | .data = .{ | ||
| 1165 | .lhs = condition, | ||
| 1166 | .rhs = then_expr, | ||
| 1167 | }, | ||
| 1168 | }); | ||
| 1169 | } else { | ||
| 1170 | return p.addNode(.{ | ||
| 1171 | .tag = .while_cont, | ||
| 1172 | .main_token = while_token, | ||
| 1173 | .data = .{ | ||
| 1174 | .lhs = condition, | ||
| 1175 | .rhs = try p.addExtra(Node.WhileCont{ | ||
| 1176 | .cont_expr = cont_expr, | ||
| 1177 | .then_expr = then_expr, | ||
| 1178 | }), | ||
| 1179 | }, | ||
| 954 | }); | 1180 | }); |
| 955 | |||
| 956 | const else_node = try p.arena.allocator.create(Node.Else); | ||
| 957 | else_node.* = .{ | ||
| 958 | .else_token = else_token, | ||
| 959 | .payload = payload, | ||
| 960 | .body = statement_node, | ||
| 961 | }; | ||
| 962 | while_prefix.@"else" = else_node; | ||
| 963 | |||
| 964 | return node; | ||
| 965 | } | 1181 | } |
| 966 | 1182 | }; | |
| 967 | return node; | 1183 | const else_payload = try p.parsePayload(); |
| 968 | } | 1184 | const else_expr = try p.expectStatement(); |
| 969 | 1185 | return p.addNode(.{ | |
| 970 | while_prefix.body = try p.expectNode(parseAssignExpr, .{ | 1186 | .tag = .@"while", |
| 971 | .ExpectedBlockOrAssignment = .{ .token = p.tok_i }, | 1187 | .main_token = while_token, |
| 972 | }); | 1188 | .data = .{ |
| 973 | 1189 | .lhs = condition, | |
| 974 | if (p.eatToken(.Semicolon) != null) return node; | 1190 | .rhs = try p.addExtra(Node.While{ |
| 975 | 1191 | .cont_expr = cont_expr, | |
| 976 | if (p.eatToken(.Keyword_else)) |else_token| { | 1192 | .then_expr = then_expr, |
| 977 | const payload = try p.parsePayload(); | 1193 | .else_expr = else_expr, |
| 978 | 1194 | }), | |
| 979 | const statement_node = try p.expectNode(parseStatement, .{ | 1195 | }, |
| 980 | .ExpectedStatement = .{ .token = p.tok_i }, | ||
| 981 | }); | ||
| 982 | |||
| 983 | const else_node = try p.arena.allocator.create(Node.Else); | ||
| 984 | else_node.* = .{ | ||
| 985 | .else_token = else_token, | ||
| 986 | .payload = payload, | ||
| 987 | .body = statement_node, | ||
| 988 | }; | ||
| 989 | while_prefix.@"else" = else_node; | ||
| 990 | return node; | ||
| 991 | } | ||
| 992 | |||
| 993 | try p.errors.append(p.gpa, .{ | ||
| 994 | .ExpectedSemiOrElse = .{ .token = p.tok_i }, | ||
| 995 | }); | 1196 | }); |
| 996 | |||
| 997 | return node; | ||
| 998 | } | 1197 | } |
| 999 | 1198 | ||
| 1000 | /// BlockExprStatement | 1199 | /// BlockExprStatement |
| 1001 | /// <- BlockExpr | 1200 | /// <- BlockExpr |
| 1002 | /// / AssignExpr SEMICOLON | 1201 | /// / AssignExpr SEMICOLON |
| 1003 | fn parseBlockExprStatement(p: *Parser) !?*Node { | 1202 | fn parseBlockExprStatement(p: *Parser) !Node.Index { |
| 1004 | if (try p.parseBlockExpr()) |node| return node; | 1203 | const block_expr = try p.parseBlockExpr(); |
| 1005 | if (try p.parseAssignExpr()) |node| { | 1204 | if (block_expr != 0) { |
| 1006 | _ = try p.expectTokenRecoverable(.Semicolon); | 1205 | return block_expr; |
| 1007 | return node; | ||
| 1008 | } | 1206 | } |
| 1009 | return null; | 1207 | const assign_expr = try p.parseAssignExpr(); |
| 1208 | if (assign_expr != 0) { | ||
| 1209 | _ = try p.expectTokenRecoverable(.semicolon); | ||
| 1210 | return assign_expr; | ||
| 1211 | } | ||
| 1212 | return null_node; | ||
| 1010 | } | 1213 | } |
| 1011 | 1214 | ||
| 1012 | /// BlockExpr <- BlockLabel? Block | 1215 | fn expectBlockExprStatement(p: *Parser) !Node.Index { |
| 1013 | fn parseBlockExpr(p: *Parser) Error!?*Node { | 1216 | const node = try p.parseBlockExprStatement(); |
| 1014 | var colon: TokenIndex = undefined; | 1217 | if (node == 0) { |
| 1015 | const label_token = p.parseBlockLabel(&colon); | 1218 | return p.fail(.expected_block_or_expr); |
| 1016 | const block_node = (try p.parseBlock(label_token)) orelse { | 1219 | } |
| 1017 | if (label_token) |label| { | 1220 | return node; |
| 1018 | p.putBackToken(label + 1); // ":" | ||
| 1019 | p.putBackToken(label); // IDENTIFIER | ||
| 1020 | } | ||
| 1021 | return null; | ||
| 1022 | }; | ||
| 1023 | return block_node; | ||
| 1024 | } | 1221 | } |
| 1025 | 1222 | ||
| 1026 | /// AssignExpr <- Expr (AssignOp Expr)? | 1223 | /// BlockExpr <- BlockLabel? Block |
| 1027 | fn parseAssignExpr(p: *Parser) !?*Node { | 1224 | fn parseBlockExpr(p: *Parser) Error!Node.Index { |
| 1028 | return p.parseBinOpExpr(parseAssignOp, parseExpr, .Once); | 1225 | switch (p.token_tags[p.tok_i]) { |
| 1226 | .identifier => { | ||
| 1227 | if (p.token_tags[p.tok_i + 1] == .colon and | ||
| 1228 | p.token_tags[p.tok_i + 2] == .l_brace) | ||
| 1229 | { | ||
| 1230 | p.tok_i += 2; | ||
| 1231 | return p.parseBlock(); | ||
| 1232 | } else { | ||
| 1233 | return null_node; | ||
| 1234 | } | ||
| 1235 | }, | ||
| 1236 | .l_brace => return p.parseBlock(), | ||
| 1237 | else => return null_node, | ||
| 1238 | } | ||
| 1029 | } | 1239 | } |
| 1030 | 1240 | ||
| 1031 | /// Expr <- BoolOrExpr | 1241 | /// AssignExpr <- Expr (AssignOp Expr)? |
| 1032 | fn parseExpr(p: *Parser) Error!?*Node { | 1242 | /// AssignOp |
| 1033 | return p.parsePrefixOpExpr(parseTry, parseBoolOrExpr); | 1243 | /// <- ASTERISKEQUAL |
| 1244 | /// / SLASHEQUAL | ||
| 1245 | /// / PERCENTEQUAL | ||
| 1246 | /// / PLUSEQUAL | ||
| 1247 | /// / MINUSEQUAL | ||
| 1248 | /// / LARROW2EQUAL | ||
| 1249 | /// / RARROW2EQUAL | ||
| 1250 | /// / AMPERSANDEQUAL | ||
| 1251 | /// / CARETEQUAL | ||
| 1252 | /// / PIPEEQUAL | ||
| 1253 | /// / ASTERISKPERCENTEQUAL | ||
| 1254 | /// / PLUSPERCENTEQUAL | ||
| 1255 | /// / MINUSPERCENTEQUAL | ||
| 1256 | /// / EQUAL | ||
| 1257 | fn parseAssignExpr(p: *Parser) !Node.Index { | ||
| 1258 | const expr = try p.parseExpr(); | ||
| 1259 | if (expr == 0) return null_node; | ||
| 1260 | |||
| 1261 | const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { | ||
| 1262 | .asterisk_equal => .assign_mul, | ||
| 1263 | .slash_equal => .assign_div, | ||
| 1264 | .percent_equal => .assign_mod, | ||
| 1265 | .plus_equal => .assign_add, | ||
| 1266 | .minus_equal => .assign_sub, | ||
| 1267 | .angle_bracket_angle_bracket_left_equal => .assign_bit_shift_left, | ||
| 1268 | .angle_bracket_angle_bracket_right_equal => .assign_bit_shift_right, | ||
| 1269 | .ampersand_equal => .assign_bit_and, | ||
| 1270 | .caret_equal => .assign_bit_xor, | ||
| 1271 | .pipe_equal => .assign_bit_or, | ||
| 1272 | .asterisk_percent_equal => .assign_mul_wrap, | ||
| 1273 | .plus_percent_equal => .assign_add_wrap, | ||
| 1274 | .minus_percent_equal => .assign_sub_wrap, | ||
| 1275 | .equal => .assign, | ||
| 1276 | else => return expr, | ||
| 1277 | }; | ||
| 1278 | return p.addNode(.{ | ||
| 1279 | .tag = tag, | ||
| 1280 | .main_token = p.nextToken(), | ||
| 1281 | .data = .{ | ||
| 1282 | .lhs = expr, | ||
| 1283 | .rhs = try p.expectExpr(), | ||
| 1284 | }, | ||
| 1285 | }); | ||
| 1034 | } | 1286 | } |
| 1035 | 1287 | ||
| 1036 | /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)* | 1288 | fn expectAssignExpr(p: *Parser) !Node.Index { |
| 1037 | fn parseBoolOrExpr(p: *Parser) !?*Node { | 1289 | const expr = try p.parseAssignExpr(); |
| 1038 | return p.parseBinOpExpr( | 1290 | if (expr == 0) { |
| 1039 | SimpleBinOpParseFn(.Keyword_or, .BoolOr), | 1291 | return p.fail(.expected_expr_or_assignment); |
| 1040 | parseBoolAndExpr, | 1292 | } |
| 1041 | .Infinitely, | 1293 | return expr; |
| 1042 | ); | ||
| 1043 | } | 1294 | } |
| 1044 | 1295 | ||
| 1045 | /// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)* | 1296 | /// Expr <- BoolOrExpr |
| 1046 | fn parseBoolAndExpr(p: *Parser) !?*Node { | 1297 | fn parseExpr(p: *Parser) Error!Node.Index { |
| 1047 | return p.parseBinOpExpr( | 1298 | return p.parseBoolOrExpr(); |
| 1048 | SimpleBinOpParseFn(.Keyword_and, .BoolAnd), | ||
| 1049 | parseCompareExpr, | ||
| 1050 | .Infinitely, | ||
| 1051 | ); | ||
| 1052 | } | 1299 | } |
| 1053 | 1300 | ||
| 1054 | /// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)? | 1301 | fn expectExpr(p: *Parser) Error!Node.Index { |
| 1055 | fn parseCompareExpr(p: *Parser) !?*Node { | 1302 | const node = try p.parseExpr(); |
| 1056 | return p.parseBinOpExpr(parseCompareOp, parseBitwiseExpr, .Once); | 1303 | if (node == 0) { |
| 1304 | return p.fail(.expected_expr); | ||
| 1305 | } else { | ||
| 1306 | return node; | ||
| 1307 | } | ||
| 1057 | } | 1308 | } |
| 1058 | 1309 | ||
| 1059 | /// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)* | 1310 | /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)* |
| 1060 | fn parseBitwiseExpr(p: *Parser) !?*Node { | 1311 | fn parseBoolOrExpr(p: *Parser) Error!Node.Index { |
| 1061 | return p.parseBinOpExpr(parseBitwiseOp, parseBitShiftExpr, .Infinitely); | 1312 | var res = try p.parseBoolAndExpr(); |
| 1062 | } | 1313 | if (res == 0) return null_node; |
| 1063 | 1314 | ||
| 1064 | /// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)* | 1315 | while (true) { |
| 1065 | fn parseBitShiftExpr(p: *Parser) !?*Node { | 1316 | switch (p.token_tags[p.tok_i]) { |
| 1066 | return p.parseBinOpExpr(parseBitShiftOp, parseAdditionExpr, .Infinitely); | 1317 | .keyword_or => { |
| 1318 | const or_token = p.nextToken(); | ||
| 1319 | const rhs = try p.parseBoolAndExpr(); | ||
| 1320 | if (rhs == 0) { | ||
| 1321 | return p.fail(.invalid_token); | ||
| 1322 | } | ||
| 1323 | res = try p.addNode(.{ | ||
| 1324 | .tag = .bool_or, | ||
| 1325 | .main_token = or_token, | ||
| 1326 | .data = .{ | ||
| 1327 | .lhs = res, | ||
| 1328 | .rhs = rhs, | ||
| 1329 | }, | ||
| 1330 | }); | ||
| 1331 | }, | ||
| 1332 | else => return res, | ||
| 1333 | } | ||
| 1334 | } | ||
| 1067 | } | 1335 | } |
| 1068 | 1336 | ||
| 1069 | /// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)* | 1337 | /// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)* |
| 1070 | fn parseAdditionExpr(p: *Parser) !?*Node { | 1338 | fn parseBoolAndExpr(p: *Parser) !Node.Index { |
| 1071 | return p.parseBinOpExpr(parseAdditionOp, parseMultiplyExpr, .Infinitely); | 1339 | var res = try p.parseCompareExpr(); |
| 1072 | } | 1340 | if (res == 0) return null_node; |
| 1073 | 1341 | ||
| 1074 | /// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)* | 1342 | while (true) { |
| 1075 | fn parseMultiplyExpr(p: *Parser) !?*Node { | 1343 | switch (p.token_tags[p.tok_i]) { |
| 1076 | return p.parseBinOpExpr(parseMultiplyOp, parsePrefixExpr, .Infinitely); | 1344 | .keyword_and => { |
| 1345 | const and_token = p.nextToken(); | ||
| 1346 | const rhs = try p.parseCompareExpr(); | ||
| 1347 | if (rhs == 0) { | ||
| 1348 | return p.fail(.invalid_token); | ||
| 1349 | } | ||
| 1350 | res = try p.addNode(.{ | ||
| 1351 | .tag = .bool_and, | ||
| 1352 | .main_token = and_token, | ||
| 1353 | .data = .{ | ||
| 1354 | .lhs = res, | ||
| 1355 | .rhs = rhs, | ||
| 1356 | }, | ||
| 1357 | }); | ||
| 1358 | }, | ||
| 1359 | .invalid_ampersands => { | ||
| 1360 | try p.warn(.invalid_and); | ||
| 1361 | p.tok_i += 1; | ||
| 1362 | return p.parseCompareExpr(); | ||
| 1363 | }, | ||
| 1364 | else => return res, | ||
| 1365 | } | ||
| 1366 | } | ||
| 1077 | } | 1367 | } |
| 1078 | 1368 | ||
| 1079 | /// PrefixExpr <- PrefixOp* PrimaryExpr | 1369 | /// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)? |
| 1080 | fn parsePrefixExpr(p: *Parser) !?*Node { | 1370 | /// CompareOp |
| 1081 | return p.parsePrefixOpExpr(parsePrefixOp, parsePrimaryExpr); | 1371 | /// <- EQUALEQUAL |
| 1372 | /// / EXCLAMATIONMARKEQUAL | ||
| 1373 | /// / LARROW | ||
| 1374 | /// / RARROW | ||
| 1375 | /// / LARROWEQUAL | ||
| 1376 | /// / RARROWEQUAL | ||
| 1377 | fn parseCompareExpr(p: *Parser) !Node.Index { | ||
| 1378 | const expr = try p.parseBitwiseExpr(); | ||
| 1379 | if (expr == 0) return null_node; | ||
| 1380 | |||
| 1381 | const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { | ||
| 1382 | .equal_equal => .equal_equal, | ||
| 1383 | .bang_equal => .bang_equal, | ||
| 1384 | .angle_bracket_left => .less_than, | ||
| 1385 | .angle_bracket_right => .greater_than, | ||
| 1386 | .angle_bracket_left_equal => .less_or_equal, | ||
| 1387 | .angle_bracket_right_equal => .greater_or_equal, | ||
| 1388 | else => return expr, | ||
| 1389 | }; | ||
| 1390 | return p.addNode(.{ | ||
| 1391 | .tag = tag, | ||
| 1392 | .main_token = p.nextToken(), | ||
| 1393 | .data = .{ | ||
| 1394 | .lhs = expr, | ||
| 1395 | .rhs = try p.expectBitwiseExpr(), | ||
| 1396 | }, | ||
| 1397 | }); | ||
| 1082 | } | 1398 | } |
| 1083 | 1399 | ||
| 1084 | /// PrimaryExpr | 1400 | /// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)* |
| 1085 | /// <- AsmExpr | 1401 | /// BitwiseOp |
| 1086 | /// / IfExpr | 1402 | /// <- AMPERSAND |
| 1087 | /// / KEYWORD_break BreakLabel? Expr? | 1403 | /// / CARET |
| 1088 | /// / KEYWORD_comptime Expr | 1404 | /// / PIPE |
| 1089 | /// / KEYWORD_nosuspend Expr | 1405 | /// / KEYWORD_orelse |
| 1090 | /// / KEYWORD_continue BreakLabel? | 1406 | /// / KEYWORD_catch Payload? |
| 1091 | /// / KEYWORD_resume Expr | 1407 | fn parseBitwiseExpr(p: *Parser) !Node.Index { |
| 1092 | /// / KEYWORD_return Expr? | 1408 | var res = try p.parseBitShiftExpr(); |
| 1093 | /// / BlockLabel? LoopExpr | 1409 | if (res == 0) return null_node; |
| 1094 | /// / Block | ||
| 1095 | /// / CurlySuffixExpr | ||
| 1096 | fn parsePrimaryExpr(p: *Parser) !?*Node { | ||
| 1097 | if (try p.parseAsmExpr()) |node| return node; | ||
| 1098 | if (try p.parseIfExpr()) |node| return node; | ||
| 1099 | |||
| 1100 | if (p.eatToken(.Keyword_break)) |token| { | ||
| 1101 | const label = try p.parseBreakLabel(); | ||
| 1102 | const expr_node = try p.parseExpr(); | ||
| 1103 | const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{ | ||
| 1104 | .tag = .Break, | ||
| 1105 | .ltoken = token, | ||
| 1106 | }, .{ | ||
| 1107 | .label = label, | ||
| 1108 | .rhs = expr_node, | ||
| 1109 | }); | ||
| 1110 | return &node.base; | ||
| 1111 | } | ||
| 1112 | 1410 | ||
| 1113 | if (p.eatToken(.Keyword_comptime)) |token| { | 1411 | while (true) { |
| 1114 | const expr_node = try p.expectNode(parseExpr, .{ | 1412 | const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { |
| 1115 | .ExpectedExpr = .{ .token = p.tok_i }, | 1413 | .ampersand => .bit_and, |
| 1116 | }); | 1414 | .caret => .bit_xor, |
| 1117 | const node = try p.arena.allocator.create(Node.Comptime); | 1415 | .pipe => .bit_or, |
| 1118 | node.* = .{ | 1416 | .keyword_orelse => .@"orelse", |
| 1119 | .doc_comments = null, | 1417 | .keyword_catch => { |
| 1120 | .comptime_token = token, | 1418 | const catch_token = p.nextToken(); |
| 1121 | .expr = expr_node, | 1419 | _ = try p.parsePayload(); |
| 1420 | const rhs = try p.parseBitShiftExpr(); | ||
| 1421 | if (rhs == 0) { | ||
| 1422 | return p.fail(.invalid_token); | ||
| 1423 | } | ||
| 1424 | res = try p.addNode(.{ | ||
| 1425 | .tag = .@"catch", | ||
| 1426 | .main_token = catch_token, | ||
| 1427 | .data = .{ | ||
| 1428 | .lhs = res, | ||
| 1429 | .rhs = rhs, | ||
| 1430 | }, | ||
| 1431 | }); | ||
| 1432 | continue; | ||
| 1433 | }, | ||
| 1434 | else => return res, | ||
| 1122 | }; | 1435 | }; |
| 1123 | return &node.base; | 1436 | res = try p.addNode(.{ |
| 1124 | } | 1437 | .tag = tag, |
| 1125 | 1438 | .main_token = p.nextToken(), | |
| 1126 | if (p.eatToken(.Keyword_nosuspend)) |token| { | 1439 | .data = .{ |
| 1127 | const expr_node = try p.expectNode(parseExpr, .{ | 1440 | .lhs = res, |
| 1128 | .ExpectedExpr = .{ .token = p.tok_i }, | 1441 | .rhs = try p.expectBitShiftExpr(), |
| 1442 | }, | ||
| 1129 | }); | 1443 | }); |
| 1130 | const node = try p.arena.allocator.create(Node.Nosuspend); | ||
| 1131 | node.* = .{ | ||
| 1132 | .nosuspend_token = token, | ||
| 1133 | .expr = expr_node, | ||
| 1134 | }; | ||
| 1135 | return &node.base; | ||
| 1136 | } | 1444 | } |
| 1445 | } | ||
| 1137 | 1446 | ||
| 1138 | if (p.eatToken(.Keyword_continue)) |token| { | 1447 | fn expectBitwiseExpr(p: *Parser) Error!Node.Index { |
| 1139 | const label = try p.parseBreakLabel(); | 1448 | const node = try p.parseBitwiseExpr(); |
| 1140 | const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{ | 1449 | if (node == 0) { |
| 1141 | .tag = .Continue, | 1450 | return p.fail(.invalid_token); |
| 1142 | .ltoken = token, | 1451 | } else { |
| 1143 | }, .{ | 1452 | return node; |
| 1144 | .label = label, | ||
| 1145 | .rhs = null, | ||
| 1146 | }); | ||
| 1147 | return &node.base; | ||
| 1148 | } | 1453 | } |
| 1454 | } | ||
| 1149 | 1455 | ||
| 1150 | if (p.eatToken(.Keyword_resume)) |token| { | 1456 | /// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)* |
| 1151 | const expr_node = try p.expectNode(parseExpr, .{ | 1457 | /// BitShiftOp |
| 1152 | .ExpectedExpr = .{ .token = p.tok_i }, | 1458 | /// <- LARROW2 |
| 1153 | }); | 1459 | /// / RARROW2 |
| 1154 | const node = try p.arena.allocator.create(Node.SimplePrefixOp); | 1460 | fn parseBitShiftExpr(p: *Parser) Error!Node.Index { |
| 1155 | node.* = .{ | 1461 | var res = try p.parseAdditionExpr(); |
| 1156 | .base = .{ .tag = .Resume }, | 1462 | if (res == 0) return null_node; |
| 1157 | .op_token = token, | ||
| 1158 | .rhs = expr_node, | ||
| 1159 | }; | ||
| 1160 | return &node.base; | ||
| 1161 | } | ||
| 1162 | 1463 | ||
| 1163 | if (p.eatToken(.Keyword_return)) |token| { | 1464 | while (true) { |
| 1164 | const expr_node = try p.parseExpr(); | 1465 | const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { |
| 1165 | const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{ | 1466 | .angle_bracket_angle_bracket_left => .bit_shift_left, |
| 1166 | .tag = .Return, | 1467 | .angle_bracket_angle_bracket_right => .bit_shift_right, |
| 1167 | .ltoken = token, | 1468 | else => return res, |
| 1168 | }, .{ | 1469 | }; |
| 1169 | .rhs = expr_node, | 1470 | res = try p.addNode(.{ |
| 1471 | .tag = tag, | ||
| 1472 | .main_token = p.nextToken(), | ||
| 1473 | .data = .{ | ||
| 1474 | .lhs = res, | ||
| 1475 | .rhs = try p.expectAdditionExpr(), | ||
| 1476 | }, | ||
| 1170 | }); | 1477 | }); |
| 1171 | return &node.base; | ||
| 1172 | } | 1478 | } |
| 1479 | } | ||
| 1173 | 1480 | ||
| 1174 | var colon: TokenIndex = undefined; | 1481 | fn expectBitShiftExpr(p: *Parser) Error!Node.Index { |
| 1175 | const label = p.parseBlockLabel(&colon); | 1482 | const node = try p.parseBitShiftExpr(); |
| 1176 | if (try p.parseLoopExpr()) |node| { | 1483 | if (node == 0) { |
| 1177 | if (node.cast(Node.For)) |for_node| { | 1484 | return p.fail(.invalid_token); |
| 1178 | for_node.label = label; | 1485 | } else { |
| 1179 | } else if (node.cast(Node.While)) |while_node| { | ||
| 1180 | while_node.label = label; | ||
| 1181 | } else unreachable; | ||
| 1182 | return node; | 1486 | return node; |
| 1183 | } | 1487 | } |
| 1184 | if (label) |token| { | 1488 | } |
| 1185 | p.putBackToken(token + 1); // ":" | ||
| 1186 | p.putBackToken(token); // IDENTIFIER | ||
| 1187 | } | ||
| 1188 | |||
| 1189 | if (try p.parseBlock(null)) |node| return node; | ||
| 1190 | if (try p.parseCurlySuffixExpr()) |node| return node; | ||
| 1191 | 1489 | ||
| 1192 | return null; | 1490 | /// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)* |
| 1193 | } | 1491 | /// AdditionOp |
| 1492 | /// <- PLUS | ||
| 1493 | /// / MINUS | ||
| 1494 | /// / PLUS2 | ||
| 1495 | /// / PLUSPERCENT | ||
| 1496 | /// / MINUSPERCENT | ||
| 1497 | fn parseAdditionExpr(p: *Parser) Error!Node.Index { | ||
| 1498 | var res = try p.parseMultiplyExpr(); | ||
| 1499 | if (res == 0) return null_node; | ||
| 1194 | 1500 | ||
| 1195 | /// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? | 1501 | while (true) { |
| 1196 | fn parseIfExpr(p: *Parser) !?*Node { | 1502 | const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { |
| 1197 | return p.parseIf(parseExpr); | 1503 | .plus => .add, |
| 1504 | .minus => .sub, | ||
| 1505 | .plus_plus => .array_cat, | ||
| 1506 | .plus_percent => .add_wrap, | ||
| 1507 | .minus_percent => .sub_wrap, | ||
| 1508 | else => return res, | ||
| 1509 | }; | ||
| 1510 | res = try p.addNode(.{ | ||
| 1511 | .tag = tag, | ||
| 1512 | .main_token = p.nextToken(), | ||
| 1513 | .data = .{ | ||
| 1514 | .lhs = res, | ||
| 1515 | .rhs = try p.expectMultiplyExpr(), | ||
| 1516 | }, | ||
| 1517 | }); | ||
| 1518 | } | ||
| 1198 | } | 1519 | } |
| 1199 | 1520 | ||
| 1200 | /// Block <- LBRACE Statement* RBRACE | 1521 | fn expectAdditionExpr(p: *Parser) Error!Node.Index { |
| 1201 | fn parseBlock(p: *Parser, label_token: ?TokenIndex) !?*Node { | 1522 | const node = try p.parseAdditionExpr(); |
| 1202 | const lbrace = p.eatToken(.LBrace) orelse return null; | 1523 | if (node == 0) { |
| 1524 | return p.fail(.invalid_token); | ||
| 1525 | } | ||
| 1526 | return node; | ||
| 1527 | } | ||
| 1203 | 1528 | ||
| 1204 | var statements = std.ArrayList(*Node).init(p.gpa); | 1529 | /// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)* |
| 1205 | defer statements.deinit(); | 1530 | /// MultiplyOp |
| 1531 | /// <- PIPE2 | ||
| 1532 | /// / ASTERISK | ||
| 1533 | /// / SLASH | ||
| 1534 | /// / PERCENT | ||
| 1535 | /// / ASTERISK2 | ||
| 1536 | /// / ASTERISKPERCENT | ||
| 1537 | fn parseMultiplyExpr(p: *Parser) Error!Node.Index { | ||
| 1538 | var res = try p.parsePrefixExpr(); | ||
| 1539 | if (res == 0) return null_node; | ||
| 1206 | 1540 | ||
| 1207 | while (true) { | 1541 | while (true) { |
| 1208 | const statement = (p.parseStatement() catch |err| switch (err) { | 1542 | const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { |
| 1209 | error.OutOfMemory => return error.OutOfMemory, | 1543 | .pipe_pipe => .merge_error_sets, |
| 1210 | error.ParseError => { | 1544 | .asterisk => .mul, |
| 1211 | // try to skip to the next statement | 1545 | .slash => .div, |
| 1212 | p.findNextStmt(); | 1546 | .percent => .mod, |
| 1213 | continue; | 1547 | .asterisk_asterisk => .array_mult, |
| 1548 | .asterisk_percent => .mul_wrap, | ||
| 1549 | else => return res, | ||
| 1550 | }; | ||
| 1551 | res = try p.addNode(.{ | ||
| 1552 | .tag = tag, | ||
| 1553 | .main_token = p.nextToken(), | ||
| 1554 | .data = .{ | ||
| 1555 | .lhs = res, | ||
| 1556 | .rhs = try p.expectPrefixExpr(), | ||
| 1214 | }, | 1557 | }, |
| 1215 | }) orelse break; | 1558 | }); |
| 1216 | try statements.append(statement); | ||
| 1217 | } | 1559 | } |
| 1560 | } | ||
| 1218 | 1561 | ||
| 1219 | const rbrace = try p.expectToken(.RBrace); | 1562 | fn expectMultiplyExpr(p: *Parser) Error!Node.Index { |
| 1220 | 1563 | const node = try p.parseMultiplyExpr(); | |
| 1221 | const statements_len = @intCast(NodeIndex, statements.items.len); | 1564 | if (node == 0) { |
| 1222 | 1565 | return p.fail(.invalid_token); | |
| 1223 | if (label_token) |label| { | ||
| 1224 | const block_node = try Node.LabeledBlock.alloc(&p.arena.allocator, statements_len); | ||
| 1225 | block_node.* = .{ | ||
| 1226 | .label = label, | ||
| 1227 | .lbrace = lbrace, | ||
| 1228 | .statements_len = statements_len, | ||
| 1229 | .rbrace = rbrace, | ||
| 1230 | }; | ||
| 1231 | std.mem.copy(*Node, block_node.statements(), statements.items); | ||
| 1232 | return &block_node.base; | ||
| 1233 | } else { | ||
| 1234 | const block_node = try Node.Block.alloc(&p.arena.allocator, statements_len); | ||
| 1235 | block_node.* = .{ | ||
| 1236 | .lbrace = lbrace, | ||
| 1237 | .statements_len = statements_len, | ||
| 1238 | .rbrace = rbrace, | ||
| 1239 | }; | ||
| 1240 | std.mem.copy(*Node, block_node.statements(), statements.items); | ||
| 1241 | return &block_node.base; | ||
| 1242 | } | 1566 | } |
| 1567 | return node; | ||
| 1243 | } | 1568 | } |
| 1244 | 1569 | ||
| 1245 | /// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr) | 1570 | /// PrefixExpr <- PrefixOp* PrimaryExpr |
| 1246 | fn parseLoopExpr(p: *Parser) !?*Node { | 1571 | /// PrefixOp |
| 1247 | const inline_token = p.eatToken(.Keyword_inline); | 1572 | /// <- EXCLAMATIONMARK |
| 1573 | /// / MINUS | ||
| 1574 | /// / TILDE | ||
| 1575 | /// / MINUSPERCENT | ||
| 1576 | /// / AMPERSAND | ||
| 1577 | /// / KEYWORD_try | ||
| 1578 | /// / KEYWORD_await | ||
| 1579 | fn parsePrefixExpr(p: *Parser) Error!Node.Index { | ||
| 1580 | const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { | ||
| 1581 | .bang => .bool_not, | ||
| 1582 | .minus => .negation, | ||
| 1583 | .tilde => .bit_not, | ||
| 1584 | .minus_percent => .negation_wrap, | ||
| 1585 | .ampersand => .address_of, | ||
| 1586 | .keyword_try => .@"try", | ||
| 1587 | .keyword_await => .@"await", | ||
| 1588 | else => return p.parsePrimaryExpr(), | ||
| 1589 | }; | ||
| 1590 | return p.addNode(.{ | ||
| 1591 | .tag = tag, | ||
| 1592 | .main_token = p.nextToken(), | ||
| 1593 | .data = .{ | ||
| 1594 | .lhs = try p.expectPrefixExpr(), | ||
| 1595 | .rhs = undefined, | ||
| 1596 | }, | ||
| 1597 | }); | ||
| 1598 | } | ||
| 1248 | 1599 | ||
| 1249 | if (try p.parseForExpr()) |node| { | 1600 | fn expectPrefixExpr(p: *Parser) Error!Node.Index { |
| 1250 | node.cast(Node.For).?.inline_token = inline_token; | 1601 | const node = try p.parsePrefixExpr(); |
| 1251 | return node; | 1602 | if (node == 0) { |
| 1603 | return p.fail(.expected_prefix_expr); | ||
| 1252 | } | 1604 | } |
| 1605 | return node; | ||
| 1606 | } | ||
| 1253 | 1607 | ||
| 1254 | if (try p.parseWhileExpr()) |node| { | 1608 | /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr |
| 1255 | node.cast(Node.While).?.inline_token = inline_token; | 1609 | /// PrefixTypeOp |
| 1256 | return node; | 1610 | /// <- QUESTIONMARK |
| 1611 | /// / KEYWORD_anyframe MINUSRARROW | ||
| 1612 | /// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | ||
| 1613 | /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | ||
| 1614 | /// PtrTypeStart | ||
| 1615 | /// <- ASTERISK | ||
| 1616 | /// / ASTERISK2 | ||
| 1617 | /// / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET | ||
| 1618 | /// ArrayTypeStart <- LBRACKET Expr? (COLON Expr)? RBRACKET | ||
| 1619 | fn parseTypeExpr(p: *Parser) Error!Node.Index { | ||
| 1620 | switch (p.token_tags[p.tok_i]) { | ||
| 1621 | .question_mark => return p.addNode(.{ | ||
| 1622 | .tag = .optional_type, | ||
| 1623 | .main_token = p.nextToken(), | ||
| 1624 | .data = .{ | ||
| 1625 | .lhs = try p.expectTypeExpr(), | ||
| 1626 | .rhs = undefined, | ||
| 1627 | }, | ||
| 1628 | }), | ||
| 1629 | .keyword_anyframe => switch (p.token_tags[p.tok_i + 1]) { | ||
| 1630 | .arrow => return p.addNode(.{ | ||
| 1631 | .tag = .anyframe_type, | ||
| 1632 | .main_token = p.nextToken(), | ||
| 1633 | .data = .{ | ||
| 1634 | .lhs = p.nextToken(), | ||
| 1635 | .rhs = try p.expectTypeExpr(), | ||
| 1636 | }, | ||
| 1637 | }), | ||
| 1638 | else => return p.parseErrorUnionExpr(), | ||
| 1639 | }, | ||
| 1640 | .asterisk => { | ||
| 1641 | const asterisk = p.nextToken(); | ||
| 1642 | const mods = try p.parsePtrModifiers(); | ||
| 1643 | const elem_type = try p.expectTypeExpr(); | ||
| 1644 | if (mods.bit_range_start == 0) { | ||
| 1645 | return p.addNode(.{ | ||
| 1646 | .tag = .ptr_type_aligned, | ||
| 1647 | .main_token = asterisk, | ||
| 1648 | .data = .{ | ||
| 1649 | .lhs = mods.align_node, | ||
| 1650 | .rhs = elem_type, | ||
| 1651 | }, | ||
| 1652 | }); | ||
| 1653 | } else { | ||
| 1654 | return p.addNode(.{ | ||
| 1655 | .tag = .ptr_type_bit_range, | ||
| 1656 | .main_token = asterisk, | ||
| 1657 | .data = .{ | ||
| 1658 | .lhs = try p.addExtra(Node.PtrTypeBitRange{ | ||
| 1659 | .sentinel = 0, | ||
| 1660 | .align_node = mods.align_node, | ||
| 1661 | .bit_range_start = mods.bit_range_start, | ||
| 1662 | .bit_range_end = mods.bit_range_end, | ||
| 1663 | }), | ||
| 1664 | .rhs = elem_type, | ||
| 1665 | }, | ||
| 1666 | }); | ||
| 1667 | } | ||
| 1668 | }, | ||
| 1669 | .asterisk_asterisk => { | ||
| 1670 | const asterisk = p.nextToken(); | ||
| 1671 | const mods = try p.parsePtrModifiers(); | ||
| 1672 | const elem_type = try p.expectTypeExpr(); | ||
| 1673 | const inner: Node.Index = inner: { | ||
| 1674 | if (mods.bit_range_start == 0) { | ||
| 1675 | break :inner try p.addNode(.{ | ||
| 1676 | .tag = .ptr_type_aligned, | ||
| 1677 | .main_token = asterisk, | ||
| 1678 | .data = .{ | ||
| 1679 | .lhs = mods.align_node, | ||
| 1680 | .rhs = elem_type, | ||
| 1681 | }, | ||
| 1682 | }); | ||
| 1683 | } else { | ||
| 1684 | break :inner try p.addNode(.{ | ||
| 1685 | .tag = .ptr_type_bit_range, | ||
| 1686 | .main_token = asterisk, | ||
| 1687 | .data = .{ | ||
| 1688 | .lhs = try p.addExtra(Node.PtrTypeBitRange{ | ||
| 1689 | .sentinel = 0, | ||
| 1690 | .align_node = mods.align_node, | ||
| 1691 | .bit_range_start = mods.bit_range_start, | ||
| 1692 | .bit_range_end = mods.bit_range_end, | ||
| 1693 | }), | ||
| 1694 | .rhs = elem_type, | ||
| 1695 | }, | ||
| 1696 | }); | ||
| 1697 | } | ||
| 1698 | }; | ||
| 1699 | return p.addNode(.{ | ||
| 1700 | .tag = .ptr_type_aligned, | ||
| 1701 | .main_token = asterisk, | ||
| 1702 | .data = .{ | ||
| 1703 | .lhs = 0, | ||
| 1704 | .rhs = inner, | ||
| 1705 | }, | ||
| 1706 | }); | ||
| 1707 | }, | ||
| 1708 | .l_bracket => switch (p.token_tags[p.tok_i + 1]) { | ||
| 1709 | .asterisk => { | ||
| 1710 | const lbracket = p.nextToken(); | ||
| 1711 | const asterisk = p.nextToken(); | ||
| 1712 | var sentinel: Node.Index = 0; | ||
| 1713 | prefix: { | ||
| 1714 | if (p.eatToken(.identifier)) |ident| { | ||
| 1715 | const token_slice = p.source[p.token_starts[ident]..][0..2]; | ||
| 1716 | if (!std.mem.eql(u8, token_slice, "c]")) { | ||
| 1717 | p.tok_i -= 1; | ||
| 1718 | } else { | ||
| 1719 | break :prefix; | ||
| 1720 | } | ||
| 1721 | } | ||
| 1722 | if (p.eatToken(.colon)) |_| { | ||
| 1723 | sentinel = try p.expectExpr(); | ||
| 1724 | } | ||
| 1725 | } | ||
| 1726 | _ = try p.expectToken(.r_bracket); | ||
| 1727 | const mods = try p.parsePtrModifiers(); | ||
| 1728 | const elem_type = try p.expectTypeExpr(); | ||
| 1729 | if (mods.bit_range_start == 0) { | ||
| 1730 | if (sentinel == 0) { | ||
| 1731 | return p.addNode(.{ | ||
| 1732 | .tag = .ptr_type_aligned, | ||
| 1733 | .main_token = asterisk, | ||
| 1734 | .data = .{ | ||
| 1735 | .lhs = mods.align_node, | ||
| 1736 | .rhs = elem_type, | ||
| 1737 | }, | ||
| 1738 | }); | ||
| 1739 | } else if (mods.align_node == 0) { | ||
| 1740 | return p.addNode(.{ | ||
| 1741 | .tag = .ptr_type_sentinel, | ||
| 1742 | .main_token = asterisk, | ||
| 1743 | .data = .{ | ||
| 1744 | .lhs = sentinel, | ||
| 1745 | .rhs = elem_type, | ||
| 1746 | }, | ||
| 1747 | }); | ||
| 1748 | } else { | ||
| 1749 | return p.addNode(.{ | ||
| 1750 | .tag = .ptr_type, | ||
| 1751 | .main_token = asterisk, | ||
| 1752 | .data = .{ | ||
| 1753 | .lhs = try p.addExtra(Node.PtrType{ | ||
| 1754 | .sentinel = sentinel, | ||
| 1755 | .align_node = mods.align_node, | ||
| 1756 | }), | ||
| 1757 | .rhs = elem_type, | ||
| 1758 | }, | ||
| 1759 | }); | ||
| 1760 | } | ||
| 1761 | } else { | ||
| 1762 | return p.addNode(.{ | ||
| 1763 | .tag = .ptr_type_bit_range, | ||
| 1764 | .main_token = asterisk, | ||
| 1765 | .data = .{ | ||
| 1766 | .lhs = try p.addExtra(Node.PtrTypeBitRange{ | ||
| 1767 | .sentinel = sentinel, | ||
| 1768 | .align_node = mods.align_node, | ||
| 1769 | .bit_range_start = mods.bit_range_start, | ||
| 1770 | .bit_range_end = mods.bit_range_end, | ||
| 1771 | }), | ||
| 1772 | .rhs = elem_type, | ||
| 1773 | }, | ||
| 1774 | }); | ||
| 1775 | } | ||
| 1776 | }, | ||
| 1777 | else => { | ||
| 1778 | const lbracket = p.nextToken(); | ||
| 1779 | const len_expr = try p.parseExpr(); | ||
| 1780 | const sentinel: Node.Index = if (p.eatToken(.colon)) |_| | ||
| 1781 | try p.expectExpr() | ||
| 1782 | else | ||
| 1783 | 0; | ||
| 1784 | _ = try p.expectToken(.r_bracket); | ||
| 1785 | const mods = try p.parsePtrModifiers(); | ||
| 1786 | const elem_type = try p.expectTypeExpr(); | ||
| 1787 | if (mods.bit_range_start != 0) { | ||
| 1788 | try p.warnMsg(.{ | ||
| 1789 | .tag = .invalid_bit_range, | ||
| 1790 | .token = p.nodes.items(.main_token)[mods.bit_range_start], | ||
| 1791 | }); | ||
| 1792 | } | ||
| 1793 | if (len_expr == 0) { | ||
| 1794 | if (sentinel == 0) { | ||
| 1795 | return p.addNode(.{ | ||
| 1796 | .tag = .ptr_type_aligned, | ||
| 1797 | .main_token = lbracket, | ||
| 1798 | .data = .{ | ||
| 1799 | .lhs = mods.align_node, | ||
| 1800 | .rhs = elem_type, | ||
| 1801 | }, | ||
| 1802 | }); | ||
| 1803 | } else if (mods.align_node == 0) { | ||
| 1804 | return p.addNode(.{ | ||
| 1805 | .tag = .ptr_type_sentinel, | ||
| 1806 | .main_token = lbracket, | ||
| 1807 | .data = .{ | ||
| 1808 | .lhs = sentinel, | ||
| 1809 | .rhs = elem_type, | ||
| 1810 | }, | ||
| 1811 | }); | ||
| 1812 | } else { | ||
| 1813 | return p.addNode(.{ | ||
| 1814 | .tag = .ptr_type, | ||
| 1815 | .main_token = lbracket, | ||
| 1816 | .data = .{ | ||
| 1817 | .lhs = try p.addExtra(Node.PtrType{ | ||
| 1818 | .sentinel = sentinel, | ||
| 1819 | .align_node = mods.align_node, | ||
| 1820 | }), | ||
| 1821 | .rhs = elem_type, | ||
| 1822 | }, | ||
| 1823 | }); | ||
| 1824 | } | ||
| 1825 | } else { | ||
| 1826 | if (mods.align_node != 0) { | ||
| 1827 | try p.warnMsg(.{ | ||
| 1828 | .tag = .invalid_align, | ||
| 1829 | .token = p.nodes.items(.main_token)[mods.align_node], | ||
| 1830 | }); | ||
| 1831 | } | ||
| 1832 | if (sentinel == 0) { | ||
| 1833 | return p.addNode(.{ | ||
| 1834 | .tag = .array_type, | ||
| 1835 | .main_token = lbracket, | ||
| 1836 | .data = .{ | ||
| 1837 | .lhs = len_expr, | ||
| 1838 | .rhs = elem_type, | ||
| 1839 | }, | ||
| 1840 | }); | ||
| 1841 | } else { | ||
| 1842 | return p.addNode(.{ | ||
| 1843 | .tag = .array_type_sentinel, | ||
| 1844 | .main_token = lbracket, | ||
| 1845 | .data = .{ | ||
| 1846 | .lhs = len_expr, | ||
| 1847 | .rhs = try p.addExtra(.{ | ||
| 1848 | .elem_type = elem_type, | ||
| 1849 | .sentinel = sentinel, | ||
| 1850 | }), | ||
| 1851 | }, | ||
| 1852 | }); | ||
| 1853 | } | ||
| 1854 | } | ||
| 1855 | }, | ||
| 1856 | }, | ||
| 1857 | else => return p.parseErrorUnionExpr(), | ||
| 1257 | } | 1858 | } |
| 1859 | } | ||
| 1258 | 1860 | ||
| 1259 | if (inline_token == null) return null; | 1861 | fn expectTypeExpr(p: *Parser) Error!Node.Index { |
| 1260 | 1862 | const node = try p.parseTypeExpr(); | |
| 1261 | // If we've seen "inline", there should have been a "for" or "while" | 1863 | if (node == 0) { |
| 1262 | try p.errors.append(p.gpa, .{ | 1864 | return p.fail(.expected_type_expr); |
| 1263 | .ExpectedInlinable = .{ .token = p.tok_i }, | 1865 | } |
| 1264 | }); | 1866 | return node; |
| 1265 | return error.ParseError; | ||
| 1266 | } | 1867 | } |
| 1267 | 1868 | ||
| 1268 | /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? | 1869 | /// PrimaryExpr |
| 1269 | fn parseForExpr(p: *Parser) !?*Node { | 1870 | /// <- AsmExpr |
| 1270 | const node = (try p.parseForPrefix()) orelse return null; | 1871 | /// / IfExpr |
| 1271 | const for_prefix = node.cast(Node.For).?; | 1872 | /// / KEYWORD_break BreakLabel? Expr? |
| 1873 | /// / KEYWORD_comptime Expr | ||
| 1874 | /// / KEYWORD_nosuspend Expr | ||
| 1875 | /// / KEYWORD_continue BreakLabel? | ||
| 1876 | /// / KEYWORD_resume Expr | ||
| 1877 | /// / KEYWORD_return Expr? | ||
| 1878 | /// / BlockLabel? LoopExpr | ||
| 1879 | /// / Block | ||
| 1880 | /// / CurlySuffixExpr | ||
| 1881 | fn parsePrimaryExpr(p: *Parser) !Node.Index { | ||
| 1882 | switch (p.token_tags[p.tok_i]) { | ||
| 1883 | .keyword_asm => return p.expectAsmExpr(), | ||
| 1884 | .keyword_if => return p.parseIfExpr(), | ||
| 1885 | .keyword_break => { | ||
| 1886 | p.tok_i += 1; | ||
| 1887 | return p.addNode(.{ | ||
| 1888 | .tag = .@"break", | ||
| 1889 | .main_token = p.tok_i - 1, | ||
| 1890 | .data = .{ | ||
| 1891 | .lhs = try p.parseBreakLabel(), | ||
| 1892 | .rhs = try p.parseExpr(), | ||
| 1893 | }, | ||
| 1894 | }); | ||
| 1895 | }, | ||
| 1896 | .keyword_continue => { | ||
| 1897 | p.tok_i += 1; | ||
| 1898 | return p.addNode(.{ | ||
| 1899 | .tag = .@"continue", | ||
| 1900 | .main_token = p.tok_i - 1, | ||
| 1901 | .data = .{ | ||
| 1902 | .lhs = try p.parseBreakLabel(), | ||
| 1903 | .rhs = undefined, | ||
| 1904 | }, | ||
| 1905 | }); | ||
| 1906 | }, | ||
| 1907 | .keyword_comptime => { | ||
| 1908 | p.tok_i += 1; | ||
| 1909 | return p.addNode(.{ | ||
| 1910 | .tag = .@"comptime", | ||
| 1911 | .main_token = p.tok_i - 1, | ||
| 1912 | .data = .{ | ||
| 1913 | .lhs = try p.expectExpr(), | ||
| 1914 | .rhs = undefined, | ||
| 1915 | }, | ||
| 1916 | }); | ||
| 1917 | }, | ||
| 1918 | .keyword_nosuspend => { | ||
| 1919 | p.tok_i += 1; | ||
| 1920 | return p.addNode(.{ | ||
| 1921 | .tag = .@"nosuspend", | ||
| 1922 | .main_token = p.tok_i - 1, | ||
| 1923 | .data = .{ | ||
| 1924 | .lhs = try p.expectExpr(), | ||
| 1925 | .rhs = undefined, | ||
| 1926 | }, | ||
| 1927 | }); | ||
| 1928 | }, | ||
| 1929 | .keyword_resume => { | ||
| 1930 | p.tok_i += 1; | ||
| 1931 | return p.addNode(.{ | ||
| 1932 | .tag = .@"resume", | ||
| 1933 | .main_token = p.tok_i - 1, | ||
| 1934 | .data = .{ | ||
| 1935 | .lhs = try p.expectExpr(), | ||
| 1936 | .rhs = undefined, | ||
| 1937 | }, | ||
| 1938 | }); | ||
| 1939 | }, | ||
| 1940 | .keyword_return => { | ||
| 1941 | p.tok_i += 1; | ||
| 1942 | return p.addNode(.{ | ||
| 1943 | .tag = .@"return", | ||
| 1944 | .main_token = p.tok_i - 1, | ||
| 1945 | .data = .{ | ||
| 1946 | .lhs = try p.parseExpr(), | ||
| 1947 | .rhs = undefined, | ||
| 1948 | }, | ||
| 1949 | }); | ||
| 1950 | }, | ||
| 1951 | .identifier => { | ||
| 1952 | if (p.token_tags[p.tok_i + 1] == .colon) { | ||
| 1953 | switch (p.token_tags[p.tok_i + 2]) { | ||
| 1954 | .keyword_inline => { | ||
| 1955 | p.tok_i += 3; | ||
| 1956 | switch (p.token_tags[p.tok_i]) { | ||
| 1957 | .keyword_for => return p.parseForExpr(), | ||
| 1958 | .keyword_while => return p.parseWhileExpr(), | ||
| 1959 | else => return p.fail(.expected_inlinable), | ||
| 1960 | } | ||
| 1961 | }, | ||
| 1962 | .keyword_for => { | ||
| 1963 | p.tok_i += 2; | ||
| 1964 | return p.parseForExpr(); | ||
| 1965 | }, | ||
| 1966 | .keyword_while => { | ||
| 1967 | p.tok_i += 2; | ||
| 1968 | return p.parseWhileExpr(); | ||
| 1969 | }, | ||
| 1970 | .l_brace => { | ||
| 1971 | p.tok_i += 2; | ||
| 1972 | return p.parseBlock(); | ||
| 1973 | }, | ||
| 1974 | else => return p.parseCurlySuffixExpr(), | ||
| 1975 | } | ||
| 1976 | } else { | ||
| 1977 | return p.parseCurlySuffixExpr(); | ||
| 1978 | } | ||
| 1979 | }, | ||
| 1980 | .keyword_inline => { | ||
| 1981 | p.tok_i += 2; | ||
| 1982 | switch (p.token_tags[p.tok_i]) { | ||
| 1983 | .keyword_for => return p.parseForExpr(), | ||
| 1984 | .keyword_while => return p.parseWhileExpr(), | ||
| 1985 | else => return p.fail(.expected_inlinable), | ||
| 1986 | } | ||
| 1987 | }, | ||
| 1988 | .keyword_for => return p.parseForExpr(), | ||
| 1989 | .keyword_while => return p.parseWhileExpr(), | ||
| 1990 | .l_brace => return p.parseBlock(), | ||
| 1991 | else => return p.parseCurlySuffixExpr(), | ||
| 1992 | } | ||
| 1993 | } | ||
| 1272 | 1994 | ||
| 1273 | const body_node = try p.expectNode(parseExpr, .{ | 1995 | /// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? |
| 1274 | .ExpectedExpr = .{ .token = p.tok_i }, | 1996 | fn parseIfExpr(p: *Parser) !Node.Index { |
| 1275 | }); | 1997 | return p.parseIf(parseExpr); |
| 1276 | for_prefix.body = body_node; | 1998 | } |
| 1277 | 1999 | ||
| 1278 | if (p.eatToken(.Keyword_else)) |else_token| { | 2000 | /// Block <- LBRACE Statement* RBRACE |
| 1279 | const body = try p.expectNode(parseExpr, .{ | 2001 | fn parseBlock(p: *Parser) !Node.Index { |
| 1280 | .ExpectedExpr = .{ .token = p.tok_i }, | 2002 | const lbrace = p.eatToken(.l_brace) orelse return null_node; |
| 2003 | |||
| 2004 | if (p.eatToken(.r_brace)) |_| { | ||
| 2005 | return p.addNode(.{ | ||
| 2006 | .tag = .block_two, | ||
| 2007 | .main_token = lbrace, | ||
| 2008 | .data = .{ | ||
| 2009 | .lhs = 0, | ||
| 2010 | .rhs = 0, | ||
| 2011 | }, | ||
| 1281 | }); | 2012 | }); |
| 2013 | } | ||
| 1282 | 2014 | ||
| 1283 | const else_node = try p.arena.allocator.create(Node.Else); | 2015 | const stmt_one = try p.expectStatementRecoverable(); |
| 1284 | else_node.* = .{ | 2016 | if (p.eatToken(.r_brace)) |_| { |
| 1285 | .else_token = else_token, | 2017 | const semicolon = p.token_tags[p.tok_i - 2] == .semicolon; |
| 1286 | .payload = null, | 2018 | return p.addNode(.{ |
| 1287 | .body = body, | 2019 | .tag = if (semicolon) .block_two_semicolon else .block_two, |
| 1288 | }; | 2020 | .main_token = lbrace, |
| 1289 | 2021 | .data = .{ | |
| 1290 | for_prefix.@"else" = else_node; | 2022 | .lhs = stmt_one, |
| 2023 | .rhs = 0, | ||
| 2024 | }, | ||
| 2025 | }); | ||
| 2026 | } | ||
| 2027 | const stmt_two = try p.expectStatementRecoverable(); | ||
| 2028 | if (p.eatToken(.r_brace)) |_| { | ||
| 2029 | const semicolon = p.token_tags[p.tok_i - 2] == .semicolon; | ||
| 2030 | return p.addNode(.{ | ||
| 2031 | .tag = if (semicolon) .block_two_semicolon else .block_two, | ||
| 2032 | .main_token = lbrace, | ||
| 2033 | .data = .{ | ||
| 2034 | .lhs = stmt_one, | ||
| 2035 | .rhs = stmt_two, | ||
| 2036 | }, | ||
| 2037 | }); | ||
| 1291 | } | 2038 | } |
| 1292 | 2039 | ||
| 1293 | return node; | 2040 | var statements = std.ArrayList(Node.Index).init(p.gpa); |
| 1294 | } | 2041 | defer statements.deinit(); |
| 1295 | 2042 | ||
| 1296 | /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? | 2043 | try statements.appendSlice(&.{ stmt_one, stmt_two }); |
| 1297 | fn parseWhileExpr(p: *Parser) !?*Node { | ||
| 1298 | const node = (try p.parseWhilePrefix()) orelse return null; | ||
| 1299 | const while_prefix = node.cast(Node.While).?; | ||
| 1300 | 2044 | ||
| 1301 | const body_node = try p.expectNode(parseExpr, .{ | 2045 | while (true) { |
| 1302 | .ExpectedExpr = .{ .token = p.tok_i }, | 2046 | const statement = try p.expectStatementRecoverable(); |
| 2047 | if (statement == 0) break; | ||
| 2048 | try statements.append(statement); | ||
| 2049 | if (p.token_tags[p.tok_i] == .r_brace) break; | ||
| 2050 | } | ||
| 2051 | _ = try p.expectToken(.r_brace); | ||
| 2052 | const semicolon = p.token_tags[p.tok_i - 2] == .semicolon; | ||
| 2053 | const statements_span = try p.listToSpan(statements.items); | ||
| 2054 | return p.addNode(.{ | ||
| 2055 | .tag = if (semicolon) .block_semicolon else .block, | ||
| 2056 | .main_token = lbrace, | ||
| 2057 | .data = .{ | ||
| 2058 | .lhs = statements_span.start, | ||
| 2059 | .rhs = statements_span.end, | ||
| 2060 | }, | ||
| 1303 | }); | 2061 | }); |
| 1304 | while_prefix.body = body_node; | 2062 | } |
| 1305 | 2063 | ||
| 1306 | if (p.eatToken(.Keyword_else)) |else_token| { | 2064 | /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload |
| 1307 | const payload = try p.parsePayload(); | 2065 | /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? |
| 1308 | const body = try p.expectNode(parseExpr, .{ | 2066 | fn parseForExpr(p: *Parser) !Node.Index { |
| 1309 | .ExpectedExpr = .{ .token = p.tok_i }, | 2067 | const for_token = p.eatToken(.keyword_for) orelse return null_node; |
| 2068 | _ = try p.expectToken(.l_paren); | ||
| 2069 | const array_expr = try p.expectExpr(); | ||
| 2070 | _ = try p.expectToken(.r_paren); | ||
| 2071 | const found_payload = try p.parsePtrIndexPayload(); | ||
| 2072 | if (found_payload == 0) try p.warn(.expected_loop_payload); | ||
| 2073 | |||
| 2074 | const then_expr = try p.expectExpr(); | ||
| 2075 | const else_token = p.eatToken(.keyword_else) orelse { | ||
| 2076 | return p.addNode(.{ | ||
| 2077 | .tag = .for_simple, | ||
| 2078 | .main_token = for_token, | ||
| 2079 | .data = .{ | ||
| 2080 | .lhs = array_expr, | ||
| 2081 | .rhs = then_expr, | ||
| 2082 | }, | ||
| 1310 | }); | 2083 | }); |
| 1311 | 2084 | }; | |
| 1312 | const else_node = try p.arena.allocator.create(Node.Else); | 2085 | const else_expr = try p.expectExpr(); |
| 1313 | else_node.* = .{ | 2086 | return p.addNode(.{ |
| 1314 | .else_token = else_token, | 2087 | .tag = .@"for", |
| 1315 | .payload = payload, | 2088 | .main_token = for_token, |
| 1316 | .body = body, | 2089 | .data = .{ |
| 1317 | }; | 2090 | .lhs = array_expr, |
| 1318 | 2091 | .rhs = try p.addExtra(Node.If{ | |
| 1319 | while_prefix.@"else" = else_node; | 2092 | .then_expr = then_expr, |
| 1320 | } | 2093 | .else_expr = else_expr, |
| 1321 | 2094 | }), | |
| 1322 | return node; | 2095 | }, |
| 2096 | }); | ||
| 1323 | } | 2097 | } |
| 1324 | 2098 | ||
| 1325 | /// CurlySuffixExpr <- TypeExpr InitList? | 2099 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? |
| 1326 | fn parseCurlySuffixExpr(p: *Parser) !?*Node { | 2100 | /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? |
| 1327 | const lhs = (try p.parseTypeExpr()) orelse return null; | 2101 | fn parseWhileExpr(p: *Parser) !Node.Index { |
| 1328 | const suffix_op = (try p.parseInitList(lhs)) orelse return lhs; | 2102 | const while_token = p.eatToken(.keyword_while) orelse return null_node; |
| 1329 | return suffix_op; | 2103 | _ = try p.expectToken(.l_paren); |
| 2104 | const condition = try p.expectExpr(); | ||
| 2105 | _ = try p.expectToken(.r_paren); | ||
| 2106 | const then_payload = try p.parsePtrPayload(); | ||
| 2107 | const cont_expr = try p.parseWhileContinueExpr(); | ||
| 2108 | |||
| 2109 | const then_expr = try p.expectExpr(); | ||
| 2110 | const else_token = p.eatToken(.keyword_else) orelse { | ||
| 2111 | if (cont_expr == 0) { | ||
| 2112 | return p.addNode(.{ | ||
| 2113 | .tag = .while_simple, | ||
| 2114 | .main_token = while_token, | ||
| 2115 | .data = .{ | ||
| 2116 | .lhs = condition, | ||
| 2117 | .rhs = then_expr, | ||
| 2118 | }, | ||
| 2119 | }); | ||
| 2120 | } else { | ||
| 2121 | return p.addNode(.{ | ||
| 2122 | .tag = .while_cont, | ||
| 2123 | .main_token = while_token, | ||
| 2124 | .data = .{ | ||
| 2125 | .lhs = condition, | ||
| 2126 | .rhs = try p.addExtra(Node.WhileCont{ | ||
| 2127 | .cont_expr = cont_expr, | ||
| 2128 | .then_expr = then_expr, | ||
| 2129 | }), | ||
| 2130 | }, | ||
| 2131 | }); | ||
| 2132 | } | ||
| 2133 | }; | ||
| 2134 | const else_payload = try p.parsePayload(); | ||
| 2135 | const else_expr = try p.expectExpr(); | ||
| 2136 | return p.addNode(.{ | ||
| 2137 | .tag = .@"while", | ||
| 2138 | .main_token = while_token, | ||
| 2139 | .data = .{ | ||
| 2140 | .lhs = condition, | ||
| 2141 | .rhs = try p.addExtra(Node.While{ | ||
| 2142 | .cont_expr = cont_expr, | ||
| 2143 | .then_expr = then_expr, | ||
| 2144 | .else_expr = else_expr, | ||
| 2145 | }), | ||
| 2146 | }, | ||
| 2147 | }); | ||
| 1330 | } | 2148 | } |
| 1331 | 2149 | ||
| 2150 | /// CurlySuffixExpr <- TypeExpr InitList? | ||
| 1332 | /// InitList | 2151 | /// InitList |
| 1333 | /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE | 2152 | /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE |
| 1334 | /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE | 2153 | /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE |
| 1335 | /// / LBRACE RBRACE | 2154 | /// / LBRACE RBRACE |
| 1336 | fn parseInitList(p: *Parser, lhs: *Node) !?*Node { | 2155 | fn parseCurlySuffixExpr(p: *Parser) !Node.Index { |
| 1337 | const lbrace = p.eatToken(.LBrace) orelse return null; | 2156 | const lhs = try p.parseTypeExpr(); |
| 1338 | var init_list = std.ArrayList(*Node).init(p.gpa); | 2157 | if (lhs == 0) return null_node; |
| 1339 | defer init_list.deinit(); | 2158 | const lbrace = p.eatToken(.l_brace) orelse return lhs; |
| 2159 | |||
| 2160 | // If there are 0 or 1 items, we can use ArrayInitOne/StructInitOne; | ||
| 2161 | // otherwise we use the full ArrayInit/StructInit. | ||
| 2162 | |||
| 2163 | if (p.eatToken(.r_brace)) |_| { | ||
| 2164 | return p.addNode(.{ | ||
| 2165 | .tag = .struct_init_one, | ||
| 2166 | .main_token = lbrace, | ||
| 2167 | .data = .{ | ||
| 2168 | .lhs = lhs, | ||
| 2169 | .rhs = 0, | ||
| 2170 | }, | ||
| 2171 | }); | ||
| 2172 | } | ||
| 2173 | const field_init = try p.parseFieldInit(); | ||
| 2174 | if (field_init != 0) { | ||
| 2175 | const comma_one = p.eatToken(.comma); | ||
| 2176 | if (p.eatToken(.r_brace)) |_| { | ||
| 2177 | return p.addNode(.{ | ||
| 2178 | .tag = if (comma_one != null) .struct_init_one_comma else .struct_init_one, | ||
| 2179 | .main_token = lbrace, | ||
| 2180 | .data = .{ | ||
| 2181 | .lhs = lhs, | ||
| 2182 | .rhs = field_init, | ||
| 2183 | }, | ||
| 2184 | }); | ||
| 2185 | } | ||
| 2186 | |||
| 2187 | var init_list = std.ArrayList(Node.Index).init(p.gpa); | ||
| 2188 | defer init_list.deinit(); | ||
| 1340 | 2189 | ||
| 1341 | if (try p.parseFieldInit()) |field_init| { | ||
| 1342 | try init_list.append(field_init); | 2190 | try init_list.append(field_init); |
| 1343 | while (p.eatToken(.Comma)) |_| { | ||
| 1344 | const next = (try p.parseFieldInit()) orelse break; | ||
| 1345 | try init_list.append(next); | ||
| 1346 | } | ||
| 1347 | const node = try Node.StructInitializer.alloc(&p.arena.allocator, init_list.items.len); | ||
| 1348 | node.* = .{ | ||
| 1349 | .lhs = lhs, | ||
| 1350 | .rtoken = try p.expectToken(.RBrace), | ||
| 1351 | .list_len = init_list.items.len, | ||
| 1352 | }; | ||
| 1353 | std.mem.copy(*Node, node.list(), init_list.items); | ||
| 1354 | return &node.base; | ||
| 1355 | } | ||
| 1356 | 2191 | ||
| 1357 | if (try p.parseExpr()) |expr| { | 2192 | while (true) { |
| 1358 | try init_list.append(expr); | 2193 | const next = try p.expectFieldInit(); |
| 1359 | while (p.eatToken(.Comma)) |_| { | ||
| 1360 | const next = (try p.parseExpr()) orelse break; | ||
| 1361 | try init_list.append(next); | 2194 | try init_list.append(next); |
| 2195 | |||
| 2196 | switch (p.token_tags[p.nextToken()]) { | ||
| 2197 | .comma => { | ||
| 2198 | if (p.eatToken(.r_brace)) |_| break; | ||
| 2199 | continue; | ||
| 2200 | }, | ||
| 2201 | .r_brace => break, | ||
| 2202 | .colon, .r_paren, .r_bracket => { | ||
| 2203 | p.tok_i -= 1; | ||
| 2204 | return p.failExpected(.r_brace); | ||
| 2205 | }, | ||
| 2206 | else => { | ||
| 2207 | // This is likely just a missing comma; | ||
| 2208 | // give an error but continue parsing this list. | ||
| 2209 | p.tok_i -= 1; | ||
| 2210 | try p.warnExpected(.comma); | ||
| 2211 | }, | ||
| 2212 | } | ||
| 1362 | } | 2213 | } |
| 1363 | const node = try Node.ArrayInitializer.alloc(&p.arena.allocator, init_list.items.len); | 2214 | const span = try p.listToSpan(init_list.items); |
| 1364 | node.* = .{ | 2215 | return p.addNode(.{ |
| 1365 | .lhs = lhs, | 2216 | .tag = if (p.token_tags[p.tok_i - 2] == .comma) .struct_init_comma else .struct_init, |
| 1366 | .rtoken = try p.expectToken(.RBrace), | 2217 | .main_token = lbrace, |
| 1367 | .list_len = init_list.items.len, | 2218 | .data = .{ |
| 1368 | }; | 2219 | .lhs = lhs, |
| 1369 | std.mem.copy(*Node, node.list(), init_list.items); | 2220 | .rhs = try p.addExtra(Node.SubRange{ |
| 1370 | return &node.base; | 2221 | .start = span.start, |
| 2222 | .end = span.end, | ||
| 2223 | }), | ||
| 2224 | }, | ||
| 2225 | }); | ||
| 1371 | } | 2226 | } |
| 1372 | 2227 | ||
| 1373 | const node = try p.arena.allocator.create(Node.StructInitializer); | 2228 | const elem_init = try p.expectExpr(); |
| 1374 | node.* = .{ | 2229 | const comma_one = p.eatToken(.comma); |
| 1375 | .lhs = lhs, | 2230 | if (p.eatToken(.r_brace)) |_| { |
| 1376 | .rtoken = try p.expectToken(.RBrace), | 2231 | return p.addNode(.{ |
| 1377 | .list_len = 0, | 2232 | .tag = if (comma_one != null) .array_init_one_comma else .array_init_one, |
| 1378 | }; | 2233 | .main_token = lbrace, |
| 1379 | return &node.base; | 2234 | .data = .{ |
| 1380 | } | 2235 | .lhs = lhs, |
| 2236 | .rhs = elem_init, | ||
| 2237 | }, | ||
| 2238 | }); | ||
| 2239 | } | ||
| 2240 | if (comma_one == null) { | ||
| 2241 | try p.warnExpected(.comma); | ||
| 2242 | } | ||
| 1381 | 2243 | ||
| 1382 | /// InitList | 2244 | var init_list = std.ArrayList(Node.Index).init(p.gpa); |
| 1383 | /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE | ||
| 1384 | /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE | ||
| 1385 | /// / LBRACE RBRACE | ||
| 1386 | fn parseAnonInitList(p: *Parser, dot: TokenIndex) !?*Node { | ||
| 1387 | const lbrace = p.eatToken(.LBrace) orelse return null; | ||
| 1388 | var init_list = std.ArrayList(*Node).init(p.gpa); | ||
| 1389 | defer init_list.deinit(); | 2245 | defer init_list.deinit(); |
| 1390 | 2246 | ||
| 1391 | if (try p.parseFieldInit()) |field_init| { | 2247 | try init_list.append(elem_init); |
| 1392 | try init_list.append(field_init); | ||
| 1393 | while (p.eatToken(.Comma)) |_| { | ||
| 1394 | const next = (try p.parseFieldInit()) orelse break; | ||
| 1395 | try init_list.append(next); | ||
| 1396 | } | ||
| 1397 | const node = try Node.StructInitializerDot.alloc(&p.arena.allocator, init_list.items.len); | ||
| 1398 | node.* = .{ | ||
| 1399 | .dot = dot, | ||
| 1400 | .rtoken = try p.expectToken(.RBrace), | ||
| 1401 | .list_len = init_list.items.len, | ||
| 1402 | }; | ||
| 1403 | std.mem.copy(*Node, node.list(), init_list.items); | ||
| 1404 | return &node.base; | ||
| 1405 | } | ||
| 1406 | 2248 | ||
| 1407 | if (try p.parseExpr()) |expr| { | 2249 | var trailing_comma = true; |
| 1408 | try init_list.append(expr); | 2250 | var next = try p.parseExpr(); |
| 1409 | while (p.eatToken(.Comma)) |_| { | 2251 | while (next != 0) : (next = try p.parseExpr()) { |
| 1410 | const next = (try p.parseExpr()) orelse break; | 2252 | try init_list.append(next); |
| 1411 | try init_list.append(next); | 2253 | if (p.eatToken(.comma) == null) { |
| 2254 | trailing_comma = false; | ||
| 2255 | break; | ||
| 1412 | } | 2256 | } |
| 1413 | const node = try Node.ArrayInitializerDot.alloc(&p.arena.allocator, init_list.items.len); | ||
| 1414 | node.* = .{ | ||
| 1415 | .dot = dot, | ||
| 1416 | .rtoken = try p.expectToken(.RBrace), | ||
| 1417 | .list_len = init_list.items.len, | ||
| 1418 | }; | ||
| 1419 | std.mem.copy(*Node, node.list(), init_list.items); | ||
| 1420 | return &node.base; | ||
| 1421 | } | 2257 | } |
| 1422 | 2258 | _ = try p.expectToken(.r_brace); | |
| 1423 | const node = try p.arena.allocator.create(Node.StructInitializerDot); | 2259 | const span = try p.listToSpan(init_list.items); |
| 1424 | node.* = .{ | 2260 | return p.addNode(.{ |
| 1425 | .dot = dot, | 2261 | .tag = if (trailing_comma) .array_init_comma else .array_init, |
| 1426 | .rtoken = try p.expectToken(.RBrace), | 2262 | .main_token = lbrace, |
| 1427 | .list_len = 0, | 2263 | .data = .{ |
| 1428 | }; | 2264 | .lhs = lhs, |
| 1429 | return &node.base; | 2265 | .rhs = try p.addExtra(Node.SubRange{ |
| 1430 | } | 2266 | .start = span.start, |
| 1431 | 2267 | .end = span.end, | |
| 1432 | /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr | 2268 | }), |
| 1433 | fn parseTypeExpr(p: *Parser) Error!?*Node { | 2269 | }, |
| 1434 | return p.parsePrefixOpExpr(parsePrefixTypeOp, parseErrorUnionExpr); | 2270 | }); |
| 1435 | } | 2271 | } |
| 1436 | 2272 | ||
| 1437 | /// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? | 2273 | /// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? |
| 1438 | fn parseErrorUnionExpr(p: *Parser) !?*Node { | 2274 | fn parseErrorUnionExpr(p: *Parser) !Node.Index { |
| 1439 | const suffix_expr = (try p.parseSuffixExpr()) orelse return null; | 2275 | const suffix_expr = try p.parseSuffixExpr(); |
| 1440 | 2276 | if (suffix_expr == 0) return null_node; | |
| 1441 | if (try SimpleBinOpParseFn(.Bang, .ErrorUnion)(p)) |node| { | 2277 | const bang = p.eatToken(.bang) orelse return suffix_expr; |
| 1442 | const error_union = node.castTag(.ErrorUnion).?; | 2278 | return p.addNode(.{ |
| 1443 | const type_expr = try p.expectNode(parseTypeExpr, .{ | 2279 | .tag = .error_union, |
| 1444 | .ExpectedTypeExpr = .{ .token = p.tok_i }, | 2280 | .main_token = bang, |
| 1445 | }); | 2281 | .data = .{ |
| 1446 | error_union.lhs = suffix_expr; | 2282 | .lhs = suffix_expr, |
| 1447 | error_union.rhs = type_expr; | 2283 | .rhs = try p.expectTypeExpr(), |
| 1448 | return node; | 2284 | }, |
| 1449 | } | 2285 | }); |
| 1450 | |||
| 1451 | return suffix_expr; | ||
| 1452 | } | 2286 | } |
| 1453 | 2287 | ||
| 1454 | /// SuffixExpr | 2288 | /// SuffixExpr |
| 1455 | /// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments | 2289 | /// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments |
| 1456 | /// / PrimaryTypeExpr (SuffixOp / FnCallArguments)* | 2290 | /// / PrimaryTypeExpr (SuffixOp / FnCallArguments)* |
| 1457 | fn parseSuffixExpr(p: *Parser) !?*Node { | 2291 | /// FnCallArguments <- LPAREN ExprList RPAREN |
| 1458 | const maybe_async = p.eatToken(.Keyword_async); | 2292 | /// ExprList <- (Expr COMMA)* Expr? |
| 1459 | if (maybe_async) |async_token| { | 2293 | fn parseSuffixExpr(p: *Parser) !Node.Index { |
| 1460 | const token_fn = p.eatToken(.Keyword_fn); | 2294 | if (p.eatToken(.keyword_async)) |async_token| { |
| 1461 | if (token_fn != null) { | 2295 | var res = try p.expectPrimaryTypeExpr(); |
| 1462 | // TODO: remove this hack when async fn rewriting is | ||
| 1463 | // HACK: If we see the keyword `fn`, then we assume that | ||
| 1464 | // we are parsing an async fn proto, and not a call. | ||
| 1465 | // We therefore put back all tokens consumed by the async | ||
| 1466 | // prefix... | ||
| 1467 | p.putBackToken(token_fn.?); | ||
| 1468 | p.putBackToken(async_token); | ||
| 1469 | return p.parsePrimaryTypeExpr(); | ||
| 1470 | } | ||
| 1471 | var res = try p.expectNode(parsePrimaryTypeExpr, .{ | ||
| 1472 | .ExpectedPrimaryTypeExpr = .{ .token = p.tok_i }, | ||
| 1473 | }); | ||
| 1474 | 2296 | ||
| 1475 | while (try p.parseSuffixOp(res)) |node| { | 2297 | while (true) { |
| 2298 | const node = try p.parseSuffixOp(res); | ||
| 2299 | if (node == 0) break; | ||
| 1476 | res = node; | 2300 | res = node; |
| 1477 | } | 2301 | } |
| 1478 | 2302 | const lparen = p.nextToken(); | |
| 1479 | const params = (try p.parseFnCallArguments()) orelse { | 2303 | if (p.token_tags[lparen] != .l_paren) { |
| 1480 | try p.errors.append(p.gpa, .{ | 2304 | p.tok_i -= 1; |
| 1481 | .ExpectedParamList = .{ .token = p.tok_i }, | 2305 | try p.warn(.expected_param_list); |
| 1482 | }); | ||
| 1483 | // ignore this, continue parsing | ||
| 1484 | return res; | 2306 | return res; |
| 1485 | }; | 2307 | } |
| 1486 | defer p.gpa.free(params.list); | 2308 | if (p.eatToken(.r_paren)) |_| { |
| 1487 | const node = try Node.Call.alloc(&p.arena.allocator, params.list.len); | 2309 | return p.addNode(.{ |
| 1488 | node.* = .{ | 2310 | .tag = .async_call_one, |
| 1489 | .lhs = res, | 2311 | .main_token = lparen, |
| 1490 | .params_len = params.list.len, | 2312 | .data = .{ |
| 1491 | .async_token = async_token, | 2313 | .lhs = res, |
| 1492 | .rtoken = params.rparen, | 2314 | .rhs = 0, |
| 1493 | }; | 2315 | }, |
| 1494 | std.mem.copy(*Node, node.params(), params.list); | 2316 | }); |
| 1495 | return &node.base; | 2317 | } |
| 1496 | } | 2318 | const param_one = try p.expectExpr(); |
| 1497 | if (try p.parsePrimaryTypeExpr()) |expr| { | 2319 | const comma_one = p.eatToken(.comma); |
| 1498 | var res = expr; | 2320 | if (p.eatToken(.r_paren)) |_| { |
| 2321 | return p.addNode(.{ | ||
| 2322 | .tag = if (comma_one == null) .async_call_one else .async_call_one_comma, | ||
| 2323 | .main_token = lparen, | ||
| 2324 | .data = .{ | ||
| 2325 | .lhs = res, | ||
| 2326 | .rhs = param_one, | ||
| 2327 | }, | ||
| 2328 | }); | ||
| 2329 | } | ||
| 2330 | if (comma_one == null) { | ||
| 2331 | try p.warnExpected(.comma); | ||
| 2332 | } | ||
| 2333 | |||
| 2334 | var param_list = std.ArrayList(Node.Index).init(p.gpa); | ||
| 2335 | defer param_list.deinit(); | ||
| 2336 | |||
| 2337 | try param_list.append(param_one); | ||
| 1499 | 2338 | ||
| 1500 | while (true) { | 2339 | while (true) { |
| 1501 | if (try p.parseSuffixOp(res)) |node| { | 2340 | const next = try p.expectExpr(); |
| 1502 | res = node; | 2341 | try param_list.append(next); |
| 1503 | continue; | 2342 | switch (p.token_tags[p.nextToken()]) { |
| 1504 | } | 2343 | .comma => { |
| 1505 | if (try p.parseFnCallArguments()) |params| { | 2344 | if (p.eatToken(.r_paren)) |_| { |
| 1506 | defer p.gpa.free(params.list); | 2345 | const span = try p.listToSpan(param_list.items); |
| 1507 | const call = try Node.Call.alloc(&p.arena.allocator, params.list.len); | 2346 | return p.addNode(.{ |
| 1508 | call.* = .{ | 2347 | .tag = .async_call_comma, |
| 1509 | .lhs = res, | 2348 | .main_token = lparen, |
| 1510 | .params_len = params.list.len, | 2349 | .data = .{ |
| 1511 | .async_token = null, | 2350 | .lhs = res, |
| 1512 | .rtoken = params.rparen, | 2351 | .rhs = try p.addExtra(Node.SubRange{ |
| 1513 | }; | 2352 | .start = span.start, |
| 1514 | std.mem.copy(*Node, call.params(), params.list); | 2353 | .end = span.end, |
| 1515 | res = &call.base; | 2354 | }), |
| 1516 | continue; | 2355 | }, |
| 2356 | }); | ||
| 2357 | } else { | ||
| 2358 | continue; | ||
| 2359 | } | ||
| 2360 | }, | ||
| 2361 | .r_paren => { | ||
| 2362 | const span = try p.listToSpan(param_list.items); | ||
| 2363 | return p.addNode(.{ | ||
| 2364 | .tag = .async_call, | ||
| 2365 | .main_token = lparen, | ||
| 2366 | .data = .{ | ||
| 2367 | .lhs = res, | ||
| 2368 | .rhs = try p.addExtra(Node.SubRange{ | ||
| 2369 | .start = span.start, | ||
| 2370 | .end = span.end, | ||
| 2371 | }), | ||
| 2372 | }, | ||
| 2373 | }); | ||
| 2374 | }, | ||
| 2375 | .colon, .r_brace, .r_bracket => { | ||
| 2376 | p.tok_i -= 1; | ||
| 2377 | return p.failExpected(.r_paren); | ||
| 2378 | }, | ||
| 2379 | else => { | ||
| 2380 | p.tok_i -= 1; | ||
| 2381 | try p.warnExpected(.comma); | ||
| 2382 | }, | ||
| 1517 | } | 2383 | } |
| 1518 | break; | ||
| 1519 | } | 2384 | } |
| 1520 | return res; | ||
| 1521 | } | 2385 | } |
| 2386 | var res = try p.parsePrimaryTypeExpr(); | ||
| 2387 | if (res == 0) return res; | ||
| 1522 | 2388 | ||
| 1523 | return null; | 2389 | while (true) { |
| 2390 | const suffix_op = try p.parseSuffixOp(res); | ||
| 2391 | if (suffix_op != 0) { | ||
| 2392 | res = suffix_op; | ||
| 2393 | continue; | ||
| 2394 | } | ||
| 2395 | res = res: { | ||
| 2396 | const lparen = p.eatToken(.l_paren) orelse return res; | ||
| 2397 | if (p.eatToken(.r_paren)) |_| { | ||
| 2398 | break :res try p.addNode(.{ | ||
| 2399 | .tag = .call_one, | ||
| 2400 | .main_token = lparen, | ||
| 2401 | .data = .{ | ||
| 2402 | .lhs = res, | ||
| 2403 | .rhs = 0, | ||
| 2404 | }, | ||
| 2405 | }); | ||
| 2406 | } | ||
| 2407 | const param_one = try p.expectExpr(); | ||
| 2408 | const comma_one = p.eatToken(.comma); | ||
| 2409 | if (p.eatToken(.r_paren)) |_| { | ||
| 2410 | break :res try p.addNode(.{ | ||
| 2411 | .tag = if (comma_one == null) .call_one else .call_one_comma, | ||
| 2412 | .main_token = lparen, | ||
| 2413 | .data = .{ | ||
| 2414 | .lhs = res, | ||
| 2415 | .rhs = param_one, | ||
| 2416 | }, | ||
| 2417 | }); | ||
| 2418 | } | ||
| 2419 | if (comma_one == null) { | ||
| 2420 | try p.warnExpected(.comma); | ||
| 2421 | } | ||
| 2422 | |||
| 2423 | var param_list = std.ArrayList(Node.Index).init(p.gpa); | ||
| 2424 | defer param_list.deinit(); | ||
| 2425 | |||
| 2426 | try param_list.append(param_one); | ||
| 2427 | |||
| 2428 | while (true) { | ||
| 2429 | const next = try p.expectExpr(); | ||
| 2430 | try param_list.append(next); | ||
| 2431 | switch (p.token_tags[p.nextToken()]) { | ||
| 2432 | .comma => { | ||
| 2433 | if (p.eatToken(.r_paren)) |_| { | ||
| 2434 | const span = try p.listToSpan(param_list.items); | ||
| 2435 | break :res try p.addNode(.{ | ||
| 2436 | .tag = .call_comma, | ||
| 2437 | .main_token = lparen, | ||
| 2438 | .data = .{ | ||
| 2439 | .lhs = res, | ||
| 2440 | .rhs = try p.addExtra(Node.SubRange{ | ||
| 2441 | .start = span.start, | ||
| 2442 | .end = span.end, | ||
| 2443 | }), | ||
| 2444 | }, | ||
| 2445 | }); | ||
| 2446 | } else { | ||
| 2447 | continue; | ||
| 2448 | } | ||
| 2449 | }, | ||
| 2450 | .r_paren => { | ||
| 2451 | const span = try p.listToSpan(param_list.items); | ||
| 2452 | break :res try p.addNode(.{ | ||
| 2453 | .tag = .call, | ||
| 2454 | .main_token = lparen, | ||
| 2455 | .data = .{ | ||
| 2456 | .lhs = res, | ||
| 2457 | .rhs = try p.addExtra(Node.SubRange{ | ||
| 2458 | .start = span.start, | ||
| 2459 | .end = span.end, | ||
| 2460 | }), | ||
| 2461 | }, | ||
| 2462 | }); | ||
| 2463 | }, | ||
| 2464 | .colon, .r_brace, .r_bracket => { | ||
| 2465 | p.tok_i -= 1; | ||
| 2466 | return p.failExpected(.r_paren); | ||
| 2467 | }, | ||
| 2468 | else => { | ||
| 2469 | p.tok_i -= 1; | ||
| 2470 | try p.warnExpected(.comma); | ||
| 2471 | }, | ||
| 2472 | } | ||
| 2473 | } | ||
| 2474 | }; | ||
| 2475 | } | ||
| 1524 | } | 2476 | } |
| 1525 | 2477 | ||
| 1526 | /// PrimaryTypeExpr | 2478 | /// PrimaryTypeExpr |
| ... | @@ -1528,6 +2480,7 @@ const Parser = struct { | ... | @@ -1528,6 +2480,7 @@ const Parser = struct { |
| 1528 | /// / CHAR_LITERAL | 2480 | /// / CHAR_LITERAL |
| 1529 | /// / ContainerDecl | 2481 | /// / ContainerDecl |
| 1530 | /// / DOT IDENTIFIER | 2482 | /// / DOT IDENTIFIER |
| 2483 | /// / DOT InitList | ||
| 1531 | /// / ErrorSetDecl | 2484 | /// / ErrorSetDecl |
| 1532 | /// / FLOAT | 2485 | /// / FLOAT |
| 1533 | /// / FnProto | 2486 | /// / FnProto |
| ... | @@ -1546,260 +2499,546 @@ const Parser = struct { | ... | @@ -1546,260 +2499,546 @@ const Parser = struct { |
| 1546 | /// / KEYWORD_unreachable | 2499 | /// / KEYWORD_unreachable |
| 1547 | /// / STRINGLITERAL | 2500 | /// / STRINGLITERAL |
| 1548 | /// / SwitchExpr | 2501 | /// / SwitchExpr |
| 1549 | fn parsePrimaryTypeExpr(p: *Parser) !?*Node { | ||
| 1550 | if (try p.parseBuiltinCall()) |node| return node; | ||
| 1551 | if (p.eatToken(.CharLiteral)) |token| { | ||
| 1552 | const node = try p.arena.allocator.create(Node.OneToken); | ||
| 1553 | node.* = .{ | ||
| 1554 | .base = .{ .tag = .CharLiteral }, | ||
| 1555 | .token = token, | ||
| 1556 | }; | ||
| 1557 | return &node.base; | ||
| 1558 | } | ||
| 1559 | if (try p.parseContainerDecl()) |node| return node; | ||
| 1560 | if (try p.parseAnonLiteral()) |node| return node; | ||
| 1561 | if (try p.parseErrorSetDecl()) |node| return node; | ||
| 1562 | if (try p.parseFloatLiteral()) |node| return node; | ||
| 1563 | if (try p.parseFnProto(.as_type, .{})) |node| return node; | ||
| 1564 | if (try p.parseGroupedExpr()) |node| return node; | ||
| 1565 | if (try p.parseLabeledTypeExpr()) |node| return node; | ||
| 1566 | if (try p.parseIdentifier()) |node| return node; | ||
| 1567 | if (try p.parseIfTypeExpr()) |node| return node; | ||
| 1568 | if (try p.parseIntegerLiteral()) |node| return node; | ||
| 1569 | if (p.eatToken(.Keyword_comptime)) |token| { | ||
| 1570 | const expr = (try p.parseTypeExpr()) orelse return null; | ||
| 1571 | const node = try p.arena.allocator.create(Node.Comptime); | ||
| 1572 | node.* = .{ | ||
| 1573 | .doc_comments = null, | ||
| 1574 | .comptime_token = token, | ||
| 1575 | .expr = expr, | ||
| 1576 | }; | ||
| 1577 | return &node.base; | ||
| 1578 | } | ||
| 1579 | if (p.eatToken(.Keyword_error)) |token| { | ||
| 1580 | const period = try p.expectTokenRecoverable(.Period); | ||
| 1581 | const identifier = try p.expectNodeRecoverable(parseIdentifier, .{ | ||
| 1582 | .ExpectedIdentifier = .{ .token = p.tok_i }, | ||
| 1583 | }); | ||
| 1584 | const global_error_set = try p.createLiteral(.ErrorType, token); | ||
| 1585 | if (period == null or identifier == null) return global_error_set; | ||
| 1586 | |||
| 1587 | const node = try p.arena.allocator.create(Node.SimpleInfixOp); | ||
| 1588 | node.* = .{ | ||
| 1589 | .base = Node{ .tag = .Period }, | ||
| 1590 | .op_token = period.?, | ||
| 1591 | .lhs = global_error_set, | ||
| 1592 | .rhs = identifier.?, | ||
| 1593 | }; | ||
| 1594 | return &node.base; | ||
| 1595 | } | ||
| 1596 | if (p.eatToken(.Keyword_false)) |token| return p.createLiteral(.BoolLiteral, token); | ||
| 1597 | if (p.eatToken(.Keyword_null)) |token| return p.createLiteral(.NullLiteral, token); | ||
| 1598 | if (p.eatToken(.Keyword_anyframe)) |token| { | ||
| 1599 | const node = try p.arena.allocator.create(Node.AnyFrameType); | ||
| 1600 | node.* = .{ | ||
| 1601 | .anyframe_token = token, | ||
| 1602 | .result = null, | ||
| 1603 | }; | ||
| 1604 | return &node.base; | ||
| 1605 | } | ||
| 1606 | if (p.eatToken(.Keyword_true)) |token| return p.createLiteral(.BoolLiteral, token); | ||
| 1607 | if (p.eatToken(.Keyword_undefined)) |token| return p.createLiteral(.UndefinedLiteral, token); | ||
| 1608 | if (p.eatToken(.Keyword_unreachable)) |token| return p.createLiteral(.Unreachable, token); | ||
| 1609 | if (try p.parseStringLiteral()) |node| return node; | ||
| 1610 | if (try p.parseSwitchExpr()) |node| return node; | ||
| 1611 | |||
| 1612 | return null; | ||
| 1613 | } | ||
| 1614 | |||
| 1615 | /// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto | 2502 | /// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto |
| 1616 | fn parseContainerDecl(p: *Parser) !?*Node { | 2503 | /// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE |
| 1617 | const layout_token = p.eatToken(.Keyword_extern) orelse | 2504 | /// InitList |
| 1618 | p.eatToken(.Keyword_packed); | 2505 | /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE |
| 1619 | 2506 | /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE | |
| 1620 | const node = (try p.parseContainerDeclAuto()) orelse { | 2507 | /// / LBRACE RBRACE |
| 1621 | if (layout_token) |token| | ||
| 1622 | p.putBackToken(token); | ||
| 1623 | return null; | ||
| 1624 | }; | ||
| 1625 | node.cast(Node.ContainerDecl).?.*.layout_token = layout_token; | ||
| 1626 | return node; | ||
| 1627 | } | ||
| 1628 | |||
| 1629 | /// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE | 2508 | /// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE |
| 1630 | fn parseErrorSetDecl(p: *Parser) !?*Node { | ||
| 1631 | const error_token = p.eatToken(.Keyword_error) orelse return null; | ||
| 1632 | if (p.eatToken(.LBrace) == null) { | ||
| 1633 | // Might parse as `KEYWORD_error DOT IDENTIFIER` later in PrimaryTypeExpr, so don't error | ||
| 1634 | p.putBackToken(error_token); | ||
| 1635 | return null; | ||
| 1636 | } | ||
| 1637 | const decls = try p.parseErrorTagList(); | ||
| 1638 | defer p.gpa.free(decls); | ||
| 1639 | const rbrace = try p.expectToken(.RBrace); | ||
| 1640 | |||
| 1641 | const node = try Node.ErrorSetDecl.alloc(&p.arena.allocator, decls.len); | ||
| 1642 | node.* = .{ | ||
| 1643 | .error_token = error_token, | ||
| 1644 | .decls_len = decls.len, | ||
| 1645 | .rbrace_token = rbrace, | ||
| 1646 | }; | ||
| 1647 | std.mem.copy(*Node, node.decls(), decls); | ||
| 1648 | return &node.base; | ||
| 1649 | } | ||
| 1650 | |||
| 1651 | /// GroupedExpr <- LPAREN Expr RPAREN | 2509 | /// GroupedExpr <- LPAREN Expr RPAREN |
| 1652 | fn parseGroupedExpr(p: *Parser) !?*Node { | ||
| 1653 | const lparen = p.eatToken(.LParen) orelse return null; | ||
| 1654 | const expr = try p.expectNode(parseExpr, .{ | ||
| 1655 | .ExpectedExpr = .{ .token = p.tok_i }, | ||
| 1656 | }); | ||
| 1657 | const rparen = try p.expectToken(.RParen); | ||
| 1658 | |||
| 1659 | const node = try p.arena.allocator.create(Node.GroupedExpression); | ||
| 1660 | node.* = .{ | ||
| 1661 | .lparen = lparen, | ||
| 1662 | .expr = expr, | ||
| 1663 | .rparen = rparen, | ||
| 1664 | }; | ||
| 1665 | return &node.base; | ||
| 1666 | } | ||
| 1667 | |||
| 1668 | /// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? | 2510 | /// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? |
| 1669 | fn parseIfTypeExpr(p: *Parser) !?*Node { | ||
| 1670 | return p.parseIf(parseTypeExpr); | ||
| 1671 | } | ||
| 1672 | |||
| 1673 | /// LabeledTypeExpr | 2511 | /// LabeledTypeExpr |
| 1674 | /// <- BlockLabel Block | 2512 | /// <- BlockLabel Block |
| 1675 | /// / BlockLabel? LoopTypeExpr | 2513 | /// / BlockLabel? LoopTypeExpr |
| 1676 | fn parseLabeledTypeExpr(p: *Parser) !?*Node { | 2514 | /// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr) |
| 1677 | var colon: TokenIndex = undefined; | 2515 | fn parsePrimaryTypeExpr(p: *Parser) !Node.Index { |
| 1678 | const label = p.parseBlockLabel(&colon); | 2516 | switch (p.token_tags[p.tok_i]) { |
| 2517 | .char_literal => return p.addNode(.{ | ||
| 2518 | .tag = .char_literal, | ||
| 2519 | .main_token = p.nextToken(), | ||
| 2520 | .data = .{ | ||
| 2521 | .lhs = undefined, | ||
| 2522 | .rhs = undefined, | ||
| 2523 | }, | ||
| 2524 | }), | ||
| 2525 | .integer_literal => return p.addNode(.{ | ||
| 2526 | .tag = .integer_literal, | ||
| 2527 | .main_token = p.nextToken(), | ||
| 2528 | .data = .{ | ||
| 2529 | .lhs = undefined, | ||
| 2530 | .rhs = undefined, | ||
| 2531 | }, | ||
| 2532 | }), | ||
| 2533 | .float_literal => return p.addNode(.{ | ||
| 2534 | .tag = .float_literal, | ||
| 2535 | .main_token = p.nextToken(), | ||
| 2536 | .data = .{ | ||
| 2537 | .lhs = undefined, | ||
| 2538 | .rhs = undefined, | ||
| 2539 | }, | ||
| 2540 | }), | ||
| 2541 | .keyword_false => return p.addNode(.{ | ||
| 2542 | .tag = .false_literal, | ||
| 2543 | .main_token = p.nextToken(), | ||
| 2544 | .data = .{ | ||
| 2545 | .lhs = undefined, | ||
| 2546 | .rhs = undefined, | ||
| 2547 | }, | ||
| 2548 | }), | ||
| 2549 | .keyword_true => return p.addNode(.{ | ||
| 2550 | .tag = .true_literal, | ||
| 2551 | .main_token = p.nextToken(), | ||
| 2552 | .data = .{ | ||
| 2553 | .lhs = undefined, | ||
| 2554 | .rhs = undefined, | ||
| 2555 | }, | ||
| 2556 | }), | ||
| 2557 | .keyword_null => return p.addNode(.{ | ||
| 2558 | .tag = .null_literal, | ||
| 2559 | .main_token = p.nextToken(), | ||
| 2560 | .data = .{ | ||
| 2561 | .lhs = undefined, | ||
| 2562 | .rhs = undefined, | ||
| 2563 | }, | ||
| 2564 | }), | ||
| 2565 | .keyword_undefined => return p.addNode(.{ | ||
| 2566 | .tag = .undefined_literal, | ||
| 2567 | .main_token = p.nextToken(), | ||
| 2568 | .data = .{ | ||
| 2569 | .lhs = undefined, | ||
| 2570 | .rhs = undefined, | ||
| 2571 | }, | ||
| 2572 | }), | ||
| 2573 | .keyword_unreachable => return p.addNode(.{ | ||
| 2574 | .tag = .unreachable_literal, | ||
| 2575 | .main_token = p.nextToken(), | ||
| 2576 | .data = .{ | ||
| 2577 | .lhs = undefined, | ||
| 2578 | .rhs = undefined, | ||
| 2579 | }, | ||
| 2580 | }), | ||
| 2581 | .keyword_anyframe => return p.addNode(.{ | ||
| 2582 | .tag = .anyframe_literal, | ||
| 2583 | .main_token = p.nextToken(), | ||
| 2584 | .data = .{ | ||
| 2585 | .lhs = undefined, | ||
| 2586 | .rhs = undefined, | ||
| 2587 | }, | ||
| 2588 | }), | ||
| 2589 | .string_literal => { | ||
| 2590 | const main_token = p.nextToken(); | ||
| 2591 | return p.addNode(.{ | ||
| 2592 | .tag = .string_literal, | ||
| 2593 | .main_token = main_token, | ||
| 2594 | .data = .{ | ||
| 2595 | .lhs = undefined, | ||
| 2596 | .rhs = undefined, | ||
| 2597 | }, | ||
| 2598 | }); | ||
| 2599 | }, | ||
| 1679 | 2600 | ||
| 1680 | if (label) |label_token| { | 2601 | .builtin => return p.parseBuiltinCall(), |
| 1681 | if (try p.parseBlock(label_token)) |node| return node; | 2602 | .keyword_fn => return p.parseFnProto(), |
| 1682 | } | 2603 | .keyword_if => return p.parseIf(parseTypeExpr), |
| 2604 | .keyword_switch => return p.expectSwitchExpr(), | ||
| 1683 | 2605 | ||
| 1684 | if (try p.parseLoopTypeExpr()) |node| { | 2606 | .keyword_extern, |
| 1685 | switch (node.tag) { | 2607 | .keyword_packed, |
| 1686 | .For => node.cast(Node.For).?.label = label, | 2608 | => { |
| 1687 | .While => node.cast(Node.While).?.label = label, | 2609 | p.tok_i += 1; |
| 1688 | else => unreachable, | 2610 | return p.parseContainerDeclAuto(); |
| 1689 | } | 2611 | }, |
| 1690 | return node; | ||
| 1691 | } | ||
| 1692 | 2612 | ||
| 1693 | if (label) |token| { | 2613 | .keyword_struct, |
| 1694 | p.putBackToken(colon); | 2614 | .keyword_opaque, |
| 1695 | p.putBackToken(token); | 2615 | .keyword_enum, |
| 1696 | } | 2616 | .keyword_union, |
| 1697 | return null; | 2617 | => return p.parseContainerDeclAuto(), |
| 1698 | } | 2618 | |
| 2619 | .keyword_comptime => return p.addNode(.{ | ||
| 2620 | .tag = .@"comptime", | ||
| 2621 | .main_token = p.nextToken(), | ||
| 2622 | .data = .{ | ||
| 2623 | .lhs = try p.expectTypeExpr(), | ||
| 2624 | .rhs = undefined, | ||
| 2625 | }, | ||
| 2626 | }), | ||
| 2627 | .multiline_string_literal_line => { | ||
| 2628 | const first_line = p.nextToken(); | ||
| 2629 | while (p.token_tags[p.tok_i] == .multiline_string_literal_line) { | ||
| 2630 | p.tok_i += 1; | ||
| 2631 | } | ||
| 2632 | return p.addNode(.{ | ||
| 2633 | .tag = .multiline_string_literal, | ||
| 2634 | .main_token = first_line, | ||
| 2635 | .data = .{ | ||
| 2636 | .lhs = first_line, | ||
| 2637 | .rhs = p.tok_i - 1, | ||
| 2638 | }, | ||
| 2639 | }); | ||
| 2640 | }, | ||
| 2641 | .identifier => switch (p.token_tags[p.tok_i + 1]) { | ||
| 2642 | .colon => switch (p.token_tags[p.tok_i + 2]) { | ||
| 2643 | .keyword_inline => { | ||
| 2644 | p.tok_i += 3; | ||
| 2645 | switch (p.token_tags[p.tok_i]) { | ||
| 2646 | .keyword_for => return p.parseForTypeExpr(), | ||
| 2647 | .keyword_while => return p.parseWhileTypeExpr(), | ||
| 2648 | else => return p.fail(.expected_inlinable), | ||
| 2649 | } | ||
| 2650 | }, | ||
| 2651 | .keyword_for => { | ||
| 2652 | p.tok_i += 2; | ||
| 2653 | return p.parseForTypeExpr(); | ||
| 2654 | }, | ||
| 2655 | .keyword_while => { | ||
| 2656 | p.tok_i += 2; | ||
| 2657 | return p.parseWhileTypeExpr(); | ||
| 2658 | }, | ||
| 2659 | .l_brace => { | ||
| 2660 | p.tok_i += 2; | ||
| 2661 | return p.parseBlock(); | ||
| 2662 | }, | ||
| 2663 | else => return p.addNode(.{ | ||
| 2664 | .tag = .identifier, | ||
| 2665 | .main_token = p.nextToken(), | ||
| 2666 | .data = .{ | ||
| 2667 | .lhs = undefined, | ||
| 2668 | .rhs = undefined, | ||
| 2669 | }, | ||
| 2670 | }), | ||
| 2671 | }, | ||
| 2672 | else => return p.addNode(.{ | ||
| 2673 | .tag = .identifier, | ||
| 2674 | .main_token = p.nextToken(), | ||
| 2675 | .data = .{ | ||
| 2676 | .lhs = undefined, | ||
| 2677 | .rhs = undefined, | ||
| 2678 | }, | ||
| 2679 | }), | ||
| 2680 | }, | ||
| 2681 | .keyword_inline => { | ||
| 2682 | p.tok_i += 1; | ||
| 2683 | switch (p.token_tags[p.tok_i]) { | ||
| 2684 | .keyword_for => return p.parseForTypeExpr(), | ||
| 2685 | .keyword_while => return p.parseWhileTypeExpr(), | ||
| 2686 | else => return p.fail(.expected_inlinable), | ||
| 2687 | } | ||
| 2688 | }, | ||
| 2689 | .keyword_for => return p.parseForTypeExpr(), | ||
| 2690 | .keyword_while => return p.parseWhileTypeExpr(), | ||
| 2691 | .period => switch (p.token_tags[p.tok_i + 1]) { | ||
| 2692 | .identifier => return p.addNode(.{ | ||
| 2693 | .tag = .enum_literal, | ||
| 2694 | .data = .{ | ||
| 2695 | .lhs = p.nextToken(), // dot | ||
| 2696 | .rhs = undefined, | ||
| 2697 | }, | ||
| 2698 | .main_token = p.nextToken(), // identifier | ||
| 2699 | }), | ||
| 2700 | .l_brace => { | ||
| 2701 | const lbrace = p.tok_i + 1; | ||
| 2702 | p.tok_i = lbrace + 1; | ||
| 2703 | |||
| 2704 | // If there are 0, 1, or 2 items, we can use ArrayInitDotTwo/StructInitDotTwo; | ||
| 2705 | // otherwise we use the full ArrayInitDot/StructInitDot. | ||
| 2706 | |||
| 2707 | if (p.eatToken(.r_brace)) |_| { | ||
| 2708 | return p.addNode(.{ | ||
| 2709 | .tag = .struct_init_dot_two, | ||
| 2710 | .main_token = lbrace, | ||
| 2711 | .data = .{ | ||
| 2712 | .lhs = 0, | ||
| 2713 | .rhs = 0, | ||
| 2714 | }, | ||
| 2715 | }); | ||
| 2716 | } | ||
| 2717 | const field_init_one = try p.parseFieldInit(); | ||
| 2718 | if (field_init_one != 0) { | ||
| 2719 | const comma_one = p.eatToken(.comma); | ||
| 2720 | if (p.eatToken(.r_brace)) |_| { | ||
| 2721 | return p.addNode(.{ | ||
| 2722 | .tag = if (comma_one != null) .struct_init_dot_two_comma else .struct_init_dot_two, | ||
| 2723 | .main_token = lbrace, | ||
| 2724 | .data = .{ | ||
| 2725 | .lhs = field_init_one, | ||
| 2726 | .rhs = 0, | ||
| 2727 | }, | ||
| 2728 | }); | ||
| 2729 | } | ||
| 2730 | if (comma_one == null) { | ||
| 2731 | try p.warnExpected(.comma); | ||
| 2732 | } | ||
| 2733 | const field_init_two = try p.expectFieldInit(); | ||
| 2734 | const comma_two = p.eatToken(.comma); | ||
| 2735 | if (p.eatToken(.r_brace)) |_| { | ||
| 2736 | return p.addNode(.{ | ||
| 2737 | .tag = if (comma_two != null) .struct_init_dot_two_comma else .struct_init_dot_two, | ||
| 2738 | .main_token = lbrace, | ||
| 2739 | .data = .{ | ||
| 2740 | .lhs = field_init_one, | ||
| 2741 | .rhs = field_init_two, | ||
| 2742 | }, | ||
| 2743 | }); | ||
| 2744 | } | ||
| 2745 | if (comma_two == null) { | ||
| 2746 | try p.warnExpected(.comma); | ||
| 2747 | } | ||
| 2748 | var init_list = std.ArrayList(Node.Index).init(p.gpa); | ||
| 2749 | defer init_list.deinit(); | ||
| 2750 | |||
| 2751 | try init_list.appendSlice(&.{ field_init_one, field_init_two }); | ||
| 2752 | |||
| 2753 | while (true) { | ||
| 2754 | const next = try p.expectFieldInit(); | ||
| 2755 | assert(next != 0); | ||
| 2756 | try init_list.append(next); | ||
| 2757 | switch (p.token_tags[p.nextToken()]) { | ||
| 2758 | .comma => { | ||
| 2759 | if (p.eatToken(.r_brace)) |_| break; | ||
| 2760 | continue; | ||
| 2761 | }, | ||
| 2762 | .r_brace => break, | ||
| 2763 | .colon, .r_paren, .r_bracket => { | ||
| 2764 | p.tok_i -= 1; | ||
| 2765 | return p.failExpected(.r_brace); | ||
| 2766 | }, | ||
| 2767 | else => { | ||
| 2768 | p.tok_i -= 1; | ||
| 2769 | try p.warnExpected(.comma); | ||
| 2770 | }, | ||
| 2771 | } | ||
| 2772 | } | ||
| 2773 | const span = try p.listToSpan(init_list.items); | ||
| 2774 | const trailing_comma = p.token_tags[p.tok_i - 2] == .comma; | ||
| 2775 | return p.addNode(.{ | ||
| 2776 | .tag = if (trailing_comma) .struct_init_dot_comma else .struct_init_dot, | ||
| 2777 | .main_token = lbrace, | ||
| 2778 | .data = .{ | ||
| 2779 | .lhs = span.start, | ||
| 2780 | .rhs = span.end, | ||
| 2781 | }, | ||
| 2782 | }); | ||
| 2783 | } | ||
| 1699 | 2784 | ||
| 1700 | /// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr) | 2785 | const elem_init_one = try p.expectExpr(); |
| 1701 | fn parseLoopTypeExpr(p: *Parser) !?*Node { | 2786 | const comma_one = p.eatToken(.comma); |
| 1702 | const inline_token = p.eatToken(.Keyword_inline); | 2787 | if (p.eatToken(.r_brace)) |_| { |
| 2788 | return p.addNode(.{ | ||
| 2789 | .tag = if (comma_one != null) .array_init_dot_two_comma else .array_init_dot_two, | ||
| 2790 | .main_token = lbrace, | ||
| 2791 | .data = .{ | ||
| 2792 | .lhs = elem_init_one, | ||
| 2793 | .rhs = 0, | ||
| 2794 | }, | ||
| 2795 | }); | ||
| 2796 | } | ||
| 2797 | if (comma_one == null) { | ||
| 2798 | try p.warnExpected(.comma); | ||
| 2799 | } | ||
| 2800 | const elem_init_two = try p.expectExpr(); | ||
| 2801 | const comma_two = p.eatToken(.comma); | ||
| 2802 | if (p.eatToken(.r_brace)) |_| { | ||
| 2803 | return p.addNode(.{ | ||
| 2804 | .tag = if (comma_two != null) .array_init_dot_two_comma else .array_init_dot_two, | ||
| 2805 | .main_token = lbrace, | ||
| 2806 | .data = .{ | ||
| 2807 | .lhs = elem_init_one, | ||
| 2808 | .rhs = elem_init_two, | ||
| 2809 | }, | ||
| 2810 | }); | ||
| 2811 | } | ||
| 2812 | if (comma_two == null) { | ||
| 2813 | try p.warnExpected(.comma); | ||
| 2814 | } | ||
| 2815 | var init_list = std.ArrayList(Node.Index).init(p.gpa); | ||
| 2816 | defer init_list.deinit(); | ||
| 2817 | |||
| 2818 | try init_list.appendSlice(&.{ elem_init_one, elem_init_two }); | ||
| 2819 | |||
| 2820 | while (true) { | ||
| 2821 | const next = try p.expectExpr(); | ||
| 2822 | if (next == 0) break; | ||
| 2823 | try init_list.append(next); | ||
| 2824 | switch (p.token_tags[p.nextToken()]) { | ||
| 2825 | .comma => { | ||
| 2826 | if (p.eatToken(.r_brace)) |_| break; | ||
| 2827 | continue; | ||
| 2828 | }, | ||
| 2829 | .r_brace => break, | ||
| 2830 | .colon, .r_paren, .r_bracket => { | ||
| 2831 | p.tok_i -= 1; | ||
| 2832 | return p.failExpected(.r_brace); | ||
| 2833 | }, | ||
| 2834 | else => { | ||
| 2835 | p.tok_i -= 1; | ||
| 2836 | try p.warnExpected(.comma); | ||
| 2837 | }, | ||
| 2838 | } | ||
| 2839 | } | ||
| 2840 | const span = try p.listToSpan(init_list.items); | ||
| 2841 | return p.addNode(.{ | ||
| 2842 | .tag = if (p.token_tags[p.tok_i - 2] == .comma) .array_init_dot_comma else .array_init_dot, | ||
| 2843 | .main_token = lbrace, | ||
| 2844 | .data = .{ | ||
| 2845 | .lhs = span.start, | ||
| 2846 | .rhs = span.end, | ||
| 2847 | }, | ||
| 2848 | }); | ||
| 2849 | }, | ||
| 2850 | else => return null_node, | ||
| 2851 | }, | ||
| 2852 | .keyword_error => switch (p.token_tags[p.tok_i + 1]) { | ||
| 2853 | .l_brace => { | ||
| 2854 | const error_token = p.tok_i; | ||
| 2855 | p.tok_i += 2; | ||
| 2856 | |||
| 2857 | if (p.eatToken(.r_brace)) |rbrace| { | ||
| 2858 | return p.addNode(.{ | ||
| 2859 | .tag = .error_set_decl, | ||
| 2860 | .main_token = error_token, | ||
| 2861 | .data = .{ | ||
| 2862 | .lhs = undefined, | ||
| 2863 | .rhs = rbrace, | ||
| 2864 | }, | ||
| 2865 | }); | ||
| 2866 | } | ||
| 1703 | 2867 | ||
| 1704 | if (try p.parseForTypeExpr()) |node| { | 2868 | while (true) { |
| 1705 | node.cast(Node.For).?.inline_token = inline_token; | 2869 | const doc_comment = try p.eatDocComments(); |
| 1706 | return node; | 2870 | const identifier = try p.expectToken(.identifier); |
| 2871 | switch (p.token_tags[p.nextToken()]) { | ||
| 2872 | .comma => { | ||
| 2873 | if (p.eatToken(.r_brace)) |_| break; | ||
| 2874 | continue; | ||
| 2875 | }, | ||
| 2876 | .r_brace => break, | ||
| 2877 | .colon, .r_paren, .r_bracket => { | ||
| 2878 | p.tok_i -= 1; | ||
| 2879 | return p.failExpected(.r_brace); | ||
| 2880 | }, | ||
| 2881 | else => { | ||
| 2882 | // This is likely just a missing comma; | ||
| 2883 | // give an error but continue parsing this list. | ||
| 2884 | p.tok_i -= 1; | ||
| 2885 | try p.warnExpected(.comma); | ||
| 2886 | }, | ||
| 2887 | } | ||
| 2888 | } | ||
| 2889 | return p.addNode(.{ | ||
| 2890 | .tag = .error_set_decl, | ||
| 2891 | .main_token = error_token, | ||
| 2892 | .data = .{ | ||
| 2893 | .lhs = undefined, | ||
| 2894 | .rhs = p.tok_i - 1, // rbrace | ||
| 2895 | }, | ||
| 2896 | }); | ||
| 2897 | }, | ||
| 2898 | else => { | ||
| 2899 | const main_token = p.nextToken(); | ||
| 2900 | const period = p.eatToken(.period); | ||
| 2901 | if (period == null) try p.warnExpected(.period); | ||
| 2902 | const identifier = p.eatToken(.identifier); | ||
| 2903 | if (identifier == null) try p.warnExpected(.identifier); | ||
| 2904 | return p.addNode(.{ | ||
| 2905 | .tag = .error_value, | ||
| 2906 | .main_token = main_token, | ||
| 2907 | .data = .{ | ||
| 2908 | .lhs = period orelse 0, | ||
| 2909 | .rhs = identifier orelse 0, | ||
| 2910 | }, | ||
| 2911 | }); | ||
| 2912 | }, | ||
| 2913 | }, | ||
| 2914 | .l_paren => return p.addNode(.{ | ||
| 2915 | .tag = .grouped_expression, | ||
| 2916 | .main_token = p.nextToken(), | ||
| 2917 | .data = .{ | ||
| 2918 | .lhs = try p.expectExpr(), | ||
| 2919 | .rhs = try p.expectToken(.r_paren), | ||
| 2920 | }, | ||
| 2921 | }), | ||
| 2922 | else => return null_node, | ||
| 1707 | } | 2923 | } |
| 2924 | } | ||
| 1708 | 2925 | ||
| 1709 | if (try p.parseWhileTypeExpr()) |node| { | 2926 | fn expectPrimaryTypeExpr(p: *Parser) !Node.Index { |
| 1710 | node.cast(Node.While).?.inline_token = inline_token; | 2927 | const node = try p.parsePrimaryTypeExpr(); |
| 1711 | return node; | 2928 | if (node == 0) { |
| 2929 | return p.fail(.expected_primary_type_expr); | ||
| 1712 | } | 2930 | } |
| 1713 | 2931 | return node; | |
| 1714 | if (inline_token == null) return null; | ||
| 1715 | |||
| 1716 | // If we've seen "inline", there should have been a "for" or "while" | ||
| 1717 | try p.errors.append(p.gpa, .{ | ||
| 1718 | .ExpectedInlinable = .{ .token = p.tok_i }, | ||
| 1719 | }); | ||
| 1720 | return error.ParseError; | ||
| 1721 | } | 2932 | } |
| 1722 | 2933 | ||
| 2934 | /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload | ||
| 1723 | /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)? | 2935 | /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)? |
| 1724 | fn parseForTypeExpr(p: *Parser) !?*Node { | 2936 | fn parseForTypeExpr(p: *Parser) !Node.Index { |
| 1725 | const node = (try p.parseForPrefix()) orelse return null; | 2937 | const for_token = p.eatToken(.keyword_for) orelse return null_node; |
| 1726 | const for_prefix = node.cast(Node.For).?; | 2938 | _ = try p.expectToken(.l_paren); |
| 1727 | 2939 | const array_expr = try p.expectExpr(); | |
| 1728 | const type_expr = try p.expectNode(parseTypeExpr, .{ | 2940 | _ = try p.expectToken(.r_paren); |
| 1729 | .ExpectedTypeExpr = .{ .token = p.tok_i }, | 2941 | const found_payload = try p.parsePtrIndexPayload(); |
| 1730 | }); | 2942 | if (found_payload == 0) try p.warn(.expected_loop_payload); |
| 1731 | for_prefix.body = type_expr; | 2943 | |
| 1732 | 2944 | const then_expr = try p.expectExpr(); | |
| 1733 | if (p.eatToken(.Keyword_else)) |else_token| { | 2945 | const else_token = p.eatToken(.keyword_else) orelse { |
| 1734 | const else_expr = try p.expectNode(parseTypeExpr, .{ | 2946 | return p.addNode(.{ |
| 1735 | .ExpectedTypeExpr = .{ .token = p.tok_i }, | 2947 | .tag = .for_simple, |
| 2948 | .main_token = for_token, | ||
| 2949 | .data = .{ | ||
| 2950 | .lhs = array_expr, | ||
| 2951 | .rhs = then_expr, | ||
| 2952 | }, | ||
| 1736 | }); | 2953 | }); |
| 1737 | 2954 | }; | |
| 1738 | const else_node = try p.arena.allocator.create(Node.Else); | 2955 | const else_expr = try p.expectTypeExpr(); |
| 1739 | else_node.* = .{ | 2956 | return p.addNode(.{ |
| 1740 | .else_token = else_token, | 2957 | .tag = .@"for", |
| 1741 | .payload = null, | 2958 | .main_token = for_token, |
| 1742 | .body = else_expr, | 2959 | .data = .{ |
| 1743 | }; | 2960 | .lhs = array_expr, |
| 1744 | 2961 | .rhs = try p.addExtra(Node.If{ | |
| 1745 | for_prefix.@"else" = else_node; | 2962 | .then_expr = then_expr, |
| 1746 | } | 2963 | .else_expr = else_expr, |
| 1747 | 2964 | }), | |
| 1748 | return node; | 2965 | }, |
| 2966 | }); | ||
| 1749 | } | 2967 | } |
| 1750 | 2968 | ||
| 2969 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? | ||
| 1751 | /// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? | 2970 | /// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? |
| 1752 | fn parseWhileTypeExpr(p: *Parser) !?*Node { | 2971 | fn parseWhileTypeExpr(p: *Parser) !Node.Index { |
| 1753 | const node = (try p.parseWhilePrefix()) orelse return null; | 2972 | const while_token = p.eatToken(.keyword_while) orelse return null_node; |
| 1754 | const while_prefix = node.cast(Node.While).?; | 2973 | _ = try p.expectToken(.l_paren); |
| 1755 | 2974 | const condition = try p.expectExpr(); | |
| 1756 | const type_expr = try p.expectNode(parseTypeExpr, .{ | 2975 | _ = try p.expectToken(.r_paren); |
| 1757 | .ExpectedTypeExpr = .{ .token = p.tok_i }, | 2976 | const then_payload = try p.parsePtrPayload(); |
| 2977 | const cont_expr = try p.parseWhileContinueExpr(); | ||
| 2978 | |||
| 2979 | const then_expr = try p.expectTypeExpr(); | ||
| 2980 | const else_token = p.eatToken(.keyword_else) orelse { | ||
| 2981 | if (cont_expr == 0) { | ||
| 2982 | return p.addNode(.{ | ||
| 2983 | .tag = .while_simple, | ||
| 2984 | .main_token = while_token, | ||
| 2985 | .data = .{ | ||
| 2986 | .lhs = condition, | ||
| 2987 | .rhs = then_expr, | ||
| 2988 | }, | ||
| 2989 | }); | ||
| 2990 | } else { | ||
| 2991 | return p.addNode(.{ | ||
| 2992 | .tag = .while_cont, | ||
| 2993 | .main_token = while_token, | ||
| 2994 | .data = .{ | ||
| 2995 | .lhs = condition, | ||
| 2996 | .rhs = try p.addExtra(Node.WhileCont{ | ||
| 2997 | .cont_expr = cont_expr, | ||
| 2998 | .then_expr = then_expr, | ||
| 2999 | }), | ||
| 3000 | }, | ||
| 3001 | }); | ||
| 3002 | } | ||
| 3003 | }; | ||
| 3004 | const else_payload = try p.parsePayload(); | ||
| 3005 | const else_expr = try p.expectTypeExpr(); | ||
| 3006 | return p.addNode(.{ | ||
| 3007 | .tag = .@"while", | ||
| 3008 | .main_token = while_token, | ||
| 3009 | .data = .{ | ||
| 3010 | .lhs = condition, | ||
| 3011 | .rhs = try p.addExtra(Node.While{ | ||
| 3012 | .cont_expr = cont_expr, | ||
| 3013 | .then_expr = then_expr, | ||
| 3014 | .else_expr = else_expr, | ||
| 3015 | }), | ||
| 3016 | }, | ||
| 1758 | }); | 3017 | }); |
| 1759 | while_prefix.body = type_expr; | ||
| 1760 | |||
| 1761 | if (p.eatToken(.Keyword_else)) |else_token| { | ||
| 1762 | const payload = try p.parsePayload(); | ||
| 1763 | |||
| 1764 | const else_expr = try p.expectNode(parseTypeExpr, .{ | ||
| 1765 | .ExpectedTypeExpr = .{ .token = p.tok_i }, | ||
| 1766 | }); | ||
| 1767 | |||
| 1768 | const else_node = try p.arena.allocator.create(Node.Else); | ||
| 1769 | else_node.* = .{ | ||
| 1770 | .else_token = else_token, | ||
| 1771 | .payload = null, | ||
| 1772 | .body = else_expr, | ||
| 1773 | }; | ||
| 1774 | |||
| 1775 | while_prefix.@"else" = else_node; | ||
| 1776 | } | ||
| 1777 | |||
| 1778 | return node; | ||
| 1779 | } | 3018 | } |
| 1780 | 3019 | ||
| 1781 | /// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE | 3020 | /// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE |
| 1782 | fn parseSwitchExpr(p: *Parser) !?*Node { | 3021 | fn expectSwitchExpr(p: *Parser) !Node.Index { |
| 1783 | const switch_token = p.eatToken(.Keyword_switch) orelse return null; | 3022 | const switch_token = p.assertToken(.keyword_switch); |
| 1784 | _ = try p.expectToken(.LParen); | 3023 | _ = try p.expectToken(.l_paren); |
| 1785 | const expr_node = try p.expectNode(parseExpr, .{ | 3024 | const expr_node = try p.expectExpr(); |
| 1786 | .ExpectedExpr = .{ .token = p.tok_i }, | 3025 | _ = try p.expectToken(.r_paren); |
| 1787 | }); | 3026 | _ = try p.expectToken(.l_brace); |
| 1788 | _ = try p.expectToken(.RParen); | ||
| 1789 | _ = try p.expectToken(.LBrace); | ||
| 1790 | const cases = try p.parseSwitchProngList(); | 3027 | const cases = try p.parseSwitchProngList(); |
| 1791 | defer p.gpa.free(cases); | 3028 | const trailing_comma = p.token_tags[p.tok_i - 1] == .comma; |
| 1792 | const rbrace = try p.expectToken(.RBrace); | 3029 | _ = try p.expectToken(.r_brace); |
| 1793 | 3030 | ||
| 1794 | const node = try Node.Switch.alloc(&p.arena.allocator, cases.len); | 3031 | return p.addNode(.{ |
| 1795 | node.* = .{ | 3032 | .tag = if (trailing_comma) .switch_comma else .@"switch", |
| 1796 | .switch_token = switch_token, | 3033 | .main_token = switch_token, |
| 1797 | .expr = expr_node, | 3034 | .data = .{ |
| 1798 | .cases_len = cases.len, | 3035 | .lhs = expr_node, |
| 1799 | .rbrace = rbrace, | 3036 | .rhs = try p.addExtra(Node.SubRange{ |
| 1800 | }; | 3037 | .start = cases.start, |
| 1801 | std.mem.copy(*Node, node.cases(), cases); | 3038 | .end = cases.end, |
| 1802 | return &node.base; | 3039 | }), |
| 3040 | }, | ||
| 3041 | }); | ||
| 1803 | } | 3042 | } |
| 1804 | 3043 | ||
| 1805 | /// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN | 3044 | /// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN |
| ... | @@ -1807,1696 +3046,1050 @@ const Parser = struct { | ... | @@ -1807,1696 +3046,1050 @@ const Parser = struct { |
| 1807 | /// AsmInput <- COLON AsmInputList AsmClobbers? | 3046 | /// AsmInput <- COLON AsmInputList AsmClobbers? |
| 1808 | /// AsmClobbers <- COLON StringList | 3047 | /// AsmClobbers <- COLON StringList |
| 1809 | /// StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL? | 3048 | /// StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL? |
| 1810 | fn parseAsmExpr(p: *Parser) !?*Node { | 3049 | /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem? |
| 1811 | const asm_token = p.eatToken(.Keyword_asm) orelse return null; | 3050 | /// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem? |
| 1812 | const volatile_token = p.eatToken(.Keyword_volatile); | 3051 | fn expectAsmExpr(p: *Parser) !Node.Index { |
| 1813 | _ = try p.expectToken(.LParen); | 3052 | const asm_token = p.assertToken(.keyword_asm); |
| 1814 | const template = try p.expectNode(parseExpr, .{ | 3053 | _ = p.eatToken(.keyword_volatile); |
| 1815 | .ExpectedExpr = .{ .token = p.tok_i }, | 3054 | _ = try p.expectToken(.l_paren); |
| 1816 | }); | 3055 | const template = try p.expectExpr(); |
| 1817 | 3056 | ||
| 1818 | var arena_outputs: []Node.Asm.Output = &[0]Node.Asm.Output{}; | 3057 | if (p.eatToken(.r_paren)) |rparen| { |
| 1819 | var arena_inputs: []Node.Asm.Input = &[0]Node.Asm.Input{}; | 3058 | return p.addNode(.{ |
| 1820 | var arena_clobbers: []*Node = &[0]*Node{}; | 3059 | .tag = .asm_simple, |
| 3060 | .main_token = asm_token, | ||
| 3061 | .data = .{ | ||
| 3062 | .lhs = template, | ||
| 3063 | .rhs = rparen, | ||
| 3064 | }, | ||
| 3065 | }); | ||
| 3066 | } | ||
| 1821 | 3067 | ||
| 1822 | if (p.eatToken(.Colon) != null) { | 3068 | _ = try p.expectToken(.colon); |
| 1823 | const outputs = try p.parseAsmOutputList(); | ||
| 1824 | defer p.gpa.free(outputs); | ||
| 1825 | arena_outputs = try p.arena.allocator.dupe(Node.Asm.Output, outputs); | ||
| 1826 | 3069 | ||
| 1827 | if (p.eatToken(.Colon) != null) { | 3070 | var list = std.ArrayList(Node.Index).init(p.gpa); |
| 1828 | const inputs = try p.parseAsmInputList(); | 3071 | defer list.deinit(); |
| 1829 | defer p.gpa.free(inputs); | ||
| 1830 | arena_inputs = try p.arena.allocator.dupe(Node.Asm.Input, inputs); | ||
| 1831 | 3072 | ||
| 1832 | if (p.eatToken(.Colon) != null) { | 3073 | while (true) { |
| 1833 | const clobbers = try ListParseFn(*Node, parseStringLiteral)(p); | 3074 | const output_item = try p.parseAsmOutputItem(); |
| 1834 | defer p.gpa.free(clobbers); | 3075 | if (output_item == 0) break; |
| 1835 | arena_clobbers = try p.arena.allocator.dupe(*Node, clobbers); | 3076 | try list.append(output_item); |
| 3077 | switch (p.token_tags[p.tok_i]) { | ||
| 3078 | .comma => p.tok_i += 1, | ||
| 3079 | .colon, .r_paren, .r_brace, .r_bracket => break, // All possible delimiters. | ||
| 3080 | else => { | ||
| 3081 | // This is likely just a missing comma; | ||
| 3082 | // give an error but continue parsing this list. | ||
| 3083 | try p.warnExpected(.comma); | ||
| 3084 | }, | ||
| 3085 | } | ||
| 3086 | } | ||
| 3087 | if (p.eatToken(.colon)) |_| { | ||
| 3088 | while (true) { | ||
| 3089 | const input_item = try p.parseAsmInputItem(); | ||
| 3090 | if (input_item == 0) break; | ||
| 3091 | try list.append(input_item); | ||
| 3092 | switch (p.token_tags[p.tok_i]) { | ||
| 3093 | .comma => p.tok_i += 1, | ||
| 3094 | .colon, .r_paren, .r_brace, .r_bracket => break, // All possible delimiters. | ||
| 3095 | else => { | ||
| 3096 | // This is likely just a missing comma; | ||
| 3097 | // give an error but continue parsing this list. | ||
| 3098 | try p.warnExpected(.comma); | ||
| 3099 | }, | ||
| 3100 | } | ||
| 3101 | } | ||
| 3102 | if (p.eatToken(.colon)) |_| { | ||
| 3103 | while (p.eatToken(.string_literal)) |_| { | ||
| 3104 | switch (p.token_tags[p.tok_i]) { | ||
| 3105 | .comma => p.tok_i += 1, | ||
| 3106 | .colon, .r_paren, .r_brace, .r_bracket => break, | ||
| 3107 | else => { | ||
| 3108 | // This is likely just a missing comma; | ||
| 3109 | // give an error but continue parsing this list. | ||
| 3110 | try p.warnExpected(.comma); | ||
| 3111 | }, | ||
| 3112 | } | ||
| 1836 | } | 3113 | } |
| 1837 | } | 3114 | } |
| 1838 | } | 3115 | } |
| 1839 | 3116 | const rparen = try p.expectToken(.r_paren); | |
| 1840 | const node = try p.arena.allocator.create(Node.Asm); | 3117 | const span = try p.listToSpan(list.items); |
| 1841 | node.* = .{ | 3118 | return p.addNode(.{ |
| 1842 | .asm_token = asm_token, | 3119 | .tag = .@"asm", |
| 1843 | .volatile_token = volatile_token, | 3120 | .main_token = asm_token, |
| 1844 | .template = template, | 3121 | .data = .{ |
| 1845 | .outputs = arena_outputs, | 3122 | .lhs = template, |
| 1846 | .inputs = arena_inputs, | 3123 | .rhs = try p.addExtra(Node.Asm{ |
| 1847 | .clobbers = arena_clobbers, | 3124 | .items_start = span.start, |
| 1848 | .rparen = try p.expectToken(.RParen), | 3125 | .items_end = span.end, |
| 1849 | }; | 3126 | .rparen = rparen, |
| 1850 | 3127 | }), | |
| 1851 | return &node.base; | 3128 | }, |
| 1852 | } | 3129 | }); |
| 1853 | |||
| 1854 | /// DOT IDENTIFIER | ||
| 1855 | fn parseAnonLiteral(p: *Parser) !?*Node { | ||
| 1856 | const dot = p.eatToken(.Period) orelse return null; | ||
| 1857 | |||
| 1858 | // anon enum literal | ||
| 1859 | if (p.eatToken(.Identifier)) |name| { | ||
| 1860 | const node = try p.arena.allocator.create(Node.EnumLiteral); | ||
| 1861 | node.* = .{ | ||
| 1862 | .dot = dot, | ||
| 1863 | .name = name, | ||
| 1864 | }; | ||
| 1865 | return &node.base; | ||
| 1866 | } | ||
| 1867 | |||
| 1868 | if (try p.parseAnonInitList(dot)) |node| { | ||
| 1869 | return node; | ||
| 1870 | } | ||
| 1871 | |||
| 1872 | p.putBackToken(dot); | ||
| 1873 | return null; | ||
| 1874 | } | 3130 | } |
| 1875 | 3131 | ||
| 1876 | /// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN | 3132 | /// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN |
| 1877 | fn parseAsmOutputItem(p: *Parser) !?Node.Asm.Output { | 3133 | fn parseAsmOutputItem(p: *Parser) !Node.Index { |
| 1878 | const lbracket = p.eatToken(.LBracket) orelse return null; | 3134 | _ = p.eatToken(.l_bracket) orelse return null_node; |
| 1879 | const name = try p.expectNode(parseIdentifier, .{ | 3135 | const identifier = try p.expectToken(.identifier); |
| 1880 | .ExpectedIdentifier = .{ .token = p.tok_i }, | 3136 | _ = try p.expectToken(.r_bracket); |
| 1881 | }); | 3137 | _ = try p.expectToken(.string_literal); |
| 1882 | _ = try p.expectToken(.RBracket); | 3138 | _ = try p.expectToken(.l_paren); |
| 1883 | 3139 | const type_expr: Node.Index = blk: { | |
| 1884 | const constraint = try p.expectNode(parseStringLiteral, .{ | 3140 | if (p.eatToken(.arrow)) |_| { |
| 1885 | .ExpectedStringLiteral = .{ .token = p.tok_i }, | 3141 | break :blk try p.expectTypeExpr(); |
| 1886 | }); | 3142 | } else { |
| 1887 | 3143 | _ = try p.expectToken(.identifier); | |
| 1888 | _ = try p.expectToken(.LParen); | 3144 | break :blk null_node; |
| 1889 | const kind: Node.Asm.Output.Kind = blk: { | ||
| 1890 | if (p.eatToken(.Arrow) != null) { | ||
| 1891 | const return_ident = try p.expectNode(parseTypeExpr, .{ | ||
| 1892 | .ExpectedTypeExpr = .{ .token = p.tok_i }, | ||
| 1893 | }); | ||
| 1894 | break :blk .{ .Return = return_ident }; | ||
| 1895 | } | 3145 | } |
| 1896 | const variable = try p.expectNode(parseIdentifier, .{ | ||
| 1897 | .ExpectedIdentifier = .{ .token = p.tok_i }, | ||
| 1898 | }); | ||
| 1899 | break :blk .{ .Variable = variable.castTag(.Identifier).? }; | ||
| 1900 | }; | ||
| 1901 | const rparen = try p.expectToken(.RParen); | ||
| 1902 | |||
| 1903 | return Node.Asm.Output{ | ||
| 1904 | .lbracket = lbracket, | ||
| 1905 | .symbolic_name = name, | ||
| 1906 | .constraint = constraint, | ||
| 1907 | .kind = kind, | ||
| 1908 | .rparen = rparen, | ||
| 1909 | }; | 3146 | }; |
| 3147 | const rparen = try p.expectToken(.r_paren); | ||
| 3148 | return p.addNode(.{ | ||
| 3149 | .tag = .asm_output, | ||
| 3150 | .main_token = identifier, | ||
| 3151 | .data = .{ | ||
| 3152 | .lhs = type_expr, | ||
| 3153 | .rhs = rparen, | ||
| 3154 | }, | ||
| 3155 | }); | ||
| 1910 | } | 3156 | } |
| 1911 | 3157 | ||
| 1912 | /// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN | 3158 | /// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN |
| 1913 | fn parseAsmInputItem(p: *Parser) !?Node.Asm.Input { | 3159 | fn parseAsmInputItem(p: *Parser) !Node.Index { |
| 1914 | const lbracket = p.eatToken(.LBracket) orelse return null; | 3160 | _ = p.eatToken(.l_bracket) orelse return null_node; |
| 1915 | const name = try p.expectNode(parseIdentifier, .{ | 3161 | const identifier = try p.expectToken(.identifier); |
| 1916 | .ExpectedIdentifier = .{ .token = p.tok_i }, | 3162 | _ = try p.expectToken(.r_bracket); |
| 1917 | }); | 3163 | _ = try p.expectToken(.string_literal); |
| 1918 | _ = try p.expectToken(.RBracket); | 3164 | _ = try p.expectToken(.l_paren); |
| 1919 | 3165 | const expr = try p.expectExpr(); | |
| 1920 | const constraint = try p.expectNode(parseStringLiteral, .{ | 3166 | const rparen = try p.expectToken(.r_paren); |
| 1921 | .ExpectedStringLiteral = .{ .token = p.tok_i }, | 3167 | return p.addNode(.{ |
| 1922 | }); | 3168 | .tag = .asm_input, |
| 1923 | 3169 | .main_token = identifier, | |
| 1924 | _ = try p.expectToken(.LParen); | 3170 | .data = .{ |
| 1925 | const expr = try p.expectNode(parseExpr, .{ | 3171 | .lhs = expr, |
| 1926 | .ExpectedExpr = .{ .token = p.tok_i }, | 3172 | .rhs = rparen, |
| 3173 | }, | ||
| 1927 | }); | 3174 | }); |
| 1928 | const rparen = try p.expectToken(.RParen); | ||
| 1929 | |||
| 1930 | return Node.Asm.Input{ | ||
| 1931 | .lbracket = lbracket, | ||
| 1932 | .symbolic_name = name, | ||
| 1933 | .constraint = constraint, | ||
| 1934 | .expr = expr, | ||
| 1935 | .rparen = rparen, | ||
| 1936 | }; | ||
| 1937 | } | 3175 | } |
| 1938 | 3176 | ||
| 1939 | /// BreakLabel <- COLON IDENTIFIER | 3177 | /// BreakLabel <- COLON IDENTIFIER |
| 1940 | fn parseBreakLabel(p: *Parser) !?TokenIndex { | 3178 | fn parseBreakLabel(p: *Parser) !TokenIndex { |
| 1941 | _ = p.eatToken(.Colon) orelse return null; | 3179 | _ = p.eatToken(.colon) orelse return @as(TokenIndex, 0); |
| 1942 | const ident = try p.expectToken(.Identifier); | 3180 | return p.expectToken(.identifier); |
| 1943 | return ident; | ||
| 1944 | } | 3181 | } |
| 1945 | 3182 | ||
| 1946 | /// BlockLabel <- IDENTIFIER COLON | 3183 | /// BlockLabel <- IDENTIFIER COLON |
| 1947 | fn parseBlockLabel(p: *Parser, colon_token: *TokenIndex) ?TokenIndex { | 3184 | fn parseBlockLabel(p: *Parser) TokenIndex { |
| 1948 | const identifier = p.eatToken(.Identifier) orelse return null; | 3185 | if (p.token_tags[p.tok_i] == .identifier and |
| 1949 | if (p.eatToken(.Colon)) |colon| { | 3186 | p.token_tags[p.tok_i + 1] == .colon) |
| 1950 | colon_token.* = colon; | 3187 | { |
| 3188 | const identifier = p.tok_i; | ||
| 3189 | p.tok_i += 2; | ||
| 1951 | return identifier; | 3190 | return identifier; |
| 1952 | } | 3191 | } |
| 1953 | p.putBackToken(identifier); | 3192 | return 0; |
| 1954 | return null; | ||
| 1955 | } | 3193 | } |
| 1956 | 3194 | ||
| 1957 | /// FieldInit <- DOT IDENTIFIER EQUAL Expr | 3195 | /// FieldInit <- DOT IDENTIFIER EQUAL Expr |
| 1958 | fn parseFieldInit(p: *Parser) !?*Node { | 3196 | fn parseFieldInit(p: *Parser) !Node.Index { |
| 1959 | const period_token = p.eatToken(.Period) orelse return null; | 3197 | if (p.token_tags[p.tok_i + 0] == .period and |
| 1960 | const name_token = p.eatToken(.Identifier) orelse { | 3198 | p.token_tags[p.tok_i + 1] == .identifier and |
| 1961 | // Because of anon literals `.{` is also valid. | 3199 | p.token_tags[p.tok_i + 2] == .equal) |
| 1962 | p.putBackToken(period_token); | 3200 | { |
| 1963 | return null; | 3201 | p.tok_i += 3; |
| 1964 | }; | 3202 | return p.expectExpr(); |
| 1965 | const eq_token = p.eatToken(.Equal) orelse { | 3203 | } else { |
| 1966 | // `.Name` may also be an enum literal, which is a later rule. | 3204 | return null_node; |
| 1967 | p.putBackToken(name_token); | 3205 | } |
| 1968 | p.putBackToken(period_token); | 3206 | } |
| 1969 | return null; | ||
| 1970 | }; | ||
| 1971 | const expr_node = try p.expectNode(parseExpr, .{ | ||
| 1972 | .ExpectedExpr = .{ .token = p.tok_i }, | ||
| 1973 | }); | ||
| 1974 | 3207 | ||
| 1975 | const node = try p.arena.allocator.create(Node.FieldInitializer); | 3208 | fn expectFieldInit(p: *Parser) !Node.Index { |
| 1976 | node.* = .{ | 3209 | _ = try p.expectToken(.period); |
| 1977 | .period_token = period_token, | 3210 | _ = try p.expectToken(.identifier); |
| 1978 | .name_token = name_token, | 3211 | _ = try p.expectToken(.equal); |
| 1979 | .expr = expr_node, | 3212 | return p.expectExpr(); |
| 1980 | }; | ||
| 1981 | return &node.base; | ||
| 1982 | } | 3213 | } |
| 1983 | 3214 | ||
| 1984 | /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN | 3215 | /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN |
| 1985 | fn parseWhileContinueExpr(p: *Parser) !?*Node { | 3216 | fn parseWhileContinueExpr(p: *Parser) !Node.Index { |
| 1986 | _ = p.eatToken(.Colon) orelse return null; | 3217 | _ = p.eatToken(.colon) orelse return null_node; |
| 1987 | _ = try p.expectToken(.LParen); | 3218 | _ = try p.expectToken(.l_paren); |
| 1988 | const node = try p.expectNode(parseAssignExpr, .{ | 3219 | const node = try p.parseAssignExpr(); |
| 1989 | .ExpectedExprOrAssignment = .{ .token = p.tok_i }, | 3220 | if (node == 0) return p.fail(.expected_expr_or_assignment); |
| 1990 | }); | 3221 | _ = try p.expectToken(.r_paren); |
| 1991 | _ = try p.expectToken(.RParen); | ||
| 1992 | return node; | 3222 | return node; |
| 1993 | } | 3223 | } |
| 1994 | 3224 | ||
| 1995 | /// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN | 3225 | /// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN |
| 1996 | fn parseLinkSection(p: *Parser) !?*Node { | 3226 | fn parseLinkSection(p: *Parser) !Node.Index { |
| 1997 | _ = p.eatToken(.Keyword_linksection) orelse return null; | 3227 | _ = p.eatToken(.keyword_linksection) orelse return null_node; |
| 1998 | _ = try p.expectToken(.LParen); | 3228 | _ = try p.expectToken(.l_paren); |
| 1999 | const expr_node = try p.expectNode(parseExpr, .{ | 3229 | const expr_node = try p.expectExpr(); |
| 2000 | .ExpectedExpr = .{ .token = p.tok_i }, | 3230 | _ = try p.expectToken(.r_paren); |
| 2001 | }); | ||
| 2002 | _ = try p.expectToken(.RParen); | ||
| 2003 | return expr_node; | 3231 | return expr_node; |
| 2004 | } | 3232 | } |
| 2005 | 3233 | ||
| 2006 | /// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN | 3234 | /// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN |
| 2007 | fn parseCallconv(p: *Parser) !?*Node { | 3235 | fn parseCallconv(p: *Parser) !Node.Index { |
| 2008 | _ = p.eatToken(.Keyword_callconv) orelse return null; | 3236 | _ = p.eatToken(.keyword_callconv) orelse return null_node; |
| 2009 | _ = try p.expectToken(.LParen); | 3237 | _ = try p.expectToken(.l_paren); |
| 2010 | const expr_node = try p.expectNode(parseExpr, .{ | 3238 | const expr_node = try p.expectExpr(); |
| 2011 | .ExpectedExpr = .{ .token = p.tok_i }, | 3239 | _ = try p.expectToken(.r_paren); |
| 2012 | }); | ||
| 2013 | _ = try p.expectToken(.RParen); | ||
| 2014 | return expr_node; | 3240 | return expr_node; |
| 2015 | } | 3241 | } |
| 2016 | 3242 | ||
| 2017 | /// ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType | 3243 | /// ParamDecl |
| 2018 | fn parseParamDecl(p: *Parser) !?Node.FnProto.ParamDecl { | 3244 | /// <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType |
| 2019 | const doc_comments = try p.parseDocComment(); | 3245 | /// / DOT3 |
| 2020 | const noalias_token = p.eatToken(.Keyword_noalias); | ||
| 2021 | const comptime_token = if (noalias_token == null) p.eatToken(.Keyword_comptime) else null; | ||
| 2022 | const name_token = blk: { | ||
| 2023 | const identifier = p.eatToken(.Identifier) orelse break :blk null; | ||
| 2024 | if (p.eatToken(.Colon) != null) break :blk identifier; | ||
| 2025 | p.putBackToken(identifier); // ParamType may also be an identifier | ||
| 2026 | break :blk null; | ||
| 2027 | }; | ||
| 2028 | const param_type = (try p.parseParamType()) orelse { | ||
| 2029 | // Only return cleanly if no keyword, identifier, or doc comment was found | ||
| 2030 | if (noalias_token == null and | ||
| 2031 | comptime_token == null and | ||
| 2032 | name_token == null and | ||
| 2033 | doc_comments == null) | ||
| 2034 | { | ||
| 2035 | return null; | ||
| 2036 | } | ||
| 2037 | try p.errors.append(p.gpa, .{ | ||
| 2038 | .ExpectedParamType = .{ .token = p.tok_i }, | ||
| 2039 | }); | ||
| 2040 | return error.ParseError; | ||
| 2041 | }; | ||
| 2042 | |||
| 2043 | return Node.FnProto.ParamDecl{ | ||
| 2044 | .doc_comments = doc_comments, | ||
| 2045 | .comptime_token = comptime_token, | ||
| 2046 | .noalias_token = noalias_token, | ||
| 2047 | .name_token = name_token, | ||
| 2048 | .param_type = param_type, | ||
| 2049 | }; | ||
| 2050 | } | ||
| 2051 | |||
| 2052 | /// ParamType | 3246 | /// ParamType |
| 2053 | /// <- Keyword_anytype | 3247 | /// <- Keyword_anytype |
| 2054 | /// / DOT3 | ||
| 2055 | /// / TypeExpr | 3248 | /// / TypeExpr |
| 2056 | fn parseParamType(p: *Parser) !?Node.FnProto.ParamDecl.ParamType { | 3249 | /// This function can return null nodes and then still return nodes afterwards, |
| 2057 | // TODO cast from tuple to error union is broken | 3250 | /// such as in the case of anytype and `...`. Caller must look for rparen to find |
| 2058 | const P = Node.FnProto.ParamDecl.ParamType; | 3251 | /// out when there are no more param decls left. |
| 2059 | if (try p.parseAnyType()) |node| return P{ .any_type = node }; | 3252 | fn expectParamDecl(p: *Parser) !Node.Index { |
| 2060 | if (try p.parseTypeExpr()) |node| return P{ .type_expr = node }; | 3253 | _ = try p.eatDocComments(); |
| 2061 | return null; | 3254 | switch (p.token_tags[p.tok_i]) { |
| 2062 | } | 3255 | .keyword_noalias, .keyword_comptime => p.tok_i += 1, |
| 2063 | 3256 | .ellipsis3 => { | |
| 2064 | /// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload? | 3257 | p.tok_i += 1; |
| 2065 | fn parseIfPrefix(p: *Parser) !?*Node { | 3258 | return null_node; |
| 2066 | const if_token = p.eatToken(.Keyword_if) orelse return null; | 3259 | }, |
| 2067 | _ = try p.expectToken(.LParen); | 3260 | else => {}, |
| 2068 | const condition = try p.expectNode(parseExpr, .{ | 3261 | } |
| 2069 | .ExpectedExpr = .{ .token = p.tok_i }, | 3262 | if (p.token_tags[p.tok_i] == .identifier and |
| 2070 | }); | 3263 | p.token_tags[p.tok_i + 1] == .colon) |
| 2071 | _ = try p.expectToken(.RParen); | 3264 | { |
| 2072 | const payload = try p.parsePtrPayload(); | 3265 | p.tok_i += 2; |
| 2073 | 3266 | } | |
| 2074 | const node = try p.arena.allocator.create(Node.If); | 3267 | switch (p.token_tags[p.tok_i]) { |
| 2075 | node.* = .{ | 3268 | .keyword_anytype => { |
| 2076 | .if_token = if_token, | 3269 | p.tok_i += 1; |
| 2077 | .condition = condition, | 3270 | return null_node; |
| 2078 | .payload = payload, | 3271 | }, |
| 2079 | .body = undefined, // set by caller | 3272 | else => return p.expectTypeExpr(), |
| 2080 | .@"else" = null, | 3273 | } |
| 2081 | }; | ||
| 2082 | return &node.base; | ||
| 2083 | } | ||
| 2084 | |||
| 2085 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? | ||
| 2086 | fn parseWhilePrefix(p: *Parser) !?*Node { | ||
| 2087 | const while_token = p.eatToken(.Keyword_while) orelse return null; | ||
| 2088 | |||
| 2089 | _ = try p.expectToken(.LParen); | ||
| 2090 | const condition = try p.expectNode(parseExpr, .{ | ||
| 2091 | .ExpectedExpr = .{ .token = p.tok_i }, | ||
| 2092 | }); | ||
| 2093 | _ = try p.expectToken(.RParen); | ||
| 2094 | |||
| 2095 | const payload = try p.parsePtrPayload(); | ||
| 2096 | const continue_expr = try p.parseWhileContinueExpr(); | ||
| 2097 | |||
| 2098 | const node = try p.arena.allocator.create(Node.While); | ||
| 2099 | node.* = .{ | ||
| 2100 | .label = null, | ||
| 2101 | .inline_token = null, | ||
| 2102 | .while_token = while_token, | ||
| 2103 | .condition = condition, | ||
| 2104 | .payload = payload, | ||
| 2105 | .continue_expr = continue_expr, | ||
| 2106 | .body = undefined, // set by caller | ||
| 2107 | .@"else" = null, | ||
| 2108 | }; | ||
| 2109 | return &node.base; | ||
| 2110 | } | ||
| 2111 | |||
| 2112 | /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload | ||
| 2113 | fn parseForPrefix(p: *Parser) !?*Node { | ||
| 2114 | const for_token = p.eatToken(.Keyword_for) orelse return null; | ||
| 2115 | |||
| 2116 | _ = try p.expectToken(.LParen); | ||
| 2117 | const array_expr = try p.expectNode(parseExpr, .{ | ||
| 2118 | .ExpectedExpr = .{ .token = p.tok_i }, | ||
| 2119 | }); | ||
| 2120 | _ = try p.expectToken(.RParen); | ||
| 2121 | |||
| 2122 | const payload = try p.expectNode(parsePtrIndexPayload, .{ | ||
| 2123 | .ExpectedPayload = .{ .token = p.tok_i }, | ||
| 2124 | }); | ||
| 2125 | |||
| 2126 | const node = try p.arena.allocator.create(Node.For); | ||
| 2127 | node.* = .{ | ||
| 2128 | .label = null, | ||
| 2129 | .inline_token = null, | ||
| 2130 | .for_token = for_token, | ||
| 2131 | .array_expr = array_expr, | ||
| 2132 | .payload = payload, | ||
| 2133 | .body = undefined, // set by caller | ||
| 2134 | .@"else" = null, | ||
| 2135 | }; | ||
| 2136 | return &node.base; | ||
| 2137 | } | 3274 | } |
| 2138 | 3275 | ||
| 2139 | /// Payload <- PIPE IDENTIFIER PIPE | 3276 | /// Payload <- PIPE IDENTIFIER PIPE |
| 2140 | fn parsePayload(p: *Parser) !?*Node { | 3277 | fn parsePayload(p: *Parser) !TokenIndex { |
| 2141 | const lpipe = p.eatToken(.Pipe) orelse return null; | 3278 | _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0); |
| 2142 | const identifier = try p.expectNode(parseIdentifier, .{ | 3279 | const identifier = try p.expectToken(.identifier); |
| 2143 | .ExpectedIdentifier = .{ .token = p.tok_i }, | 3280 | _ = try p.expectToken(.pipe); |
| 2144 | }); | 3281 | return identifier; |
| 2145 | const rpipe = try p.expectToken(.Pipe); | ||
| 2146 | |||
| 2147 | const node = try p.arena.allocator.create(Node.Payload); | ||
| 2148 | node.* = .{ | ||
| 2149 | .lpipe = lpipe, | ||
| 2150 | .error_symbol = identifier, | ||
| 2151 | .rpipe = rpipe, | ||
| 2152 | }; | ||
| 2153 | return &node.base; | ||
| 2154 | } | 3282 | } |
| 2155 | 3283 | ||
| 2156 | /// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE | 3284 | /// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE |
| 2157 | fn parsePtrPayload(p: *Parser) !?*Node { | 3285 | fn parsePtrPayload(p: *Parser) !TokenIndex { |
| 2158 | const lpipe = p.eatToken(.Pipe) orelse return null; | 3286 | _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0); |
| 2159 | const asterisk = p.eatToken(.Asterisk); | 3287 | _ = p.eatToken(.asterisk); |
| 2160 | const identifier = try p.expectNode(parseIdentifier, .{ | 3288 | const identifier = try p.expectToken(.identifier); |
| 2161 | .ExpectedIdentifier = .{ .token = p.tok_i }, | 3289 | _ = try p.expectToken(.pipe); |
| 2162 | }); | 3290 | return identifier; |
| 2163 | const rpipe = try p.expectToken(.Pipe); | ||
| 2164 | |||
| 2165 | const node = try p.arena.allocator.create(Node.PointerPayload); | ||
| 2166 | node.* = .{ | ||
| 2167 | .lpipe = lpipe, | ||
| 2168 | .ptr_token = asterisk, | ||
| 2169 | .value_symbol = identifier, | ||
| 2170 | .rpipe = rpipe, | ||
| 2171 | }; | ||
| 2172 | return &node.base; | ||
| 2173 | } | 3291 | } |
| 2174 | 3292 | ||
| 2175 | /// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE | 3293 | /// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE |
| 2176 | fn parsePtrIndexPayload(p: *Parser) !?*Node { | 3294 | /// Returns the first identifier token, if any. |
| 2177 | const lpipe = p.eatToken(.Pipe) orelse return null; | 3295 | fn parsePtrIndexPayload(p: *Parser) !TokenIndex { |
| 2178 | const asterisk = p.eatToken(.Asterisk); | 3296 | _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0); |
| 2179 | const identifier = try p.expectNode(parseIdentifier, .{ | 3297 | _ = p.eatToken(.asterisk); |
| 2180 | .ExpectedIdentifier = .{ .token = p.tok_i }, | 3298 | const identifier = try p.expectToken(.identifier); |
| 2181 | }); | 3299 | if (p.eatToken(.comma) != null) { |
| 2182 | 3300 | _ = try p.expectToken(.identifier); | |
| 2183 | const index = if (p.eatToken(.Comma) == null) | 3301 | } |
| 2184 | null | 3302 | _ = try p.expectToken(.pipe); |
| 2185 | else | 3303 | return identifier; |
| 2186 | try p.expectNode(parseIdentifier, .{ | ||
| 2187 | .ExpectedIdentifier = .{ .token = p.tok_i }, | ||
| 2188 | }); | ||
| 2189 | |||
| 2190 | const rpipe = try p.expectToken(.Pipe); | ||
| 2191 | |||
| 2192 | const node = try p.arena.allocator.create(Node.PointerIndexPayload); | ||
| 2193 | node.* = .{ | ||
| 2194 | .lpipe = lpipe, | ||
| 2195 | .ptr_token = asterisk, | ||
| 2196 | .value_symbol = identifier, | ||
| 2197 | .index_symbol = index, | ||
| 2198 | .rpipe = rpipe, | ||
| 2199 | }; | ||
| 2200 | return &node.base; | ||
| 2201 | } | 3304 | } |
| 2202 | 3305 | ||
| 2203 | /// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr | 3306 | /// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr |
| 2204 | fn parseSwitchProng(p: *Parser) !?*Node { | ||
| 2205 | const node = (try p.parseSwitchCase()) orelse return null; | ||
| 2206 | const arrow = try p.expectToken(.EqualAngleBracketRight); | ||
| 2207 | const payload = try p.parsePtrPayload(); | ||
| 2208 | const expr = try p.expectNode(parseAssignExpr, .{ | ||
| 2209 | .ExpectedExprOrAssignment = .{ .token = p.tok_i }, | ||
| 2210 | }); | ||
| 2211 | |||
| 2212 | const switch_case = node.cast(Node.SwitchCase).?; | ||
| 2213 | switch_case.arrow_token = arrow; | ||
| 2214 | switch_case.payload = payload; | ||
| 2215 | switch_case.expr = expr; | ||
| 2216 | |||
| 2217 | return node; | ||
| 2218 | } | ||
| 2219 | |||
| 2220 | /// SwitchCase | 3307 | /// SwitchCase |
| 2221 | /// <- SwitchItem (COMMA SwitchItem)* COMMA? | 3308 | /// <- SwitchItem (COMMA SwitchItem)* COMMA? |
| 2222 | /// / KEYWORD_else | 3309 | /// / KEYWORD_else |
| 2223 | fn parseSwitchCase(p: *Parser) !?*Node { | 3310 | fn parseSwitchProng(p: *Parser) !Node.Index { |
| 2224 | var list = std.ArrayList(*Node).init(p.gpa); | 3311 | if (p.eatToken(.keyword_else)) |_| { |
| 2225 | defer list.deinit(); | 3312 | const arrow_token = try p.expectToken(.equal_angle_bracket_right); |
| 2226 | 3313 | _ = try p.parsePtrPayload(); | |
| 2227 | if (try p.parseSwitchItem()) |first_item| { | 3314 | return p.addNode(.{ |
| 2228 | try list.append(first_item); | 3315 | .tag = .switch_case_one, |
| 2229 | while (p.eatToken(.Comma) != null) { | 3316 | .main_token = arrow_token, |
| 2230 | const next_item = (try p.parseSwitchItem()) orelse break; | 3317 | .data = .{ |
| 2231 | try list.append(next_item); | 3318 | .lhs = 0, |
| 2232 | } | 3319 | .rhs = try p.expectAssignExpr(), |
| 2233 | } else if (p.eatToken(.Keyword_else)) |else_token| { | 3320 | }, |
| 2234 | const else_node = try p.arena.allocator.create(Node.SwitchElse); | ||
| 2235 | else_node.* = .{ | ||
| 2236 | .token = else_token, | ||
| 2237 | }; | ||
| 2238 | try list.append(&else_node.base); | ||
| 2239 | } else return null; | ||
| 2240 | |||
| 2241 | const node = try Node.SwitchCase.alloc(&p.arena.allocator, list.items.len); | ||
| 2242 | node.* = .{ | ||
| 2243 | .items_len = list.items.len, | ||
| 2244 | .arrow_token = undefined, // set by caller | ||
| 2245 | .payload = null, | ||
| 2246 | .expr = undefined, // set by caller | ||
| 2247 | }; | ||
| 2248 | std.mem.copy(*Node, node.items(), list.items); | ||
| 2249 | return &node.base; | ||
| 2250 | } | ||
| 2251 | |||
| 2252 | /// SwitchItem <- Expr (DOT3 Expr)? | ||
| 2253 | fn parseSwitchItem(p: *Parser) !?*Node { | ||
| 2254 | const expr = (try p.parseExpr()) orelse return null; | ||
| 2255 | if (p.eatToken(.Ellipsis3)) |token| { | ||
| 2256 | const range_end = try p.expectNode(parseExpr, .{ | ||
| 2257 | .ExpectedExpr = .{ .token = p.tok_i }, | ||
| 2258 | }); | 3321 | }); |
| 2259 | |||
| 2260 | const node = try p.arena.allocator.create(Node.SimpleInfixOp); | ||
| 2261 | node.* = .{ | ||
| 2262 | .base = Node{ .tag = .Range }, | ||
| 2263 | .op_token = token, | ||
| 2264 | .lhs = expr, | ||
| 2265 | .rhs = range_end, | ||
| 2266 | }; | ||
| 2267 | return &node.base; | ||
| 2268 | } | 3322 | } |
| 2269 | return expr; | 3323 | const first_item = try p.parseSwitchItem(); |
| 2270 | } | 3324 | if (first_item == 0) return null_node; |
| 2271 | 3325 | ||
| 2272 | /// AssignOp | 3326 | if (p.eatToken(.equal_angle_bracket_right)) |arrow_token| { |
| 2273 | /// <- ASTERISKEQUAL | 3327 | _ = try p.parsePtrPayload(); |
| 2274 | /// / SLASHEQUAL | 3328 | return p.addNode(.{ |
| 2275 | /// / PERCENTEQUAL | 3329 | .tag = .switch_case_one, |
| 2276 | /// / PLUSEQUAL | 3330 | .main_token = arrow_token, |
| 2277 | /// / MINUSEQUAL | 3331 | .data = .{ |
| 2278 | /// / LARROW2EQUAL | 3332 | .lhs = first_item, |
| 2279 | /// / RARROW2EQUAL | 3333 | .rhs = try p.expectAssignExpr(), |
| 2280 | /// / AMPERSANDEQUAL | ||
| 2281 | /// / CARETEQUAL | ||
| 2282 | /// / PIPEEQUAL | ||
| 2283 | /// / ASTERISKPERCENTEQUAL | ||
| 2284 | /// / PLUSPERCENTEQUAL | ||
| 2285 | /// / MINUSPERCENTEQUAL | ||
| 2286 | /// / EQUAL | ||
| 2287 | fn parseAssignOp(p: *Parser) !?*Node { | ||
| 2288 | const token = p.nextToken(); | ||
| 2289 | const op: Node.Tag = switch (p.token_ids[token]) { | ||
| 2290 | .AsteriskEqual => .AssignMul, | ||
| 2291 | .SlashEqual => .AssignDiv, | ||
| 2292 | .PercentEqual => .AssignMod, | ||
| 2293 | .PlusEqual => .AssignAdd, | ||
| 2294 | .MinusEqual => .AssignSub, | ||
| 2295 | .AngleBracketAngleBracketLeftEqual => .AssignBitShiftLeft, | ||
| 2296 | .AngleBracketAngleBracketRightEqual => .AssignBitShiftRight, | ||
| 2297 | .AmpersandEqual => .AssignBitAnd, | ||
| 2298 | .CaretEqual => .AssignBitXor, | ||
| 2299 | .PipeEqual => .AssignBitOr, | ||
| 2300 | .AsteriskPercentEqual => .AssignMulWrap, | ||
| 2301 | .PlusPercentEqual => .AssignAddWrap, | ||
| 2302 | .MinusPercentEqual => .AssignSubWrap, | ||
| 2303 | .Equal => .Assign, | ||
| 2304 | else => { | ||
| 2305 | p.putBackToken(token); | ||
| 2306 | return null; | ||
| 2307 | }, | ||
| 2308 | }; | ||
| 2309 | |||
| 2310 | const node = try p.arena.allocator.create(Node.SimpleInfixOp); | ||
| 2311 | node.* = .{ | ||
| 2312 | .base = .{ .tag = op }, | ||
| 2313 | .op_token = token, | ||
| 2314 | .lhs = undefined, // set by caller | ||
| 2315 | .rhs = undefined, // set by caller | ||
| 2316 | }; | ||
| 2317 | return &node.base; | ||
| 2318 | } | ||
| 2319 | |||
| 2320 | /// CompareOp | ||
| 2321 | /// <- EQUALEQUAL | ||
| 2322 | /// / EXCLAMATIONMARKEQUAL | ||
| 2323 | /// / LARROW | ||
| 2324 | /// / RARROW | ||
| 2325 | /// / LARROWEQUAL | ||
| 2326 | /// / RARROWEQUAL | ||
| 2327 | fn parseCompareOp(p: *Parser) !?*Node { | ||
| 2328 | const token = p.nextToken(); | ||
| 2329 | const op: Node.Tag = switch (p.token_ids[token]) { | ||
| 2330 | .EqualEqual => .EqualEqual, | ||
| 2331 | .BangEqual => .BangEqual, | ||
| 2332 | .AngleBracketLeft => .LessThan, | ||
| 2333 | .AngleBracketRight => .GreaterThan, | ||
| 2334 | .AngleBracketLeftEqual => .LessOrEqual, | ||
| 2335 | .AngleBracketRightEqual => .GreaterOrEqual, | ||
| 2336 | else => { | ||
| 2337 | p.putBackToken(token); | ||
| 2338 | return null; | ||
| 2339 | }, | ||
| 2340 | }; | ||
| 2341 | |||
| 2342 | return p.createInfixOp(token, op); | ||
| 2343 | } | ||
| 2344 | |||
| 2345 | /// BitwiseOp | ||
| 2346 | /// <- AMPERSAND | ||
| 2347 | /// / CARET | ||
| 2348 | /// / PIPE | ||
| 2349 | /// / KEYWORD_orelse | ||
| 2350 | /// / KEYWORD_catch Payload? | ||
| 2351 | fn parseBitwiseOp(p: *Parser) !?*Node { | ||
| 2352 | const token = p.nextToken(); | ||
| 2353 | const op: Node.Tag = switch (p.token_ids[token]) { | ||
| 2354 | .Ampersand => .BitAnd, | ||
| 2355 | .Caret => .BitXor, | ||
| 2356 | .Pipe => .BitOr, | ||
| 2357 | .Keyword_orelse => .OrElse, | ||
| 2358 | .Keyword_catch => { | ||
| 2359 | const payload = try p.parsePayload(); | ||
| 2360 | const node = try p.arena.allocator.create(Node.Catch); | ||
| 2361 | node.* = .{ | ||
| 2362 | .op_token = token, | ||
| 2363 | .lhs = undefined, // set by caller | ||
| 2364 | .rhs = undefined, // set by caller | ||
| 2365 | .payload = payload, | ||
| 2366 | }; | ||
| 2367 | return &node.base; | ||
| 2368 | }, | ||
| 2369 | else => { | ||
| 2370 | p.putBackToken(token); | ||
| 2371 | return null; | ||
| 2372 | }, | ||
| 2373 | }; | ||
| 2374 | |||
| 2375 | return p.createInfixOp(token, op); | ||
| 2376 | } | ||
| 2377 | |||
| 2378 | /// BitShiftOp | ||
| 2379 | /// <- LARROW2 | ||
| 2380 | /// / RARROW2 | ||
| 2381 | fn parseBitShiftOp(p: *Parser) !?*Node { | ||
| 2382 | const token = p.nextToken(); | ||
| 2383 | const op: Node.Tag = switch (p.token_ids[token]) { | ||
| 2384 | .AngleBracketAngleBracketLeft => .BitShiftLeft, | ||
| 2385 | .AngleBracketAngleBracketRight => .BitShiftRight, | ||
| 2386 | else => { | ||
| 2387 | p.putBackToken(token); | ||
| 2388 | return null; | ||
| 2389 | }, | ||
| 2390 | }; | ||
| 2391 | |||
| 2392 | return p.createInfixOp(token, op); | ||
| 2393 | } | ||
| 2394 | |||
| 2395 | /// AdditionOp | ||
| 2396 | /// <- PLUS | ||
| 2397 | /// / MINUS | ||
| 2398 | /// / PLUS2 | ||
| 2399 | /// / PLUSPERCENT | ||
| 2400 | /// / MINUSPERCENT | ||
| 2401 | fn parseAdditionOp(p: *Parser) !?*Node { | ||
| 2402 | const token = p.nextToken(); | ||
| 2403 | const op: Node.Tag = switch (p.token_ids[token]) { | ||
| 2404 | .Plus => .Add, | ||
| 2405 | .Minus => .Sub, | ||
| 2406 | .PlusPlus => .ArrayCat, | ||
| 2407 | .PlusPercent => .AddWrap, | ||
| 2408 | .MinusPercent => .SubWrap, | ||
| 2409 | else => { | ||
| 2410 | p.putBackToken(token); | ||
| 2411 | return null; | ||
| 2412 | }, | ||
| 2413 | }; | ||
| 2414 | |||
| 2415 | return p.createInfixOp(token, op); | ||
| 2416 | } | ||
| 2417 | |||
| 2418 | /// MultiplyOp | ||
| 2419 | /// <- PIPE2 | ||
| 2420 | /// / ASTERISK | ||
| 2421 | /// / SLASH | ||
| 2422 | /// / PERCENT | ||
| 2423 | /// / ASTERISK2 | ||
| 2424 | /// / ASTERISKPERCENT | ||
| 2425 | fn parseMultiplyOp(p: *Parser) !?*Node { | ||
| 2426 | const token = p.nextToken(); | ||
| 2427 | const op: Node.Tag = switch (p.token_ids[token]) { | ||
| 2428 | .PipePipe => .MergeErrorSets, | ||
| 2429 | .Asterisk => .Mul, | ||
| 2430 | .Slash => .Div, | ||
| 2431 | .Percent => .Mod, | ||
| 2432 | .AsteriskAsterisk => .ArrayMult, | ||
| 2433 | .AsteriskPercent => .MulWrap, | ||
| 2434 | else => { | ||
| 2435 | p.putBackToken(token); | ||
| 2436 | return null; | ||
| 2437 | }, | ||
| 2438 | }; | ||
| 2439 | |||
| 2440 | return p.createInfixOp(token, op); | ||
| 2441 | } | ||
| 2442 | |||
| 2443 | /// PrefixOp | ||
| 2444 | /// <- EXCLAMATIONMARK | ||
| 2445 | /// / MINUS | ||
| 2446 | /// / TILDE | ||
| 2447 | /// / MINUSPERCENT | ||
| 2448 | /// / AMPERSAND | ||
| 2449 | /// / KEYWORD_try | ||
| 2450 | /// / KEYWORD_await | ||
| 2451 | fn parsePrefixOp(p: *Parser) !?*Node { | ||
| 2452 | const token = p.nextToken(); | ||
| 2453 | switch (p.token_ids[token]) { | ||
| 2454 | .Bang => return p.allocSimplePrefixOp(.BoolNot, token), | ||
| 2455 | .Minus => return p.allocSimplePrefixOp(.Negation, token), | ||
| 2456 | .Tilde => return p.allocSimplePrefixOp(.BitNot, token), | ||
| 2457 | .MinusPercent => return p.allocSimplePrefixOp(.NegationWrap, token), | ||
| 2458 | .Ampersand => return p.allocSimplePrefixOp(.AddressOf, token), | ||
| 2459 | .Keyword_try => return p.allocSimplePrefixOp(.Try, token), | ||
| 2460 | .Keyword_await => return p.allocSimplePrefixOp(.Await, token), | ||
| 2461 | else => { | ||
| 2462 | p.putBackToken(token); | ||
| 2463 | return null; | ||
| 2464 | }, | ||
| 2465 | } | ||
| 2466 | } | ||
| 2467 | |||
| 2468 | fn allocSimplePrefixOp(p: *Parser, comptime tag: Node.Tag, token: TokenIndex) !?*Node { | ||
| 2469 | const node = try p.arena.allocator.create(Node.SimplePrefixOp); | ||
| 2470 | node.* = .{ | ||
| 2471 | .base = .{ .tag = tag }, | ||
| 2472 | .op_token = token, | ||
| 2473 | .rhs = undefined, // set by caller | ||
| 2474 | }; | ||
| 2475 | return &node.base; | ||
| 2476 | } | ||
| 2477 | |||
| 2478 | // TODO: ArrayTypeStart is either an array or a slice, but const/allowzero only work on | ||
| 2479 | // pointers. Consider updating this rule: | ||
| 2480 | // ... | ||
| 2481 | // / ArrayTypeStart | ||
| 2482 | // / SliceTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | ||
| 2483 | // / PtrTypeStart ... | ||
| 2484 | |||
| 2485 | /// PrefixTypeOp | ||
| 2486 | /// <- QUESTIONMARK | ||
| 2487 | /// / KEYWORD_anyframe MINUSRARROW | ||
| 2488 | /// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | ||
| 2489 | /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | ||
| 2490 | fn parsePrefixTypeOp(p: *Parser) !?*Node { | ||
| 2491 | if (p.eatToken(.QuestionMark)) |token| { | ||
| 2492 | const node = try p.arena.allocator.create(Node.SimplePrefixOp); | ||
| 2493 | node.* = .{ | ||
| 2494 | .base = .{ .tag = .OptionalType }, | ||
| 2495 | .op_token = token, | ||
| 2496 | .rhs = undefined, // set by caller | ||
| 2497 | }; | ||
| 2498 | return &node.base; | ||
| 2499 | } | ||
| 2500 | |||
| 2501 | if (p.eatToken(.Keyword_anyframe)) |token| { | ||
| 2502 | const arrow = p.eatToken(.Arrow) orelse { | ||
| 2503 | p.putBackToken(token); | ||
| 2504 | return null; | ||
| 2505 | }; | ||
| 2506 | const node = try p.arena.allocator.create(Node.AnyFrameType); | ||
| 2507 | node.* = .{ | ||
| 2508 | .anyframe_token = token, | ||
| 2509 | .result = .{ | ||
| 2510 | .arrow_token = arrow, | ||
| 2511 | .return_type = undefined, // set by caller | ||
| 2512 | }, | 3334 | }, |
| 2513 | }; | ||
| 2514 | return &node.base; | ||
| 2515 | } | ||
| 2516 | |||
| 2517 | if (try p.parsePtrTypeStart()) |node| { | ||
| 2518 | // If the token encountered was **, there will be two nodes instead of one. | ||
| 2519 | // The attributes should be applied to the rightmost operator. | ||
| 2520 | var ptr_info = if (node.cast(Node.PtrType)) |ptr_type| | ||
| 2521 | if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk) | ||
| 2522 | &ptr_type.rhs.cast(Node.PtrType).?.ptr_info | ||
| 2523 | else | ||
| 2524 | &ptr_type.ptr_info | ||
| 2525 | else if (node.cast(Node.SliceType)) |slice_type| | ||
| 2526 | &slice_type.ptr_info | ||
| 2527 | else | ||
| 2528 | unreachable; | ||
| 2529 | |||
| 2530 | while (true) { | ||
| 2531 | if (p.eatToken(.Keyword_align)) |align_token| { | ||
| 2532 | const lparen = try p.expectToken(.LParen); | ||
| 2533 | const expr_node = try p.expectNode(parseExpr, .{ | ||
| 2534 | .ExpectedExpr = .{ .token = p.tok_i }, | ||
| 2535 | }); | ||
| 2536 | |||
| 2537 | // Optional bit range | ||
| 2538 | const bit_range = if (p.eatToken(.Colon)) |_| bit_range_value: { | ||
| 2539 | const range_start = try p.expectNode(parseIntegerLiteral, .{ | ||
| 2540 | .ExpectedIntegerLiteral = .{ .token = p.tok_i }, | ||
| 2541 | }); | ||
| 2542 | _ = try p.expectToken(.Colon); | ||
| 2543 | const range_end = try p.expectNode(parseIntegerLiteral, .{ | ||
| 2544 | .ExpectedIntegerLiteral = .{ .token = p.tok_i }, | ||
| 2545 | }); | ||
| 2546 | |||
| 2547 | break :bit_range_value ast.PtrInfo.Align.BitRange{ | ||
| 2548 | .start = range_start, | ||
| 2549 | .end = range_end, | ||
| 2550 | }; | ||
| 2551 | } else null; | ||
| 2552 | _ = try p.expectToken(.RParen); | ||
| 2553 | |||
| 2554 | if (ptr_info.align_info != null) { | ||
| 2555 | try p.errors.append(p.gpa, .{ | ||
| 2556 | .ExtraAlignQualifier = .{ .token = p.tok_i - 1 }, | ||
| 2557 | }); | ||
| 2558 | continue; | ||
| 2559 | } | ||
| 2560 | |||
| 2561 | ptr_info.align_info = ast.PtrInfo.Align{ | ||
| 2562 | .node = expr_node, | ||
| 2563 | .bit_range = bit_range, | ||
| 2564 | }; | ||
| 2565 | |||
| 2566 | continue; | ||
| 2567 | } | ||
| 2568 | if (p.eatToken(.Keyword_const)) |const_token| { | ||
| 2569 | if (ptr_info.const_token != null) { | ||
| 2570 | try p.errors.append(p.gpa, .{ | ||
| 2571 | .ExtraConstQualifier = .{ .token = p.tok_i - 1 }, | ||
| 2572 | }); | ||
| 2573 | continue; | ||
| 2574 | } | ||
| 2575 | ptr_info.const_token = const_token; | ||
| 2576 | continue; | ||
| 2577 | } | ||
| 2578 | if (p.eatToken(.Keyword_volatile)) |volatile_token| { | ||
| 2579 | if (ptr_info.volatile_token != null) { | ||
| 2580 | try p.errors.append(p.gpa, .{ | ||
| 2581 | .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 }, | ||
| 2582 | }); | ||
| 2583 | continue; | ||
| 2584 | } | ||
| 2585 | ptr_info.volatile_token = volatile_token; | ||
| 2586 | continue; | ||
| 2587 | } | ||
| 2588 | if (p.eatToken(.Keyword_allowzero)) |allowzero_token| { | ||
| 2589 | if (ptr_info.allowzero_token != null) { | ||
| 2590 | try p.errors.append(p.gpa, .{ | ||
| 2591 | .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 }, | ||
| 2592 | }); | ||
| 2593 | continue; | ||
| 2594 | } | ||
| 2595 | ptr_info.allowzero_token = allowzero_token; | ||
| 2596 | continue; | ||
| 2597 | } | ||
| 2598 | break; | ||
| 2599 | } | ||
| 2600 | |||
| 2601 | return node; | ||
| 2602 | } | ||
| 2603 | |||
| 2604 | if (try p.parseArrayTypeStart()) |node| { | ||
| 2605 | if (node.cast(Node.SliceType)) |slice_type| { | ||
| 2606 | // Collect pointer qualifiers in any order, but disallow duplicates | ||
| 2607 | while (true) { | ||
| 2608 | if (try p.parseByteAlign()) |align_expr| { | ||
| 2609 | if (slice_type.ptr_info.align_info != null) { | ||
| 2610 | try p.errors.append(p.gpa, .{ | ||
| 2611 | .ExtraAlignQualifier = .{ .token = p.tok_i - 1 }, | ||
| 2612 | }); | ||
| 2613 | continue; | ||
| 2614 | } | ||
| 2615 | slice_type.ptr_info.align_info = ast.PtrInfo.Align{ | ||
| 2616 | .node = align_expr, | ||
| 2617 | .bit_range = null, | ||
| 2618 | }; | ||
| 2619 | continue; | ||
| 2620 | } | ||
| 2621 | if (p.eatToken(.Keyword_const)) |const_token| { | ||
| 2622 | if (slice_type.ptr_info.const_token != null) { | ||
| 2623 | try p.errors.append(p.gpa, .{ | ||
| 2624 | .ExtraConstQualifier = .{ .token = p.tok_i - 1 }, | ||
| 2625 | }); | ||
| 2626 | continue; | ||
| 2627 | } | ||
| 2628 | slice_type.ptr_info.const_token = const_token; | ||
| 2629 | continue; | ||
| 2630 | } | ||
| 2631 | if (p.eatToken(.Keyword_volatile)) |volatile_token| { | ||
| 2632 | if (slice_type.ptr_info.volatile_token != null) { | ||
| 2633 | try p.errors.append(p.gpa, .{ | ||
| 2634 | .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 }, | ||
| 2635 | }); | ||
| 2636 | continue; | ||
| 2637 | } | ||
| 2638 | slice_type.ptr_info.volatile_token = volatile_token; | ||
| 2639 | continue; | ||
| 2640 | } | ||
| 2641 | if (p.eatToken(.Keyword_allowzero)) |allowzero_token| { | ||
| 2642 | if (slice_type.ptr_info.allowzero_token != null) { | ||
| 2643 | try p.errors.append(p.gpa, .{ | ||
| 2644 | .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 }, | ||
| 2645 | }); | ||
| 2646 | continue; | ||
| 2647 | } | ||
| 2648 | slice_type.ptr_info.allowzero_token = allowzero_token; | ||
| 2649 | continue; | ||
| 2650 | } | ||
| 2651 | break; | ||
| 2652 | } | ||
| 2653 | } | ||
| 2654 | return node; | ||
| 2655 | } | ||
| 2656 | |||
| 2657 | return null; | ||
| 2658 | } | ||
| 2659 | |||
| 2660 | /// SuffixOp | ||
| 2661 | /// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET | ||
| 2662 | /// / DOT IDENTIFIER | ||
| 2663 | /// / DOTASTERISK | ||
| 2664 | /// / DOTQUESTIONMARK | ||
| 2665 | fn parseSuffixOp(p: *Parser, lhs: *Node) !?*Node { | ||
| 2666 | if (p.eatToken(.LBracket)) |_| { | ||
| 2667 | const index_expr = try p.expectNode(parseExpr, .{ | ||
| 2668 | .ExpectedExpr = .{ .token = p.tok_i }, | ||
| 2669 | }); | 3335 | }); |
| 2670 | |||
| 2671 | if (p.eatToken(.Ellipsis2) != null) { | ||
| 2672 | const end_expr = try p.parseExpr(); | ||
| 2673 | const sentinel: ?*Node = if (p.eatToken(.Colon) != null) | ||
| 2674 | try p.parseExpr() | ||
| 2675 | else | ||
| 2676 | null; | ||
| 2677 | const rtoken = try p.expectToken(.RBracket); | ||
| 2678 | const node = try p.arena.allocator.create(Node.Slice); | ||
| 2679 | node.* = .{ | ||
| 2680 | .lhs = lhs, | ||
| 2681 | .rtoken = rtoken, | ||
| 2682 | .start = index_expr, | ||
| 2683 | .end = end_expr, | ||
| 2684 | .sentinel = sentinel, | ||
| 2685 | }; | ||
| 2686 | return &node.base; | ||
| 2687 | } | ||
| 2688 | |||
| 2689 | const rtoken = try p.expectToken(.RBracket); | ||
| 2690 | const node = try p.arena.allocator.create(Node.ArrayAccess); | ||
| 2691 | node.* = .{ | ||
| 2692 | .lhs = lhs, | ||
| 2693 | .rtoken = rtoken, | ||
| 2694 | .index_expr = index_expr, | ||
| 2695 | }; | ||
| 2696 | return &node.base; | ||
| 2697 | } | 3336 | } |
| 2698 | 3337 | ||
| 2699 | if (p.eatToken(.PeriodAsterisk)) |period_asterisk| { | 3338 | var list = std.ArrayList(Node.Index).init(p.gpa); |
| 2700 | const node = try p.arena.allocator.create(Node.SimpleSuffixOp); | 3339 | defer list.deinit(); |
| 2701 | node.* = .{ | ||
| 2702 | .base = .{ .tag = .Deref }, | ||
| 2703 | .lhs = lhs, | ||
| 2704 | .rtoken = period_asterisk, | ||
| 2705 | }; | ||
| 2706 | return &node.base; | ||
| 2707 | } | ||
| 2708 | 3340 | ||
| 2709 | if (p.eatToken(.Invalid_periodasterisks)) |period_asterisk| { | 3341 | try list.append(first_item); |
| 2710 | try p.errors.append(p.gpa, .{ | 3342 | while (p.eatToken(.comma)) |_| { |
| 2711 | .AsteriskAfterPointerDereference = .{ .token = period_asterisk }, | 3343 | const next_item = try p.parseSwitchItem(); |
| 2712 | }); | 3344 | if (next_item == 0) break; |
| 2713 | const node = try p.arena.allocator.create(Node.SimpleSuffixOp); | 3345 | try list.append(next_item); |
| 2714 | node.* = .{ | 3346 | } |
| 2715 | .base = .{ .tag = .Deref }, | 3347 | const span = try p.listToSpan(list.items); |
| 2716 | .lhs = lhs, | 3348 | const arrow_token = try p.expectToken(.equal_angle_bracket_right); |
| 2717 | .rtoken = period_asterisk, | 3349 | _ = try p.parsePtrPayload(); |
| 2718 | }; | 3350 | return p.addNode(.{ |
| 2719 | return &node.base; | 3351 | .tag = .switch_case, |
| 2720 | } | 3352 | .main_token = arrow_token, |
| 3353 | .data = .{ | ||
| 3354 | .lhs = try p.addExtra(Node.SubRange{ | ||
| 3355 | .start = span.start, | ||
| 3356 | .end = span.end, | ||
| 3357 | }), | ||
| 3358 | .rhs = try p.expectAssignExpr(), | ||
| 3359 | }, | ||
| 3360 | }); | ||
| 3361 | } | ||
| 2721 | 3362 | ||
| 2722 | if (p.eatToken(.Period)) |period| { | 3363 | /// SwitchItem <- Expr (DOT3 Expr)? |
| 2723 | if (try p.parseIdentifier()) |identifier| { | 3364 | fn parseSwitchItem(p: *Parser) !Node.Index { |
| 2724 | const node = try p.arena.allocator.create(Node.SimpleInfixOp); | 3365 | const expr = try p.parseExpr(); |
| 2725 | node.* = .{ | 3366 | if (expr == 0) return null_node; |
| 2726 | .base = Node{ .tag = .Period }, | 3367 | |
| 2727 | .op_token = period, | 3368 | if (p.eatToken(.ellipsis3)) |token| { |
| 2728 | .lhs = lhs, | 3369 | return p.addNode(.{ |
| 2729 | .rhs = identifier, | 3370 | .tag = .switch_range, |
| 2730 | }; | 3371 | .main_token = token, |
| 2731 | return &node.base; | 3372 | .data = .{ |
| 2732 | } | 3373 | .lhs = expr, |
| 2733 | if (p.eatToken(.QuestionMark)) |question_mark| { | 3374 | .rhs = try p.expectExpr(), |
| 2734 | const node = try p.arena.allocator.create(Node.SimpleSuffixOp); | 3375 | }, |
| 2735 | node.* = .{ | ||
| 2736 | .base = .{ .tag = .UnwrapOptional }, | ||
| 2737 | .lhs = lhs, | ||
| 2738 | .rtoken = question_mark, | ||
| 2739 | }; | ||
| 2740 | return &node.base; | ||
| 2741 | } | ||
| 2742 | try p.errors.append(p.gpa, .{ | ||
| 2743 | .ExpectedSuffixOp = .{ .token = p.tok_i }, | ||
| 2744 | }); | 3376 | }); |
| 2745 | return null; | ||
| 2746 | } | 3377 | } |
| 2747 | 3378 | return expr; | |
| 2748 | return null; | ||
| 2749 | } | ||
| 2750 | |||
| 2751 | /// FnCallArguments <- LPAREN ExprList RPAREN | ||
| 2752 | /// ExprList <- (Expr COMMA)* Expr? | ||
| 2753 | fn parseFnCallArguments(p: *Parser) !?AnnotatedParamList { | ||
| 2754 | if (p.eatToken(.LParen) == null) return null; | ||
| 2755 | const list = try ListParseFn(*Node, parseExpr)(p); | ||
| 2756 | errdefer p.gpa.free(list); | ||
| 2757 | const rparen = try p.expectToken(.RParen); | ||
| 2758 | return AnnotatedParamList{ .list = list, .rparen = rparen }; | ||
| 2759 | } | 3379 | } |
| 2760 | 3380 | ||
| 2761 | const AnnotatedParamList = struct { | 3381 | const PtrModifiers = struct { |
| 2762 | list: []*Node, | 3382 | align_node: Node.Index, |
| 2763 | rparen: TokenIndex, | 3383 | bit_range_start: Node.Index, |
| 3384 | bit_range_end: Node.Index, | ||
| 2764 | }; | 3385 | }; |
| 2765 | 3386 | ||
| 2766 | /// ArrayTypeStart <- LBRACKET Expr? (COLON Expr)? RBRACKET | 3387 | fn parsePtrModifiers(p: *Parser) !PtrModifiers { |
| 2767 | fn parseArrayTypeStart(p: *Parser) !?*Node { | 3388 | var result: PtrModifiers = .{ |
| 2768 | const lbracket = p.eatToken(.LBracket) orelse return null; | 3389 | .align_node = 0, |
| 2769 | const expr = try p.parseExpr(); | 3390 | .bit_range_start = 0, |
| 2770 | const sentinel = if (p.eatToken(.Colon)) |_| | 3391 | .bit_range_end = 0, |
| 2771 | try p.expectNode(parseExpr, .{ | ||
| 2772 | .ExpectedExpr = .{ .token = p.tok_i }, | ||
| 2773 | }) | ||
| 2774 | else | ||
| 2775 | null; | ||
| 2776 | const rbracket = try p.expectToken(.RBracket); | ||
| 2777 | |||
| 2778 | if (expr) |len_expr| { | ||
| 2779 | if (sentinel) |s| { | ||
| 2780 | const node = try p.arena.allocator.create(Node.ArrayTypeSentinel); | ||
| 2781 | node.* = .{ | ||
| 2782 | .op_token = lbracket, | ||
| 2783 | .rhs = undefined, // set by caller | ||
| 2784 | .len_expr = len_expr, | ||
| 2785 | .sentinel = s, | ||
| 2786 | }; | ||
| 2787 | return &node.base; | ||
| 2788 | } else { | ||
| 2789 | const node = try p.arena.allocator.create(Node.ArrayType); | ||
| 2790 | node.* = .{ | ||
| 2791 | .op_token = lbracket, | ||
| 2792 | .rhs = undefined, // set by caller | ||
| 2793 | .len_expr = len_expr, | ||
| 2794 | }; | ||
| 2795 | return &node.base; | ||
| 2796 | } | ||
| 2797 | } | ||
| 2798 | |||
| 2799 | const node = try p.arena.allocator.create(Node.SliceType); | ||
| 2800 | node.* = .{ | ||
| 2801 | .op_token = lbracket, | ||
| 2802 | .rhs = undefined, // set by caller | ||
| 2803 | .ptr_info = .{ .sentinel = sentinel }, | ||
| 2804 | }; | 3392 | }; |
| 2805 | return &node.base; | 3393 | var saw_const = false; |
| 2806 | } | 3394 | var saw_volatile = false; |
| 2807 | 3395 | var saw_allowzero = false; | |
| 2808 | /// PtrTypeStart | 3396 | while (true) { |
| 2809 | /// <- ASTERISK | 3397 | switch (p.token_tags[p.tok_i]) { |
| 2810 | /// / ASTERISK2 | 3398 | .keyword_align => { |
| 2811 | /// / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET | 3399 | if (result.align_node != 0) { |
| 2812 | fn parsePtrTypeStart(p: *Parser) !?*Node { | 3400 | try p.warn(.extra_align_qualifier); |
| 2813 | if (p.eatToken(.Asterisk)) |asterisk| { | 3401 | } |
| 2814 | const sentinel = if (p.eatToken(.Colon)) |_| | 3402 | p.tok_i += 1; |
| 2815 | try p.expectNode(parseExpr, .{ | 3403 | _ = try p.expectToken(.l_paren); |
| 2816 | .ExpectedExpr = .{ .token = p.tok_i }, | 3404 | result.align_node = try p.expectExpr(); |
| 2817 | }) | 3405 | |
| 2818 | else | 3406 | if (p.eatToken(.colon)) |_| { |
| 2819 | null; | 3407 | result.bit_range_start = try p.expectExpr(); |
| 2820 | const node = try p.arena.allocator.create(Node.PtrType); | 3408 | _ = try p.expectToken(.colon); |
| 2821 | node.* = .{ | 3409 | result.bit_range_end = try p.expectExpr(); |
| 2822 | .op_token = asterisk, | 3410 | } |
| 2823 | .rhs = undefined, // set by caller | ||
| 2824 | .ptr_info = .{ .sentinel = sentinel }, | ||
| 2825 | }; | ||
| 2826 | return &node.base; | ||
| 2827 | } | ||
| 2828 | |||
| 2829 | if (p.eatToken(.AsteriskAsterisk)) |double_asterisk| { | ||
| 2830 | const node = try p.arena.allocator.create(Node.PtrType); | ||
| 2831 | node.* = .{ | ||
| 2832 | .op_token = double_asterisk, | ||
| 2833 | .rhs = undefined, // set by caller | ||
| 2834 | }; | ||
| 2835 | |||
| 2836 | // Special case for **, which is its own token | ||
| 2837 | const child = try p.arena.allocator.create(Node.PtrType); | ||
| 2838 | child.* = .{ | ||
| 2839 | .op_token = double_asterisk, | ||
| 2840 | .rhs = undefined, // set by caller | ||
| 2841 | }; | ||
| 2842 | node.rhs = &child.base; | ||
| 2843 | 3411 | ||
| 2844 | return &node.base; | 3412 | _ = try p.expectToken(.r_paren); |
| 2845 | } | 3413 | }, |
| 2846 | if (p.eatToken(.LBracket)) |lbracket| { | 3414 | .keyword_const => { |
| 2847 | const asterisk = p.eatToken(.Asterisk) orelse { | 3415 | if (saw_const) { |
| 2848 | p.putBackToken(lbracket); | 3416 | try p.warn(.extra_const_qualifier); |
| 2849 | return null; | 3417 | } |
| 2850 | }; | 3418 | p.tok_i += 1; |
| 2851 | if (p.eatToken(.Identifier)) |ident| { | 3419 | saw_const = true; |
| 2852 | const token_loc = p.token_locs[ident]; | 3420 | }, |
| 2853 | const token_slice = p.source[token_loc.start..token_loc.end]; | 3421 | .keyword_volatile => { |
| 2854 | if (!std.mem.eql(u8, token_slice, "c")) { | 3422 | if (saw_volatile) { |
| 2855 | p.putBackToken(ident); | 3423 | try p.warn(.extra_volatile_qualifier); |
| 2856 | } else { | 3424 | } |
| 2857 | _ = try p.expectToken(.RBracket); | 3425 | p.tok_i += 1; |
| 2858 | const node = try p.arena.allocator.create(Node.PtrType); | 3426 | saw_volatile = true; |
| 2859 | node.* = .{ | 3427 | }, |
| 2860 | .op_token = lbracket, | 3428 | .keyword_allowzero => { |
| 2861 | .rhs = undefined, // set by caller | 3429 | if (saw_allowzero) { |
| 2862 | }; | 3430 | try p.warn(.extra_allowzero_qualifier); |
| 2863 | return &node.base; | 3431 | } |
| 2864 | } | 3432 | p.tok_i += 1; |
| 3433 | saw_allowzero = true; | ||
| 3434 | }, | ||
| 3435 | else => return result, | ||
| 2865 | } | 3436 | } |
| 2866 | const sentinel = if (p.eatToken(.Colon)) |_| | ||
| 2867 | try p.expectNode(parseExpr, .{ | ||
| 2868 | .ExpectedExpr = .{ .token = p.tok_i }, | ||
| 2869 | }) | ||
| 2870 | else | ||
| 2871 | null; | ||
| 2872 | _ = try p.expectToken(.RBracket); | ||
| 2873 | const node = try p.arena.allocator.create(Node.PtrType); | ||
| 2874 | node.* = .{ | ||
| 2875 | .op_token = lbracket, | ||
| 2876 | .rhs = undefined, // set by caller | ||
| 2877 | .ptr_info = .{ .sentinel = sentinel }, | ||
| 2878 | }; | ||
| 2879 | return &node.base; | ||
| 2880 | } | 3437 | } |
| 2881 | return null; | ||
| 2882 | } | 3438 | } |
| 2883 | 3439 | ||
| 2884 | /// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE | 3440 | /// SuffixOp |
| 2885 | fn parseContainerDeclAuto(p: *Parser) !?*Node { | 3441 | /// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET |
| 2886 | const container_decl_type = (try p.parseContainerDeclType()) orelse return null; | 3442 | /// / DOT IDENTIFIER |
| 2887 | const lbrace = try p.expectToken(.LBrace); | 3443 | /// / DOTASTERISK |
| 2888 | const members = try p.parseContainerMembers(false); | 3444 | /// / DOTQUESTIONMARK |
| 2889 | defer p.gpa.free(members); | 3445 | fn parseSuffixOp(p: *Parser, lhs: Node.Index) !Node.Index { |
| 2890 | const rbrace = try p.expectToken(.RBrace); | 3446 | switch (p.token_tags[p.tok_i]) { |
| 2891 | 3447 | .l_bracket => { | |
| 2892 | const members_len = @intCast(NodeIndex, members.len); | 3448 | const lbracket = p.nextToken(); |
| 2893 | const node = try Node.ContainerDecl.alloc(&p.arena.allocator, members_len); | 3449 | const index_expr = try p.expectExpr(); |
| 2894 | node.* = .{ | 3450 | |
| 2895 | .layout_token = null, | 3451 | if (p.eatToken(.ellipsis2)) |_| { |
| 2896 | .kind_token = container_decl_type.kind_token, | 3452 | const end_expr = try p.parseExpr(); |
| 2897 | .init_arg_expr = container_decl_type.init_arg_expr, | 3453 | if (end_expr == 0) { |
| 2898 | .fields_and_decls_len = members_len, | 3454 | _ = try p.expectToken(.r_bracket); |
| 2899 | .lbrace_token = lbrace, | 3455 | return p.addNode(.{ |
| 2900 | .rbrace_token = rbrace, | 3456 | .tag = .slice_open, |
| 2901 | }; | 3457 | .main_token = lbracket, |
| 2902 | std.mem.copy(*Node, node.fieldsAndDecls(), members); | 3458 | .data = .{ |
| 2903 | return &node.base; | 3459 | .lhs = lhs, |
| 3460 | .rhs = index_expr, | ||
| 3461 | }, | ||
| 3462 | }); | ||
| 3463 | } | ||
| 3464 | if (p.eatToken(.colon)) |_| { | ||
| 3465 | const sentinel = try p.parseExpr(); | ||
| 3466 | _ = try p.expectToken(.r_bracket); | ||
| 3467 | return p.addNode(.{ | ||
| 3468 | .tag = .slice_sentinel, | ||
| 3469 | .main_token = lbracket, | ||
| 3470 | .data = .{ | ||
| 3471 | .lhs = lhs, | ||
| 3472 | .rhs = try p.addExtra(Node.SliceSentinel{ | ||
| 3473 | .start = index_expr, | ||
| 3474 | .end = end_expr, | ||
| 3475 | .sentinel = sentinel, | ||
| 3476 | }), | ||
| 3477 | }, | ||
| 3478 | }); | ||
| 3479 | } else { | ||
| 3480 | _ = try p.expectToken(.r_bracket); | ||
| 3481 | return p.addNode(.{ | ||
| 3482 | .tag = .slice, | ||
| 3483 | .main_token = lbracket, | ||
| 3484 | .data = .{ | ||
| 3485 | .lhs = lhs, | ||
| 3486 | .rhs = try p.addExtra(Node.Slice{ | ||
| 3487 | .start = index_expr, | ||
| 3488 | .end = end_expr, | ||
| 3489 | }), | ||
| 3490 | }, | ||
| 3491 | }); | ||
| 3492 | } | ||
| 3493 | } | ||
| 3494 | _ = try p.expectToken(.r_bracket); | ||
| 3495 | return p.addNode(.{ | ||
| 3496 | .tag = .array_access, | ||
| 3497 | .main_token = lbracket, | ||
| 3498 | .data = .{ | ||
| 3499 | .lhs = lhs, | ||
| 3500 | .rhs = index_expr, | ||
| 3501 | }, | ||
| 3502 | }); | ||
| 3503 | }, | ||
| 3504 | .period_asterisk => return p.addNode(.{ | ||
| 3505 | .tag = .deref, | ||
| 3506 | .main_token = p.nextToken(), | ||
| 3507 | .data = .{ | ||
| 3508 | .lhs = lhs, | ||
| 3509 | .rhs = undefined, | ||
| 3510 | }, | ||
| 3511 | }), | ||
| 3512 | .invalid_periodasterisks => { | ||
| 3513 | try p.warn(.asterisk_after_ptr_deref); | ||
| 3514 | return p.addNode(.{ | ||
| 3515 | .tag = .deref, | ||
| 3516 | .main_token = p.nextToken(), | ||
| 3517 | .data = .{ | ||
| 3518 | .lhs = lhs, | ||
| 3519 | .rhs = undefined, | ||
| 3520 | }, | ||
| 3521 | }); | ||
| 3522 | }, | ||
| 3523 | .period => switch (p.token_tags[p.tok_i + 1]) { | ||
| 3524 | .identifier => return p.addNode(.{ | ||
| 3525 | .tag = .field_access, | ||
| 3526 | .main_token = p.nextToken(), | ||
| 3527 | .data = .{ | ||
| 3528 | .lhs = lhs, | ||
| 3529 | .rhs = p.nextToken(), | ||
| 3530 | }, | ||
| 3531 | }), | ||
| 3532 | .question_mark => return p.addNode(.{ | ||
| 3533 | .tag = .unwrap_optional, | ||
| 3534 | .main_token = p.nextToken(), | ||
| 3535 | .data = .{ | ||
| 3536 | .lhs = lhs, | ||
| 3537 | .rhs = p.nextToken(), | ||
| 3538 | }, | ||
| 3539 | }), | ||
| 3540 | else => { | ||
| 3541 | p.tok_i += 1; | ||
| 3542 | try p.warn(.expected_suffix_op); | ||
| 3543 | return null_node; | ||
| 3544 | }, | ||
| 3545 | }, | ||
| 3546 | else => return null_node, | ||
| 3547 | } | ||
| 2904 | } | 3548 | } |
| 2905 | 3549 | ||
| 2906 | /// Holds temporary data until we are ready to construct the full ContainerDecl AST node. | 3550 | /// Caller must have already verified the first token. |
| 2907 | const ContainerDeclType = struct { | ||
| 2908 | kind_token: TokenIndex, | ||
| 2909 | init_arg_expr: Node.ContainerDecl.InitArg, | ||
| 2910 | }; | ||
| 2911 | |||
| 2912 | /// ContainerDeclType | 3551 | /// ContainerDeclType |
| 2913 | /// <- KEYWORD_struct | 3552 | /// <- KEYWORD_struct |
| 2914 | /// / KEYWORD_enum (LPAREN Expr RPAREN)? | 3553 | /// / KEYWORD_enum (LPAREN Expr RPAREN)? |
| 2915 | /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? | 3554 | /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? |
| 2916 | /// / KEYWORD_opaque | 3555 | /// / KEYWORD_opaque |
| 2917 | fn parseContainerDeclType(p: *Parser) !?ContainerDeclType { | 3556 | fn parseContainerDeclAuto(p: *Parser) !Node.Index { |
| 2918 | const kind_token = p.nextToken(); | 3557 | const main_token = p.nextToken(); |
| 2919 | 3558 | const arg_expr = switch (p.token_tags[main_token]) { | |
| 2920 | const init_arg_expr = switch (p.token_ids[kind_token]) { | 3559 | .keyword_struct, .keyword_opaque => null_node, |
| 2921 | .Keyword_struct, .Keyword_opaque => Node.ContainerDecl.InitArg{ .None = {} }, | 3560 | .keyword_enum => blk: { |
| 2922 | .Keyword_enum => blk: { | 3561 | if (p.eatToken(.l_paren)) |_| { |
| 2923 | if (p.eatToken(.LParen) != null) { | 3562 | const expr = try p.expectExpr(); |
| 2924 | const expr = try p.expectNode(parseExpr, .{ | 3563 | _ = try p.expectToken(.r_paren); |
| 2925 | .ExpectedExpr = .{ .token = p.tok_i }, | 3564 | break :blk expr; |
| 2926 | }); | 3565 | } else { |
| 2927 | _ = try p.expectToken(.RParen); | 3566 | break :blk null_node; |
| 2928 | break :blk Node.ContainerDecl.InitArg{ .Type = expr }; | ||
| 2929 | } | 3567 | } |
| 2930 | break :blk Node.ContainerDecl.InitArg{ .None = {} }; | 3568 | }, |
| 2931 | }, | 3569 | .keyword_union => blk: { |
| 2932 | .Keyword_union => blk: { | 3570 | if (p.eatToken(.l_paren)) |_| { |
| 2933 | if (p.eatToken(.LParen) != null) { | 3571 | if (p.eatToken(.keyword_enum)) |_| { |
| 2934 | if (p.eatToken(.Keyword_enum) != null) { | 3572 | if (p.eatToken(.l_paren)) |_| { |
| 2935 | if (p.eatToken(.LParen) != null) { | 3573 | const enum_tag_expr = try p.expectExpr(); |
| 2936 | const expr = try p.expectNode(parseExpr, .{ | 3574 | _ = try p.expectToken(.r_paren); |
| 2937 | .ExpectedExpr = .{ .token = p.tok_i }, | 3575 | _ = try p.expectToken(.r_paren); |
| 3576 | |||
| 3577 | _ = try p.expectToken(.l_brace); | ||
| 3578 | const members = try p.parseContainerMembers(); | ||
| 3579 | const members_span = try members.toSpan(p); | ||
| 3580 | _ = try p.expectToken(.r_brace); | ||
| 3581 | return p.addNode(.{ | ||
| 3582 | .tag = switch (members.trailing) { | ||
| 3583 | true => .tagged_union_enum_tag_trailing, | ||
| 3584 | false => .tagged_union_enum_tag, | ||
| 3585 | }, | ||
| 3586 | .main_token = main_token, | ||
| 3587 | .data = .{ | ||
| 3588 | .lhs = enum_tag_expr, | ||
| 3589 | .rhs = try p.addExtra(members_span), | ||
| 3590 | }, | ||
| 2938 | }); | 3591 | }); |
| 2939 | _ = try p.expectToken(.RParen); | 3592 | } else { |
| 2940 | _ = try p.expectToken(.RParen); | 3593 | _ = try p.expectToken(.r_paren); |
| 2941 | break :blk Node.ContainerDecl.InitArg{ .Enum = expr }; | 3594 | |
| 3595 | _ = try p.expectToken(.l_brace); | ||
| 3596 | const members = try p.parseContainerMembers(); | ||
| 3597 | _ = try p.expectToken(.r_brace); | ||
| 3598 | if (members.len <= 2) { | ||
| 3599 | return p.addNode(.{ | ||
| 3600 | .tag = switch (members.trailing) { | ||
| 3601 | true => .tagged_union_two_trailing, | ||
| 3602 | false => .tagged_union_two, | ||
| 3603 | }, | ||
| 3604 | .main_token = main_token, | ||
| 3605 | .data = .{ | ||
| 3606 | .lhs = members.lhs, | ||
| 3607 | .rhs = members.rhs, | ||
| 3608 | }, | ||
| 3609 | }); | ||
| 3610 | } else { | ||
| 3611 | const span = try members.toSpan(p); | ||
| 3612 | return p.addNode(.{ | ||
| 3613 | .tag = switch (members.trailing) { | ||
| 3614 | true => .tagged_union_trailing, | ||
| 3615 | false => .tagged_union, | ||
| 3616 | }, | ||
| 3617 | .main_token = main_token, | ||
| 3618 | .data = .{ | ||
| 3619 | .lhs = span.start, | ||
| 3620 | .rhs = span.end, | ||
| 3621 | }, | ||
| 3622 | }); | ||
| 3623 | } | ||
| 2942 | } | 3624 | } |
| 2943 | _ = try p.expectToken(.RParen); | 3625 | } else { |
| 2944 | break :blk Node.ContainerDecl.InitArg{ .Enum = null }; | 3626 | const expr = try p.expectExpr(); |
| 3627 | _ = try p.expectToken(.r_paren); | ||
| 3628 | break :blk expr; | ||
| 2945 | } | 3629 | } |
| 2946 | const expr = try p.expectNode(parseExpr, .{ | 3630 | } else { |
| 2947 | .ExpectedExpr = .{ .token = p.tok_i }, | 3631 | break :blk null_node; |
| 2948 | }); | ||
| 2949 | _ = try p.expectToken(.RParen); | ||
| 2950 | break :blk Node.ContainerDecl.InitArg{ .Type = expr }; | ||
| 2951 | } | 3632 | } |
| 2952 | break :blk Node.ContainerDecl.InitArg{ .None = {} }; | ||
| 2953 | }, | 3633 | }, |
| 2954 | else => { | 3634 | else => { |
| 2955 | p.putBackToken(kind_token); | 3635 | p.tok_i -= 1; |
| 2956 | return null; | 3636 | return p.fail(.expected_container); |
| 2957 | }, | 3637 | }, |
| 2958 | }; | 3638 | }; |
| 2959 | 3639 | _ = try p.expectToken(.l_brace); | |
| 2960 | return ContainerDeclType{ | 3640 | const members = try p.parseContainerMembers(); |
| 2961 | .kind_token = kind_token, | 3641 | _ = try p.expectToken(.r_brace); |
| 2962 | .init_arg_expr = init_arg_expr, | 3642 | if (arg_expr == 0) { |
| 2963 | }; | 3643 | if (members.len <= 2) { |
| 3644 | return p.addNode(.{ | ||
| 3645 | .tag = switch (members.trailing) { | ||
| 3646 | true => .container_decl_two_trailing, | ||
| 3647 | false => .container_decl_two, | ||
| 3648 | }, | ||
| 3649 | .main_token = main_token, | ||
| 3650 | .data = .{ | ||
| 3651 | .lhs = members.lhs, | ||
| 3652 | .rhs = members.rhs, | ||
| 3653 | }, | ||
| 3654 | }); | ||
| 3655 | } else { | ||
| 3656 | const span = try members.toSpan(p); | ||
| 3657 | return p.addNode(.{ | ||
| 3658 | .tag = switch (members.trailing) { | ||
| 3659 | true => .container_decl_trailing, | ||
| 3660 | false => .container_decl, | ||
| 3661 | }, | ||
| 3662 | .main_token = main_token, | ||
| 3663 | .data = .{ | ||
| 3664 | .lhs = span.start, | ||
| 3665 | .rhs = span.end, | ||
| 3666 | }, | ||
| 3667 | }); | ||
| 3668 | } | ||
| 3669 | } else { | ||
| 3670 | const span = try members.toSpan(p); | ||
| 3671 | return p.addNode(.{ | ||
| 3672 | .tag = switch (members.trailing) { | ||
| 3673 | true => .container_decl_arg_trailing, | ||
| 3674 | false => .container_decl_arg, | ||
| 3675 | }, | ||
| 3676 | .main_token = main_token, | ||
| 3677 | .data = .{ | ||
| 3678 | .lhs = arg_expr, | ||
| 3679 | .rhs = try p.addExtra(Node.SubRange{ | ||
| 3680 | .start = span.start, | ||
| 3681 | .end = span.end, | ||
| 3682 | }), | ||
| 3683 | }, | ||
| 3684 | }); | ||
| 3685 | } | ||
| 2964 | } | 3686 | } |
| 2965 | 3687 | ||
| 3688 | /// Holds temporary data until we are ready to construct the full ContainerDecl AST node. | ||
| 2966 | /// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN | 3689 | /// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN |
| 2967 | fn parseByteAlign(p: *Parser) !?*Node { | 3690 | fn parseByteAlign(p: *Parser) !Node.Index { |
| 2968 | _ = p.eatToken(.Keyword_align) orelse return null; | 3691 | _ = p.eatToken(.keyword_align) orelse return null_node; |
| 2969 | _ = try p.expectToken(.LParen); | 3692 | _ = try p.expectToken(.l_paren); |
| 2970 | const expr = try p.expectNode(parseExpr, .{ | 3693 | const expr = try p.expectExpr(); |
| 2971 | .ExpectedExpr = .{ .token = p.tok_i }, | 3694 | _ = try p.expectToken(.r_paren); |
| 2972 | }); | ||
| 2973 | _ = try p.expectToken(.RParen); | ||
| 2974 | return expr; | 3695 | return expr; |
| 2975 | } | 3696 | } |
| 2976 | 3697 | ||
| 2977 | /// IdentifierList <- (IDENTIFIER COMMA)* IDENTIFIER? | ||
| 2978 | /// Only ErrorSetDecl parses an IdentifierList | ||
| 2979 | fn parseErrorTagList(p: *Parser) ![]*Node { | ||
| 2980 | return ListParseFn(*Node, parseErrorTag)(p); | ||
| 2981 | } | ||
| 2982 | |||
| 2983 | /// SwitchProngList <- (SwitchProng COMMA)* SwitchProng? | 3698 | /// SwitchProngList <- (SwitchProng COMMA)* SwitchProng? |
| 2984 | fn parseSwitchProngList(p: *Parser) ![]*Node { | 3699 | fn parseSwitchProngList(p: *Parser) !Node.SubRange { |
| 2985 | return ListParseFn(*Node, parseSwitchProng)(p); | 3700 | return ListParseFn(parseSwitchProng)(p); |
| 2986 | } | 3701 | } |
| 2987 | 3702 | ||
| 2988 | /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem? | 3703 | /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl? |
| 2989 | fn parseAsmOutputList(p: *Parser) Error![]Node.Asm.Output { | 3704 | fn parseParamDeclList(p: *Parser) !SmallSpan { |
| 2990 | return ListParseFn(Node.Asm.Output, parseAsmOutputItem)(p); | 3705 | _ = try p.expectToken(.l_paren); |
| 2991 | } | 3706 | if (p.eatToken(.r_paren)) |_| { |
| 3707 | return SmallSpan{ .zero_or_one = 0 }; | ||
| 3708 | } | ||
| 3709 | const param_one = while (true) { | ||
| 3710 | const param = try p.expectParamDecl(); | ||
| 3711 | if (param != 0) break param; | ||
| 3712 | switch (p.token_tags[p.nextToken()]) { | ||
| 3713 | .comma => { | ||
| 3714 | if (p.eatToken(.r_paren)) |_| { | ||
| 3715 | return SmallSpan{ .zero_or_one = 0 }; | ||
| 3716 | } | ||
| 3717 | continue; | ||
| 3718 | }, | ||
| 3719 | .r_paren => return SmallSpan{ .zero_or_one = 0 }, | ||
| 3720 | else => { | ||
| 3721 | // This is likely just a missing comma; | ||
| 3722 | // give an error but continue parsing this list. | ||
| 3723 | p.tok_i -= 1; | ||
| 3724 | try p.warnExpected(.comma); | ||
| 3725 | }, | ||
| 3726 | } | ||
| 3727 | } else unreachable; | ||
| 2992 | 3728 | ||
| 2993 | /// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem? | 3729 | const param_two = while (true) { |
| 2994 | fn parseAsmInputList(p: *Parser) Error![]Node.Asm.Input { | 3730 | switch (p.token_tags[p.nextToken()]) { |
| 2995 | return ListParseFn(Node.Asm.Input, parseAsmInputItem)(p); | 3731 | .comma => { |
| 2996 | } | 3732 | if (p.eatToken(.r_paren)) |_| { |
| 3733 | return SmallSpan{ .zero_or_one = param_one }; | ||
| 3734 | } | ||
| 3735 | const param = try p.expectParamDecl(); | ||
| 3736 | if (param != 0) break param; | ||
| 3737 | continue; | ||
| 3738 | }, | ||
| 3739 | .r_paren => return SmallSpan{ .zero_or_one = param_one }, | ||
| 3740 | .colon, .r_brace, .r_bracket => { | ||
| 3741 | p.tok_i -= 1; | ||
| 3742 | return p.failExpected(.r_paren); | ||
| 3743 | }, | ||
| 3744 | else => { | ||
| 3745 | // This is likely just a missing comma; | ||
| 3746 | // give an error but continue parsing this list. | ||
| 3747 | p.tok_i -= 1; | ||
| 3748 | try p.warnExpected(.comma); | ||
| 3749 | }, | ||
| 3750 | } | ||
| 3751 | } else unreachable; | ||
| 2997 | 3752 | ||
| 2998 | /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl? | 3753 | var list = std.ArrayList(Node.Index).init(p.gpa); |
| 2999 | fn parseParamDeclList(p: *Parser) ![]Node.FnProto.ParamDecl { | 3754 | defer list.deinit(); |
| 3000 | return ListParseFn(Node.FnProto.ParamDecl, parseParamDecl)(p); | 3755 | |
| 3756 | try list.appendSlice(&.{ param_one, param_two }); | ||
| 3757 | |||
| 3758 | while (true) { | ||
| 3759 | switch (p.token_tags[p.nextToken()]) { | ||
| 3760 | .comma => { | ||
| 3761 | if (p.token_tags[p.tok_i] == .r_paren) { | ||
| 3762 | p.tok_i += 1; | ||
| 3763 | return SmallSpan{ .multi = list.toOwnedSlice() }; | ||
| 3764 | } | ||
| 3765 | const param = try p.expectParamDecl(); | ||
| 3766 | if (param != 0) { | ||
| 3767 | try list.append(param); | ||
| 3768 | } | ||
| 3769 | continue; | ||
| 3770 | }, | ||
| 3771 | .r_paren => return SmallSpan{ .multi = list.toOwnedSlice() }, | ||
| 3772 | .colon, .r_brace, .r_bracket => { | ||
| 3773 | p.tok_i -= 1; | ||
| 3774 | return p.failExpected(.r_paren); | ||
| 3775 | }, | ||
| 3776 | else => { | ||
| 3777 | // This is likely just a missing comma; | ||
| 3778 | // give an error but continue parsing this list. | ||
| 3779 | p.tok_i -= 1; | ||
| 3780 | try p.warnExpected(.comma); | ||
| 3781 | }, | ||
| 3782 | } | ||
| 3783 | } | ||
| 3001 | } | 3784 | } |
| 3002 | 3785 | ||
| 3003 | const NodeParseFn = fn (p: *Parser) Error!?*Node; | 3786 | const NodeParseFn = fn (p: *Parser) Error!Node.Index; |
| 3004 | 3787 | ||
| 3005 | fn ListParseFn(comptime E: type, comptime nodeParseFn: anytype) ParseFn([]E) { | 3788 | fn ListParseFn(comptime nodeParseFn: anytype) (fn (p: *Parser) Error!Node.SubRange) { |
| 3006 | return struct { | 3789 | return struct { |
| 3007 | pub fn parse(p: *Parser) ![]E { | 3790 | pub fn parse(p: *Parser) Error!Node.SubRange { |
| 3008 | var list = std.ArrayList(E).init(p.gpa); | 3791 | var list = std.ArrayList(Node.Index).init(p.gpa); |
| 3009 | defer list.deinit(); | 3792 | defer list.deinit(); |
| 3010 | 3793 | ||
| 3011 | while (try nodeParseFn(p)) |item| { | 3794 | while (true) { |
| 3795 | const item = try nodeParseFn(p); | ||
| 3796 | if (item == 0) break; | ||
| 3797 | |||
| 3012 | try list.append(item); | 3798 | try list.append(item); |
| 3013 | 3799 | ||
| 3014 | switch (p.token_ids[p.tok_i]) { | 3800 | switch (p.token_tags[p.tok_i]) { |
| 3015 | .Comma => _ = p.nextToken(), | 3801 | .comma => p.tok_i += 1, |
| 3016 | // all possible delimiters | 3802 | // all possible delimiters |
| 3017 | .Colon, .RParen, .RBrace, .RBracket => break, | 3803 | .colon, .r_paren, .r_brace, .r_bracket => break, |
| 3018 | else => { | 3804 | else => { |
| 3019 | // this is likely just a missing comma, | 3805 | // This is likely just a missing comma; |
| 3020 | // continue parsing this list and give an error | 3806 | // give an error but continue parsing this list. |
| 3021 | try p.errors.append(p.gpa, .{ | 3807 | try p.warnExpected(.comma); |
| 3022 | .ExpectedToken = .{ .token = p.tok_i, .expected_id = .Comma }, | ||
| 3023 | }); | ||
| 3024 | }, | 3808 | }, |
| 3025 | } | 3809 | } |
| 3026 | } | 3810 | } |
| 3027 | return list.toOwnedSlice(); | 3811 | return p.listToSpan(list.items); |
| 3028 | } | ||
| 3029 | }.parse; | ||
| 3030 | } | ||
| 3031 | |||
| 3032 | fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.Tag) NodeParseFn { | ||
| 3033 | return struct { | ||
| 3034 | pub fn parse(p: *Parser) Error!?*Node { | ||
| 3035 | const op_token = if (token == .Keyword_and) switch (p.token_ids[p.tok_i]) { | ||
| 3036 | .Keyword_and => p.nextToken(), | ||
| 3037 | .Invalid_ampersands => blk: { | ||
| 3038 | try p.errors.append(p.gpa, .{ | ||
| 3039 | .InvalidAnd = .{ .token = p.tok_i }, | ||
| 3040 | }); | ||
| 3041 | break :blk p.nextToken(); | ||
| 3042 | }, | ||
| 3043 | else => return null, | ||
| 3044 | } else p.eatToken(token) orelse return null; | ||
| 3045 | |||
| 3046 | const node = try p.arena.allocator.create(Node.SimpleInfixOp); | ||
| 3047 | node.* = .{ | ||
| 3048 | .base = .{ .tag = op }, | ||
| 3049 | .op_token = op_token, | ||
| 3050 | .lhs = undefined, // set by caller | ||
| 3051 | .rhs = undefined, // set by caller | ||
| 3052 | }; | ||
| 3053 | return &node.base; | ||
| 3054 | } | 3812 | } |
| 3055 | }.parse; | 3813 | }.parse; |
| 3056 | } | 3814 | } |
| 3057 | 3815 | ||
| 3058 | // Helper parsers not included in the grammar | 3816 | /// FnCallArguments <- LPAREN ExprList RPAREN |
| 3059 | 3817 | /// ExprList <- (Expr COMMA)* Expr? | |
| 3060 | fn parseBuiltinCall(p: *Parser) !?*Node { | 3818 | fn parseBuiltinCall(p: *Parser) !Node.Index { |
| 3061 | const token = p.eatToken(.Builtin) orelse return null; | 3819 | const builtin_token = p.assertToken(.builtin); |
| 3062 | const params = (try p.parseFnCallArguments()) orelse { | 3820 | if (p.token_tags[p.nextToken()] != .l_paren) { |
| 3063 | try p.errors.append(p.gpa, .{ | 3821 | p.tok_i -= 1; |
| 3064 | .ExpectedParamList = .{ .token = p.tok_i }, | 3822 | try p.warn(.expected_param_list); |
| 3823 | // Pretend this was an identifier so we can continue parsing. | ||
| 3824 | return p.addNode(.{ | ||
| 3825 | .tag = .identifier, | ||
| 3826 | .main_token = builtin_token, | ||
| 3827 | .data = .{ | ||
| 3828 | .lhs = undefined, | ||
| 3829 | .rhs = undefined, | ||
| 3830 | }, | ||
| 3065 | }); | 3831 | }); |
| 3832 | } | ||
| 3833 | if (p.eatToken(.r_paren)) |_| { | ||
| 3834 | return p.addNode(.{ | ||
| 3835 | .tag = .builtin_call_two, | ||
| 3836 | .main_token = builtin_token, | ||
| 3837 | .data = .{ | ||
| 3838 | .lhs = 0, | ||
| 3839 | .rhs = 0, | ||
| 3840 | }, | ||
| 3841 | }); | ||
| 3842 | } | ||
| 3843 | const param_one = try p.expectExpr(); | ||
| 3844 | switch (p.token_tags[p.nextToken()]) { | ||
| 3845 | .comma => { | ||
| 3846 | if (p.eatToken(.r_paren)) |_| { | ||
| 3847 | return p.addNode(.{ | ||
| 3848 | .tag = .builtin_call_two_comma, | ||
| 3849 | .main_token = builtin_token, | ||
| 3850 | .data = .{ | ||
| 3851 | .lhs = param_one, | ||
| 3852 | .rhs = 0, | ||
| 3853 | }, | ||
| 3854 | }); | ||
| 3855 | } | ||
| 3856 | }, | ||
| 3857 | .r_paren => return p.addNode(.{ | ||
| 3858 | .tag = .builtin_call_two, | ||
| 3859 | .main_token = builtin_token, | ||
| 3860 | .data = .{ | ||
| 3861 | .lhs = param_one, | ||
| 3862 | .rhs = 0, | ||
| 3863 | }, | ||
| 3864 | }), | ||
| 3865 | else => { | ||
| 3866 | // This is likely just a missing comma; | ||
| 3867 | // give an error but continue parsing this list. | ||
| 3868 | p.tok_i -= 1; | ||
| 3869 | try p.warnExpected(.comma); | ||
| 3870 | }, | ||
| 3871 | } | ||
| 3872 | const param_two = try p.expectExpr(); | ||
| 3873 | switch (p.token_tags[p.nextToken()]) { | ||
| 3874 | .comma => { | ||
| 3875 | if (p.eatToken(.r_paren)) |_| { | ||
| 3876 | return p.addNode(.{ | ||
| 3877 | .tag = .builtin_call_two_comma, | ||
| 3878 | .main_token = builtin_token, | ||
| 3879 | .data = .{ | ||
| 3880 | .lhs = param_one, | ||
| 3881 | .rhs = param_two, | ||
| 3882 | }, | ||
| 3883 | }); | ||
| 3884 | } | ||
| 3885 | }, | ||
| 3886 | .r_paren => return p.addNode(.{ | ||
| 3887 | .tag = .builtin_call_two, | ||
| 3888 | .main_token = builtin_token, | ||
| 3889 | .data = .{ | ||
| 3890 | .lhs = param_one, | ||
| 3891 | .rhs = param_two, | ||
| 3892 | }, | ||
| 3893 | }), | ||
| 3894 | else => { | ||
| 3895 | // This is likely just a missing comma; | ||
| 3896 | // give an error but continue parsing this list. | ||
| 3897 | p.tok_i -= 1; | ||
| 3898 | try p.warnExpected(.comma); | ||
| 3899 | }, | ||
| 3900 | } | ||
| 3066 | 3901 | ||
| 3067 | // lets pretend this was an identifier so we can continue parsing | 3902 | var list = std.ArrayList(Node.Index).init(p.gpa); |
| 3068 | const node = try p.arena.allocator.create(Node.OneToken); | 3903 | defer list.deinit(); |
| 3069 | node.* = .{ | ||
| 3070 | .base = .{ .tag = .Identifier }, | ||
| 3071 | .token = token, | ||
| 3072 | }; | ||
| 3073 | return &node.base; | ||
| 3074 | }; | ||
| 3075 | defer p.gpa.free(params.list); | ||
| 3076 | |||
| 3077 | const node = try Node.BuiltinCall.alloc(&p.arena.allocator, params.list.len); | ||
| 3078 | node.* = .{ | ||
| 3079 | .builtin_token = token, | ||
| 3080 | .params_len = params.list.len, | ||
| 3081 | .rparen_token = params.rparen, | ||
| 3082 | }; | ||
| 3083 | std.mem.copy(*Node, node.params(), params.list); | ||
| 3084 | return &node.base; | ||
| 3085 | } | ||
| 3086 | |||
| 3087 | fn parseErrorTag(p: *Parser) !?*Node { | ||
| 3088 | const doc_comments = try p.parseDocComment(); // no need to rewind on failure | ||
| 3089 | const token = p.eatToken(.Identifier) orelse return null; | ||
| 3090 | |||
| 3091 | const node = try p.arena.allocator.create(Node.ErrorTag); | ||
| 3092 | node.* = .{ | ||
| 3093 | .doc_comments = doc_comments, | ||
| 3094 | .name_token = token, | ||
| 3095 | }; | ||
| 3096 | return &node.base; | ||
| 3097 | } | ||
| 3098 | |||
| 3099 | fn parseIdentifier(p: *Parser) !?*Node { | ||
| 3100 | const token = p.eatToken(.Identifier) orelse return null; | ||
| 3101 | const node = try p.arena.allocator.create(Node.OneToken); | ||
| 3102 | node.* = .{ | ||
| 3103 | .base = .{ .tag = .Identifier }, | ||
| 3104 | .token = token, | ||
| 3105 | }; | ||
| 3106 | return &node.base; | ||
| 3107 | } | ||
| 3108 | |||
| 3109 | fn parseAnyType(p: *Parser) !?*Node { | ||
| 3110 | const token = p.eatToken(.Keyword_anytype) orelse | ||
| 3111 | p.eatToken(.Keyword_var) orelse return null; // TODO remove in next release cycle | ||
| 3112 | const node = try p.arena.allocator.create(Node.OneToken); | ||
| 3113 | node.* = .{ | ||
| 3114 | .base = .{ .tag = .AnyType }, | ||
| 3115 | .token = token, | ||
| 3116 | }; | ||
| 3117 | return &node.base; | ||
| 3118 | } | ||
| 3119 | 3904 | ||
| 3120 | fn createLiteral(p: *Parser, tag: ast.Node.Tag, token: TokenIndex) !*Node { | 3905 | try list.appendSlice(&.{ param_one, param_two }); |
| 3121 | const result = try p.arena.allocator.create(Node.OneToken); | ||
| 3122 | result.* = .{ | ||
| 3123 | .base = .{ .tag = tag }, | ||
| 3124 | .token = token, | ||
| 3125 | }; | ||
| 3126 | return &result.base; | ||
| 3127 | } | ||
| 3128 | 3906 | ||
| 3129 | fn parseStringLiteralSingle(p: *Parser) !?*Node { | 3907 | while (true) { |
| 3130 | if (p.eatToken(.StringLiteral)) |token| { | 3908 | const param = try p.expectExpr(); |
| 3131 | const node = try p.arena.allocator.create(Node.OneToken); | 3909 | try list.append(param); |
| 3132 | node.* = .{ | 3910 | switch (p.token_tags[p.nextToken()]) { |
| 3133 | .base = .{ .tag = .StringLiteral }, | 3911 | .comma => { |
| 3134 | .token = token, | 3912 | if (p.eatToken(.r_paren)) |_| { |
| 3135 | }; | 3913 | const params = try p.listToSpan(list.items); |
| 3136 | return &node.base; | 3914 | return p.addNode(.{ |
| 3915 | .tag = .builtin_call_comma, | ||
| 3916 | .main_token = builtin_token, | ||
| 3917 | .data = .{ | ||
| 3918 | .lhs = params.start, | ||
| 3919 | .rhs = params.end, | ||
| 3920 | }, | ||
| 3921 | }); | ||
| 3922 | } | ||
| 3923 | continue; | ||
| 3924 | }, | ||
| 3925 | .r_paren => { | ||
| 3926 | const params = try p.listToSpan(list.items); | ||
| 3927 | return p.addNode(.{ | ||
| 3928 | .tag = .builtin_call, | ||
| 3929 | .main_token = builtin_token, | ||
| 3930 | .data = .{ | ||
| 3931 | .lhs = params.start, | ||
| 3932 | .rhs = params.end, | ||
| 3933 | }, | ||
| 3934 | }); | ||
| 3935 | }, | ||
| 3936 | else => { | ||
| 3937 | // This is likely just a missing comma; | ||
| 3938 | // give an error but continue parsing this list. | ||
| 3939 | p.tok_i -= 1; | ||
| 3940 | try p.warnExpected(.comma); | ||
| 3941 | }, | ||
| 3942 | } | ||
| 3137 | } | 3943 | } |
| 3138 | return null; | ||
| 3139 | } | 3944 | } |
| 3140 | 3945 | ||
| 3141 | // string literal or multiline string literal | 3946 | // string literal or multiline string literal |
| 3142 | fn parseStringLiteral(p: *Parser) !?*Node { | 3947 | fn parseStringLiteral(p: *Parser) !Node.Index { |
| 3143 | if (try p.parseStringLiteralSingle()) |node| return node; | 3948 | switch (p.token_tags[p.tok_i]) { |
| 3144 | 3949 | .string_literal => { | |
| 3145 | if (p.eatToken(.MultilineStringLiteralLine)) |first_line| { | 3950 | const main_token = p.nextToken(); |
| 3146 | const start_tok_i = p.tok_i; | 3951 | return p.addNode(.{ |
| 3147 | var tok_i = start_tok_i; | 3952 | .tag = .string_literal, |
| 3148 | var count: usize = 1; // including first_line | 3953 | .main_token = main_token, |
| 3149 | while (true) : (tok_i += 1) { | 3954 | .data = .{ |
| 3150 | switch (p.token_ids[tok_i]) { | 3955 | .lhs = undefined, |
| 3151 | .LineComment => continue, | 3956 | .rhs = undefined, |
| 3152 | .MultilineStringLiteralLine => count += 1, | ||
| 3153 | else => break, | ||
| 3154 | } | ||
| 3155 | } | ||
| 3156 | |||
| 3157 | const node = try Node.MultilineStringLiteral.alloc(&p.arena.allocator, count); | ||
| 3158 | node.* = .{ .lines_len = count }; | ||
| 3159 | const lines = node.lines(); | ||
| 3160 | tok_i = start_tok_i; | ||
| 3161 | lines[0] = first_line; | ||
| 3162 | count = 1; | ||
| 3163 | while (true) : (tok_i += 1) { | ||
| 3164 | switch (p.token_ids[tok_i]) { | ||
| 3165 | .LineComment => continue, | ||
| 3166 | .MultilineStringLiteralLine => { | ||
| 3167 | lines[count] = tok_i; | ||
| 3168 | count += 1; | ||
| 3169 | }, | 3957 | }, |
| 3170 | else => break, | 3958 | }); |
| 3959 | }, | ||
| 3960 | .multiline_string_literal_line => { | ||
| 3961 | const first_line = p.nextToken(); | ||
| 3962 | while (p.token_tags[p.tok_i] == .multiline_string_literal_line) { | ||
| 3963 | p.tok_i += 1; | ||
| 3171 | } | 3964 | } |
| 3172 | } | 3965 | return p.addNode(.{ |
| 3173 | p.tok_i = tok_i; | 3966 | .tag = .multiline_string_literal, |
| 3174 | return &node.base; | 3967 | .main_token = first_line, |
| 3968 | .data = .{ | ||
| 3969 | .lhs = first_line, | ||
| 3970 | .rhs = p.tok_i - 1, | ||
| 3971 | }, | ||
| 3972 | }); | ||
| 3973 | }, | ||
| 3974 | else => return null_node, | ||
| 3175 | } | 3975 | } |
| 3176 | |||
| 3177 | return null; | ||
| 3178 | } | 3976 | } |
| 3179 | 3977 | ||
| 3180 | fn parseIntegerLiteral(p: *Parser) !?*Node { | 3978 | fn expectStringLiteral(p: *Parser) !Node.Index { |
| 3181 | const token = p.eatToken(.IntegerLiteral) orelse return null; | 3979 | const node = try p.parseStringLiteral(); |
| 3182 | const node = try p.arena.allocator.create(Node.OneToken); | 3980 | if (node == 0) { |
| 3183 | node.* = .{ | 3981 | return p.fail(.expected_string_literal); |
| 3184 | .base = .{ .tag = .IntegerLiteral }, | 3982 | } |
| 3185 | .token = token, | 3983 | return node; |
| 3186 | }; | ||
| 3187 | return &node.base; | ||
| 3188 | } | 3984 | } |
| 3189 | 3985 | ||
| 3190 | fn parseFloatLiteral(p: *Parser) !?*Node { | 3986 | fn expectIntegerLiteral(p: *Parser) !Node.Index { |
| 3191 | const token = p.eatToken(.FloatLiteral) orelse return null; | 3987 | return p.addNode(.{ |
| 3192 | const node = try p.arena.allocator.create(Node.OneToken); | 3988 | .tag = .integer_literal, |
| 3193 | node.* = .{ | 3989 | .main_token = try p.expectToken(.integer_literal), |
| 3194 | .base = .{ .tag = .FloatLiteral }, | 3990 | .data = .{ |
| 3195 | .token = token, | 3991 | .lhs = undefined, |
| 3196 | }; | 3992 | .rhs = undefined, |
| 3197 | return &node.base; | 3993 | }, |
| 3994 | }); | ||
| 3198 | } | 3995 | } |
| 3199 | 3996 | ||
| 3200 | fn parseTry(p: *Parser) !?*Node { | 3997 | /// KEYWORD_if LPAREN Expr RPAREN PtrPayload? Body (KEYWORD_else Payload? Body)? |
| 3201 | const token = p.eatToken(.Keyword_try) orelse return null; | 3998 | fn parseIf(p: *Parser, bodyParseFn: NodeParseFn) !Node.Index { |
| 3202 | const node = try p.arena.allocator.create(Node.SimplePrefixOp); | 3999 | const if_token = p.eatToken(.keyword_if) orelse return null_node; |
| 3203 | node.* = .{ | 4000 | _ = try p.expectToken(.l_paren); |
| 3204 | .base = .{ .tag = .Try }, | 4001 | const condition = try p.expectExpr(); |
| 3205 | .op_token = token, | 4002 | _ = try p.expectToken(.r_paren); |
| 3206 | .rhs = undefined, // set by caller | 4003 | const then_payload = try p.parsePtrPayload(); |
| 3207 | }; | ||
| 3208 | return &node.base; | ||
| 3209 | } | ||
| 3210 | 4004 | ||
| 3211 | /// IfPrefix Body (KEYWORD_else Payload? Body)? | 4005 | const then_expr = try bodyParseFn(p); |
| 3212 | fn parseIf(p: *Parser, bodyParseFn: NodeParseFn) !?*Node { | 4006 | if (then_expr == 0) return p.fail(.invalid_token); |
| 3213 | const node = (try p.parseIfPrefix()) orelse return null; | ||
| 3214 | const if_prefix = node.cast(Node.If).?; | ||
| 3215 | 4007 | ||
| 3216 | if_prefix.body = try p.expectNode(bodyParseFn, .{ | 4008 | const else_token = p.eatToken(.keyword_else) orelse return p.addNode(.{ |
| 3217 | .InvalidToken = .{ .token = p.tok_i }, | 4009 | .tag = .if_simple, |
| 4010 | .main_token = if_token, | ||
| 4011 | .data = .{ | ||
| 4012 | .lhs = condition, | ||
| 4013 | .rhs = then_expr, | ||
| 4014 | }, | ||
| 3218 | }); | 4015 | }); |
| 3219 | 4016 | const else_payload = try p.parsePayload(); | |
| 3220 | const else_token = p.eatToken(.Keyword_else) orelse return node; | 4017 | const else_expr = try bodyParseFn(p); |
| 3221 | const payload = try p.parsePayload(); | 4018 | if (else_expr == 0) return p.fail(.invalid_token); |
| 3222 | const else_expr = try p.expectNode(bodyParseFn, .{ | 4019 | |
| 3223 | .InvalidToken = .{ .token = p.tok_i }, | 4020 | return p.addNode(.{ |
| 4021 | .tag = .@"if", | ||
| 4022 | .main_token = if_token, | ||
| 4023 | .data = .{ | ||
| 4024 | .lhs = condition, | ||
| 4025 | .rhs = try p.addExtra(Node.If{ | ||
| 4026 | .then_expr = then_expr, | ||
| 4027 | .else_expr = else_expr, | ||
| 4028 | }), | ||
| 4029 | }, | ||
| 3224 | }); | 4030 | }); |
| 3225 | const else_node = try p.arena.allocator.create(Node.Else); | ||
| 3226 | else_node.* = .{ | ||
| 3227 | .else_token = else_token, | ||
| 3228 | .payload = payload, | ||
| 3229 | .body = else_expr, | ||
| 3230 | }; | ||
| 3231 | if_prefix.@"else" = else_node; | ||
| 3232 | |||
| 3233 | return node; | ||
| 3234 | } | 4031 | } |
| 3235 | 4032 | ||
| 3236 | /// Eat a multiline doc comment | 4033 | /// Skips over doc comment tokens. Returns the first one, if any. |
| 3237 | fn parseDocComment(p: *Parser) !?*Node.DocComment { | 4034 | fn eatDocComments(p: *Parser) !?TokenIndex { |
| 3238 | if (p.eatToken(.DocComment)) |first_line| { | 4035 | if (p.eatToken(.doc_comment)) |tok| { |
| 3239 | while (p.eatToken(.DocComment)) |_| {} | 4036 | var first_line = tok; |
| 3240 | const node = try p.arena.allocator.create(Node.DocComment); | 4037 | if (tok > 0 and tokensOnSameLine(p, tok - 1, tok)) { |
| 3241 | node.* = .{ .first_line = first_line }; | 4038 | try p.warnMsg(.{ |
| 3242 | return node; | 4039 | .tag = .same_line_doc_comment, |
| 4040 | .token = tok, | ||
| 4041 | }); | ||
| 4042 | first_line = p.eatToken(.doc_comment) orelse return null; | ||
| 4043 | } | ||
| 4044 | while (p.eatToken(.doc_comment)) |_| {} | ||
| 4045 | return first_line; | ||
| 3243 | } | 4046 | } |
| 3244 | return null; | 4047 | return null; |
| 3245 | } | 4048 | } |
| 3246 | 4049 | ||
| 3247 | fn tokensOnSameLine(p: *Parser, token1: TokenIndex, token2: TokenIndex) bool { | 4050 | fn tokensOnSameLine(p: *Parser, token1: TokenIndex, token2: TokenIndex) bool { |
| 3248 | return std.mem.indexOfScalar(u8, p.source[p.token_locs[token1].end..p.token_locs[token2].start], '\n') == null; | 4051 | return std.mem.indexOfScalar(u8, p.source[p.token_starts[token1]..p.token_starts[token2]], '\n') == null; |
| 3249 | } | 4052 | } |
| 3250 | 4053 | ||
| 3251 | /// Eat a single-line doc comment on the same line as another node | 4054 | fn eatToken(p: *Parser, tag: Token.Tag) ?TokenIndex { |
| 3252 | fn parseAppendedDocComment(p: *Parser, after_token: TokenIndex) !?*Node.DocComment { | 4055 | return if (p.token_tags[p.tok_i] == tag) p.nextToken() else null; |
| 3253 | const comment_token = p.eatToken(.DocComment) orelse return null; | ||
| 3254 | if (p.tokensOnSameLine(after_token, comment_token)) { | ||
| 3255 | const node = try p.arena.allocator.create(Node.DocComment); | ||
| 3256 | node.* = .{ .first_line = comment_token }; | ||
| 3257 | return node; | ||
| 3258 | } | ||
| 3259 | p.putBackToken(comment_token); | ||
| 3260 | return null; | ||
| 3261 | } | 4056 | } |
| 3262 | 4057 | ||
| 3263 | /// Op* Child | 4058 | fn assertToken(p: *Parser, tag: Token.Tag) TokenIndex { |
| 3264 | fn parsePrefixOpExpr(p: *Parser, comptime opParseFn: NodeParseFn, comptime childParseFn: NodeParseFn) Error!?*Node { | 4059 | const token = p.nextToken(); |
| 3265 | if (try opParseFn(p)) |first_op| { | 4060 | assert(p.token_tags[token] == tag); |
| 3266 | var rightmost_op = first_op; | 4061 | return token; |
| 3267 | while (true) { | ||
| 3268 | switch (rightmost_op.tag) { | ||
| 3269 | .AddressOf, | ||
| 3270 | .Await, | ||
| 3271 | .BitNot, | ||
| 3272 | .BoolNot, | ||
| 3273 | .OptionalType, | ||
| 3274 | .Negation, | ||
| 3275 | .NegationWrap, | ||
| 3276 | .Resume, | ||
| 3277 | .Try, | ||
| 3278 | => { | ||
| 3279 | if (try opParseFn(p)) |rhs| { | ||
| 3280 | rightmost_op.cast(Node.SimplePrefixOp).?.rhs = rhs; | ||
| 3281 | rightmost_op = rhs; | ||
| 3282 | } else break; | ||
| 3283 | }, | ||
| 3284 | .ArrayType => { | ||
| 3285 | if (try opParseFn(p)) |rhs| { | ||
| 3286 | rightmost_op.cast(Node.ArrayType).?.rhs = rhs; | ||
| 3287 | rightmost_op = rhs; | ||
| 3288 | } else break; | ||
| 3289 | }, | ||
| 3290 | .ArrayTypeSentinel => { | ||
| 3291 | if (try opParseFn(p)) |rhs| { | ||
| 3292 | rightmost_op.cast(Node.ArrayTypeSentinel).?.rhs = rhs; | ||
| 3293 | rightmost_op = rhs; | ||
| 3294 | } else break; | ||
| 3295 | }, | ||
| 3296 | .SliceType => { | ||
| 3297 | if (try opParseFn(p)) |rhs| { | ||
| 3298 | rightmost_op.cast(Node.SliceType).?.rhs = rhs; | ||
| 3299 | rightmost_op = rhs; | ||
| 3300 | } else break; | ||
| 3301 | }, | ||
| 3302 | .PtrType => { | ||
| 3303 | var ptr_type = rightmost_op.cast(Node.PtrType).?; | ||
| 3304 | // If the token encountered was **, there will be two nodes | ||
| 3305 | if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk) { | ||
| 3306 | rightmost_op = ptr_type.rhs; | ||
| 3307 | ptr_type = rightmost_op.cast(Node.PtrType).?; | ||
| 3308 | } | ||
| 3309 | if (try opParseFn(p)) |rhs| { | ||
| 3310 | ptr_type.rhs = rhs; | ||
| 3311 | rightmost_op = rhs; | ||
| 3312 | } else break; | ||
| 3313 | }, | ||
| 3314 | .AnyFrameType => { | ||
| 3315 | const prom = rightmost_op.cast(Node.AnyFrameType).?; | ||
| 3316 | if (try opParseFn(p)) |rhs| { | ||
| 3317 | prom.result.?.return_type = rhs; | ||
| 3318 | rightmost_op = rhs; | ||
| 3319 | } else break; | ||
| 3320 | }, | ||
| 3321 | else => unreachable, | ||
| 3322 | } | ||
| 3323 | } | ||
| 3324 | |||
| 3325 | // If any prefix op existed, a child node on the RHS is required | ||
| 3326 | switch (rightmost_op.tag) { | ||
| 3327 | .AddressOf, | ||
| 3328 | .Await, | ||
| 3329 | .BitNot, | ||
| 3330 | .BoolNot, | ||
| 3331 | .OptionalType, | ||
| 3332 | .Negation, | ||
| 3333 | .NegationWrap, | ||
| 3334 | .Resume, | ||
| 3335 | .Try, | ||
| 3336 | => { | ||
| 3337 | const prefix_op = rightmost_op.cast(Node.SimplePrefixOp).?; | ||
| 3338 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | ||
| 3339 | .InvalidToken = .{ .token = p.tok_i }, | ||
| 3340 | }); | ||
| 3341 | }, | ||
| 3342 | .ArrayType => { | ||
| 3343 | const prefix_op = rightmost_op.cast(Node.ArrayType).?; | ||
| 3344 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | ||
| 3345 | .InvalidToken = .{ .token = p.tok_i }, | ||
| 3346 | }); | ||
| 3347 | }, | ||
| 3348 | .ArrayTypeSentinel => { | ||
| 3349 | const prefix_op = rightmost_op.cast(Node.ArrayTypeSentinel).?; | ||
| 3350 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | ||
| 3351 | .InvalidToken = .{ .token = p.tok_i }, | ||
| 3352 | }); | ||
| 3353 | }, | ||
| 3354 | .PtrType => { | ||
| 3355 | const prefix_op = rightmost_op.cast(Node.PtrType).?; | ||
| 3356 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | ||
| 3357 | .InvalidToken = .{ .token = p.tok_i }, | ||
| 3358 | }); | ||
| 3359 | }, | ||
| 3360 | .SliceType => { | ||
| 3361 | const prefix_op = rightmost_op.cast(Node.SliceType).?; | ||
| 3362 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | ||
| 3363 | .InvalidToken = .{ .token = p.tok_i }, | ||
| 3364 | }); | ||
| 3365 | }, | ||
| 3366 | .AnyFrameType => { | ||
| 3367 | const prom = rightmost_op.cast(Node.AnyFrameType).?; | ||
| 3368 | prom.result.?.return_type = try p.expectNode(childParseFn, .{ | ||
| 3369 | .InvalidToken = .{ .token = p.tok_i }, | ||
| 3370 | }); | ||
| 3371 | }, | ||
| 3372 | else => unreachable, | ||
| 3373 | } | ||
| 3374 | |||
| 3375 | return first_op; | ||
| 3376 | } | ||
| 3377 | |||
| 3378 | // Otherwise, the child node is optional | ||
| 3379 | return childParseFn(p); | ||
| 3380 | } | 4062 | } |
| 3381 | 4063 | ||
| 3382 | /// Child (Op Child)* | 4064 | fn expectToken(p: *Parser, tag: Token.Tag) Error!TokenIndex { |
| 3383 | /// Child (Op Child)? | 4065 | const token = p.nextToken(); |
| 3384 | fn parseBinOpExpr( | 4066 | if (p.token_tags[token] != tag) { |
| 3385 | p: *Parser, | 4067 | p.tok_i -= 1; // Go back so that we can recover properly. |
| 3386 | opParseFn: NodeParseFn, | 4068 | return p.failMsg(.{ |
| 3387 | childParseFn: NodeParseFn, | 4069 | .tag = .expected_token, |
| 3388 | chain: enum { | 4070 | .token = token, |
| 3389 | Once, | 4071 | .extra = .{ .expected_tag = tag }, |
| 3390 | Infinitely, | ||
| 3391 | }, | ||
| 3392 | ) Error!?*Node { | ||
| 3393 | var res = (try childParseFn(p)) orelse return null; | ||
| 3394 | |||
| 3395 | while (try opParseFn(p)) |node| { | ||
| 3396 | const right = try p.expectNode(childParseFn, .{ | ||
| 3397 | .InvalidToken = .{ .token = p.tok_i }, | ||
| 3398 | }); | 4072 | }); |
| 3399 | const left = res; | ||
| 3400 | res = node; | ||
| 3401 | |||
| 3402 | if (node.castTag(.Catch)) |op| { | ||
| 3403 | op.lhs = left; | ||
| 3404 | op.rhs = right; | ||
| 3405 | } else if (node.cast(Node.SimpleInfixOp)) |op| { | ||
| 3406 | op.lhs = left; | ||
| 3407 | op.rhs = right; | ||
| 3408 | } | ||
| 3409 | |||
| 3410 | switch (chain) { | ||
| 3411 | .Once => break, | ||
| 3412 | .Infinitely => continue, | ||
| 3413 | } | ||
| 3414 | } | 4073 | } |
| 3415 | 4074 | return token; | |
| 3416 | return res; | ||
| 3417 | } | ||
| 3418 | |||
| 3419 | fn createInfixOp(p: *Parser, op_token: TokenIndex, tag: Node.Tag) !*Node { | ||
| 3420 | const node = try p.arena.allocator.create(Node.SimpleInfixOp); | ||
| 3421 | node.* = .{ | ||
| 3422 | .base = Node{ .tag = tag }, | ||
| 3423 | .op_token = op_token, | ||
| 3424 | .lhs = undefined, // set by caller | ||
| 3425 | .rhs = undefined, // set by caller | ||
| 3426 | }; | ||
| 3427 | return &node.base; | ||
| 3428 | } | ||
| 3429 | |||
| 3430 | fn eatToken(p: *Parser, id: Token.Id) ?TokenIndex { | ||
| 3431 | return if (p.token_ids[p.tok_i] == id) p.nextToken() else null; | ||
| 3432 | } | ||
| 3433 | |||
| 3434 | fn expectToken(p: *Parser, id: Token.Id) Error!TokenIndex { | ||
| 3435 | return (try p.expectTokenRecoverable(id)) orelse error.ParseError; | ||
| 3436 | } | 4075 | } |
| 3437 | 4076 | ||
| 3438 | fn expectTokenRecoverable(p: *Parser, id: Token.Id) !?TokenIndex { | 4077 | fn expectTokenRecoverable(p: *Parser, tag: Token.Tag) !?TokenIndex { |
| 3439 | const token = p.nextToken(); | 4078 | if (p.token_tags[p.tok_i] != tag) { |
| 3440 | if (p.token_ids[token] != id) { | 4079 | try p.warnExpected(tag); |
| 3441 | try p.errors.append(p.gpa, .{ | ||
| 3442 | .ExpectedToken = .{ .token = token, .expected_id = id }, | ||
| 3443 | }); | ||
| 3444 | // go back so that we can recover properly | ||
| 3445 | p.putBackToken(token); | ||
| 3446 | return null; | 4080 | return null; |
| 4081 | } else { | ||
| 4082 | return p.nextToken(); | ||
| 3447 | } | 4083 | } |
| 3448 | return token; | ||
| 3449 | } | 4084 | } |
| 3450 | 4085 | ||
| 3451 | fn nextToken(p: *Parser) TokenIndex { | 4086 | fn nextToken(p: *Parser) TokenIndex { |
| 3452 | const result = p.tok_i; | 4087 | const result = p.tok_i; |
| 3453 | p.tok_i += 1; | 4088 | p.tok_i += 1; |
| 3454 | assert(p.token_ids[result] != .LineComment); | 4089 | return result; |
| 3455 | if (p.tok_i >= p.token_ids.len) return result; | ||
| 3456 | |||
| 3457 | while (true) { | ||
| 3458 | if (p.token_ids[p.tok_i] != .LineComment) return result; | ||
| 3459 | p.tok_i += 1; | ||
| 3460 | } | ||
| 3461 | } | ||
| 3462 | |||
| 3463 | fn putBackToken(p: *Parser, putting_back: TokenIndex) void { | ||
| 3464 | while (p.tok_i > 0) { | ||
| 3465 | p.tok_i -= 1; | ||
| 3466 | if (p.token_ids[p.tok_i] == .LineComment) continue; | ||
| 3467 | assert(putting_back == p.tok_i); | ||
| 3468 | return; | ||
| 3469 | } | ||
| 3470 | } | ||
| 3471 | |||
| 3472 | /// TODO Delete this function. I don't like the inversion of control. | ||
| 3473 | fn expectNode( | ||
| 3474 | p: *Parser, | ||
| 3475 | parseFn: NodeParseFn, | ||
| 3476 | /// if parsing fails | ||
| 3477 | err: AstError, | ||
| 3478 | ) Error!*Node { | ||
| 3479 | return (try p.expectNodeRecoverable(parseFn, err)) orelse return error.ParseError; | ||
| 3480 | } | ||
| 3481 | |||
| 3482 | /// TODO Delete this function. I don't like the inversion of control. | ||
| 3483 | fn expectNodeRecoverable( | ||
| 3484 | p: *Parser, | ||
| 3485 | parseFn: NodeParseFn, | ||
| 3486 | /// if parsing fails | ||
| 3487 | err: AstError, | ||
| 3488 | ) !?*Node { | ||
| 3489 | return (try parseFn(p)) orelse { | ||
| 3490 | try p.errors.append(p.gpa, err); | ||
| 3491 | return null; | ||
| 3492 | }; | ||
| 3493 | } | 4090 | } |
| 3494 | }; | 4091 | }; |
| 3495 | 4092 | ||
| 3496 | fn ParseFn(comptime T: type) type { | 4093 | test { |
| 3497 | return fn (p: *Parser) Error!T; | ||
| 3498 | } | ||
| 3499 | |||
| 3500 | test "std.zig.parser" { | ||
| 3501 | _ = @import("parser_test.zig"); | 4094 | _ = @import("parser_test.zig"); |
| 3502 | } | 4095 | } |
lib/std/zig/parser_test.zig+1753-949| ... | @@ -3,275 +3,34 @@ | ... | @@ -3,275 +3,34 @@ |
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. |
| 4 | // The MIT license requires this copyright notice to be included in all copies | 4 | // The MIT license requires this copyright notice to be included in all copies |
| 5 | // and substantial portions of the software. | 5 | // and substantial portions of the software. |
| 6 | test "zig fmt: convert var to anytype" { | 6 | |
| 7 | // TODO remove in next release cycle | 7 | // TODO Remove this after zig 0.9.0 is released. |
| 8 | test "zig fmt: rewrite inline functions as callconv(.Inline)" { | ||
| 8 | try testTransform( | 9 | try testTransform( |
| 9 | \\pub fn main( | 10 | \\inline fn foo() void {} |
| 10 | \\ a: var, | 11 | \\ |
| 11 | \\ bar: var, | ||
| 12 | \\) void {} | ||
| 13 | , | 12 | , |
| 14 | \\pub fn main( | 13 | \\fn foo() callconv(.Inline) void {} |
| 15 | \\ a: anytype, | ||
| 16 | \\ bar: anytype, | ||
| 17 | \\) void {} | ||
| 18 | \\ | 14 | \\ |
| 19 | ); | 15 | ); |
| 20 | } | 16 | } |
| 21 | 17 | ||
| 22 | test "zig fmt: noasync to nosuspend" { | 18 | test "zig fmt: simple top level comptime block" { |
| 23 | // TODO: remove this | 19 | try testCanonical( |
| 24 | try testTransform( | 20 | \\// line comment |
| 25 | \\pub fn main() void { | 21 | \\comptime {} |
| 26 | \\ noasync call(); | ||
| 27 | \\} | ||
| 28 | , | ||
| 29 | \\pub fn main() void { | ||
| 30 | \\ nosuspend call(); | ||
| 31 | \\} | ||
| 32 | \\ | 22 | \\ |
| 33 | ); | 23 | ); |
| 34 | } | 24 | } |
| 35 | 25 | ||
| 36 | test "recovery: top level" { | 26 | test "zig fmt: two spaced line comments before decl" { |
| 37 | try testError( | 27 | try testCanonical( |
| 38 | \\test "" {inline} | 28 | \\// line comment |
| 39 | \\test "" {inline} | 29 | \\ |
| 40 | , &[_]Error{ | 30 | \\// another |
| 41 | .ExpectedInlinable, | 31 | \\comptime {} |
| 42 | .ExpectedInlinable, | 32 | \\ |
| 43 | }); | 33 | ); |
| 44 | } | ||
| 45 | |||
| 46 | test "recovery: block statements" { | ||
| 47 | try testError( | ||
| 48 | \\test "" { | ||
| 49 | \\ foo + +; | ||
| 50 | \\ inline; | ||
| 51 | \\} | ||
| 52 | , &[_]Error{ | ||
| 53 | .InvalidToken, | ||
| 54 | .ExpectedInlinable, | ||
| 55 | }); | ||
| 56 | } | ||
| 57 | |||
| 58 | test "recovery: missing comma" { | ||
| 59 | try testError( | ||
| 60 | \\test "" { | ||
| 61 | \\ switch (foo) { | ||
| 62 | \\ 2 => {} | ||
| 63 | \\ 3 => {} | ||
| 64 | \\ else => { | ||
| 65 | \\ foo && bar +; | ||
| 66 | \\ } | ||
| 67 | \\ } | ||
| 68 | \\} | ||
| 69 | , &[_]Error{ | ||
| 70 | .ExpectedToken, | ||
| 71 | .ExpectedToken, | ||
| 72 | .InvalidAnd, | ||
| 73 | .InvalidToken, | ||
| 74 | }); | ||
| 75 | } | ||
| 76 | |||
| 77 | test "recovery: extra qualifier" { | ||
| 78 | try testError( | ||
| 79 | \\const a: *const const u8; | ||
| 80 | \\test "" | ||
| 81 | , &[_]Error{ | ||
| 82 | .ExtraConstQualifier, | ||
| 83 | .ExpectedLBrace, | ||
| 84 | }); | ||
| 85 | } | ||
| 86 | |||
| 87 | test "recovery: missing return type" { | ||
| 88 | try testError( | ||
| 89 | \\fn foo() { | ||
| 90 | \\ a && b; | ||
| 91 | \\} | ||
| 92 | \\test "" | ||
| 93 | , &[_]Error{ | ||
| 94 | .ExpectedReturnType, | ||
| 95 | .InvalidAnd, | ||
| 96 | .ExpectedLBrace, | ||
| 97 | }); | ||
| 98 | } | ||
| 99 | |||
| 100 | test "recovery: continue after invalid decl" { | ||
| 101 | try testError( | ||
| 102 | \\fn foo { | ||
| 103 | \\ inline; | ||
| 104 | \\} | ||
| 105 | \\pub test "" { | ||
| 106 | \\ async a && b; | ||
| 107 | \\} | ||
| 108 | , &[_]Error{ | ||
| 109 | .ExpectedToken, | ||
| 110 | .ExpectedPubItem, | ||
| 111 | .ExpectedParamList, | ||
| 112 | .InvalidAnd, | ||
| 113 | }); | ||
| 114 | try testError( | ||
| 115 | \\threadlocal test "" { | ||
| 116 | \\ @a && b; | ||
| 117 | \\} | ||
| 118 | , &[_]Error{ | ||
| 119 | .ExpectedVarDecl, | ||
| 120 | .ExpectedParamList, | ||
| 121 | .InvalidAnd, | ||
| 122 | }); | ||
| 123 | } | ||
| 124 | |||
| 125 | test "recovery: invalid extern/inline" { | ||
| 126 | try testError( | ||
| 127 | \\inline test "" { a && b; } | ||
| 128 | , &[_]Error{ | ||
| 129 | .ExpectedFn, | ||
| 130 | .InvalidAnd, | ||
| 131 | }); | ||
| 132 | try testError( | ||
| 133 | \\extern "" test "" { a && b; } | ||
| 134 | , &[_]Error{ | ||
| 135 | .ExpectedVarDeclOrFn, | ||
| 136 | .InvalidAnd, | ||
| 137 | }); | ||
| 138 | } | ||
| 139 | |||
| 140 | test "recovery: missing semicolon" { | ||
| 141 | try testError( | ||
| 142 | \\test "" { | ||
| 143 | \\ comptime a && b | ||
| 144 | \\ c && d | ||
| 145 | \\ @foo | ||
| 146 | \\} | ||
| 147 | , &[_]Error{ | ||
| 148 | .InvalidAnd, | ||
| 149 | .ExpectedToken, | ||
| 150 | .InvalidAnd, | ||
| 151 | .ExpectedToken, | ||
| 152 | .ExpectedParamList, | ||
| 153 | .ExpectedToken, | ||
| 154 | }); | ||
| 155 | } | ||
| 156 | |||
| 157 | test "recovery: invalid container members" { | ||
| 158 | try testError( | ||
| 159 | \\usingnamespace; | ||
| 160 | \\foo+ | ||
| 161 | \\bar@, | ||
| 162 | \\while (a == 2) { test "" {}} | ||
| 163 | \\test "" { | ||
| 164 | \\ a && b | ||
| 165 | \\} | ||
| 166 | , &[_]Error{ | ||
| 167 | .ExpectedExpr, | ||
| 168 | .ExpectedToken, | ||
| 169 | .ExpectedToken, | ||
| 170 | .ExpectedContainerMembers, | ||
| 171 | .InvalidAnd, | ||
| 172 | .ExpectedToken, | ||
| 173 | }); | ||
| 174 | } | ||
| 175 | |||
| 176 | test "recovery: invalid parameter" { | ||
| 177 | try testError( | ||
| 178 | \\fn main() void { | ||
| 179 | \\ a(comptime T: type) | ||
| 180 | \\} | ||
| 181 | , &[_]Error{ | ||
| 182 | .ExpectedToken, | ||
| 183 | }); | ||
| 184 | } | ||
| 185 | |||
| 186 | test "recovery: extra '}' at top level" { | ||
| 187 | try testError( | ||
| 188 | \\}}} | ||
| 189 | \\test "" { | ||
| 190 | \\ a && b; | ||
| 191 | \\} | ||
| 192 | , &[_]Error{ | ||
| 193 | .ExpectedContainerMembers, | ||
| 194 | .ExpectedContainerMembers, | ||
| 195 | .ExpectedContainerMembers, | ||
| 196 | .InvalidAnd, | ||
| 197 | }); | ||
| 198 | } | ||
| 199 | |||
| 200 | test "recovery: mismatched bracket at top level" { | ||
| 201 | try testError( | ||
| 202 | \\const S = struct { | ||
| 203 | \\ arr: 128]?G | ||
| 204 | \\}; | ||
| 205 | , &[_]Error{ | ||
| 206 | .ExpectedToken, | ||
| 207 | }); | ||
| 208 | } | ||
| 209 | |||
| 210 | test "recovery: invalid global error set access" { | ||
| 211 | try testError( | ||
| 212 | \\test "" { | ||
| 213 | \\ error && foo; | ||
| 214 | \\} | ||
| 215 | , &[_]Error{ | ||
| 216 | .ExpectedToken, | ||
| 217 | .ExpectedIdentifier, | ||
| 218 | .InvalidAnd, | ||
| 219 | }); | ||
| 220 | } | ||
| 221 | |||
| 222 | test "recovery: invalid asterisk after pointer dereference" { | ||
| 223 | try testError( | ||
| 224 | \\test "" { | ||
| 225 | \\ var sequence = "repeat".*** 10; | ||
| 226 | \\} | ||
| 227 | , &[_]Error{ | ||
| 228 | .AsteriskAfterPointerDereference, | ||
| 229 | }); | ||
| 230 | try testError( | ||
| 231 | \\test "" { | ||
| 232 | \\ var sequence = "repeat".** 10&&a; | ||
| 233 | \\} | ||
| 234 | , &[_]Error{ | ||
| 235 | .AsteriskAfterPointerDereference, | ||
| 236 | .InvalidAnd, | ||
| 237 | }); | ||
| 238 | } | ||
| 239 | |||
| 240 | test "recovery: missing semicolon after if, for, while stmt" { | ||
| 241 | try testError( | ||
| 242 | \\test "" { | ||
| 243 | \\ if (foo) bar | ||
| 244 | \\ for (foo) |a| bar | ||
| 245 | \\ while (foo) bar | ||
| 246 | \\ a && b; | ||
| 247 | \\} | ||
| 248 | , &[_]Error{ | ||
| 249 | .ExpectedSemiOrElse, | ||
| 250 | .ExpectedSemiOrElse, | ||
| 251 | .ExpectedSemiOrElse, | ||
| 252 | .InvalidAnd, | ||
| 253 | }); | ||
| 254 | } | ||
| 255 | |||
| 256 | test "recovery: invalid comptime" { | ||
| 257 | try testError( | ||
| 258 | \\comptime | ||
| 259 | , &[_]Error{ | ||
| 260 | .ExpectedBlockOrField, | ||
| 261 | }); | ||
| 262 | } | ||
| 263 | |||
| 264 | test "recovery: missing block after for/while loops" { | ||
| 265 | try testError( | ||
| 266 | \\test "" { while (foo) } | ||
| 267 | , &[_]Error{ | ||
| 268 | .ExpectedBlockOrAssignment, | ||
| 269 | }); | ||
| 270 | try testError( | ||
| 271 | \\test "" { for (foo) |bar| } | ||
| 272 | , &[_]Error{ | ||
| 273 | .ExpectedBlockOrAssignment, | ||
| 274 | }); | ||
| 275 | } | 34 | } |
| 276 | 35 | ||
| 277 | test "zig fmt: respect line breaks after var declarations" { | 36 | test "zig fmt: respect line breaks after var declarations" { |
| ... | @@ -325,6 +84,35 @@ test "zig fmt: empty file" { | ... | @@ -325,6 +84,35 @@ test "zig fmt: empty file" { |
| 325 | ); | 84 | ); |
| 326 | } | 85 | } |
| 327 | 86 | ||
| 87 | test "zig fmt: file ends in comment" { | ||
| 88 | try testTransform( | ||
| 89 | \\ //foobar | ||
| 90 | , | ||
| 91 | \\//foobar | ||
| 92 | \\ | ||
| 93 | ); | ||
| 94 | } | ||
| 95 | |||
| 96 | test "zig fmt: file ends in comment after var decl" { | ||
| 97 | try testTransform( | ||
| 98 | \\const x = 42; | ||
| 99 | \\ //foobar | ||
| 100 | , | ||
| 101 | \\const x = 42; | ||
| 102 | \\//foobar | ||
| 103 | \\ | ||
| 104 | ); | ||
| 105 | } | ||
| 106 | |||
| 107 | test "zig fmt: doc comments on test" { | ||
| 108 | try testCanonical( | ||
| 109 | \\/// hello | ||
| 110 | \\/// world | ||
| 111 | \\test "" {} | ||
| 112 | \\ | ||
| 113 | ); | ||
| 114 | } | ||
| 115 | |||
| 328 | test "zig fmt: if statment" { | 116 | test "zig fmt: if statment" { |
| 329 | try testCanonical( | 117 | try testCanonical( |
| 330 | \\test "" { | 118 | \\test "" { |
| ... | @@ -357,7 +145,7 @@ test "zig fmt: decl between fields" { | ... | @@ -357,7 +145,7 @@ test "zig fmt: decl between fields" { |
| 357 | \\ b: usize, | 145 | \\ b: usize, |
| 358 | \\}; | 146 | \\}; |
| 359 | , &[_]Error{ | 147 | , &[_]Error{ |
| 360 | .DeclBetweenFields, | 148 | .decl_between_fields, |
| 361 | }); | 149 | }); |
| 362 | } | 150 | } |
| 363 | 151 | ||
| ... | @@ -365,7 +153,7 @@ test "zig fmt: eof after missing comma" { | ... | @@ -365,7 +153,7 @@ test "zig fmt: eof after missing comma" { |
| 365 | try testError( | 153 | try testError( |
| 366 | \\foo() | 154 | \\foo() |
| 367 | , &[_]Error{ | 155 | , &[_]Error{ |
| 368 | .ExpectedToken, | 156 | .expected_token, |
| 369 | }); | 157 | }); |
| 370 | } | 158 | } |
| 371 | 159 | ||
| ... | @@ -402,7 +190,7 @@ test "zig fmt: nosuspend await" { | ... | @@ -402,7 +190,7 @@ test "zig fmt: nosuspend await" { |
| 402 | ); | 190 | ); |
| 403 | } | 191 | } |
| 404 | 192 | ||
| 405 | test "zig fmt: trailing comma in container declaration" { | 193 | test "zig fmt: container declaration, single line" { |
| 406 | try testCanonical( | 194 | try testCanonical( |
| 407 | \\const X = struct { foo: i32 }; | 195 | \\const X = struct { foo: i32 }; |
| 408 | \\const X = struct { foo: i32, bar: i32 }; | 196 | \\const X = struct { foo: i32, bar: i32 }; |
| ... | @@ -411,7 +199,23 @@ test "zig fmt: trailing comma in container declaration" { | ... | @@ -411,7 +199,23 @@ test "zig fmt: trailing comma in container declaration" { |
| 411 | \\const X = struct { foo: i32 align(4) = 1, bar: i32 align(4) = 2 }; | 199 | \\const X = struct { foo: i32 align(4) = 1, bar: i32 align(4) = 2 }; |
| 412 | \\ | 200 | \\ |
| 413 | ); | 201 | ); |
| 202 | } | ||
| 203 | |||
| 204 | test "zig fmt: container declaration, one item, multi line trailing comma" { | ||
| 414 | try testCanonical( | 205 | try testCanonical( |
| 206 | \\test "" { | ||
| 207 | \\ comptime { | ||
| 208 | \\ const X = struct { | ||
| 209 | \\ x: i32, | ||
| 210 | \\ }; | ||
| 211 | \\ } | ||
| 212 | \\} | ||
| 213 | \\ | ||
| 214 | ); | ||
| 215 | } | ||
| 216 | |||
| 217 | test "zig fmt: container declaration, no trailing comma on separate line" { | ||
| 218 | try testTransform( | ||
| 415 | \\test "" { | 219 | \\test "" { |
| 416 | \\ comptime { | 220 | \\ comptime { |
| 417 | \\ const X = struct { | 221 | \\ const X = struct { |
| ... | @@ -420,30 +224,113 @@ test "zig fmt: trailing comma in container declaration" { | ... | @@ -420,30 +224,113 @@ test "zig fmt: trailing comma in container declaration" { |
| 420 | \\ } | 224 | \\ } |
| 421 | \\} | 225 | \\} |
| 422 | \\ | 226 | \\ |
| 227 | , | ||
| 228 | \\test "" { | ||
| 229 | \\ comptime { | ||
| 230 | \\ const X = struct { x: i32 }; | ||
| 231 | \\ } | ||
| 232 | \\} | ||
| 233 | \\ | ||
| 423 | ); | 234 | ); |
| 235 | } | ||
| 236 | |||
| 237 | test "zig fmt: container declaration, line break, no trailing comma" { | ||
| 424 | try testTransform( | 238 | try testTransform( |
| 425 | \\const X = struct { | 239 | \\const X = struct { |
| 426 | \\ foo: i32, bar: i8 }; | 240 | \\ foo: i32, bar: i8 }; |
| 241 | , | ||
| 242 | \\const X = struct { foo: i32, bar: i8 }; | ||
| 243 | \\ | ||
| 244 | ); | ||
| 245 | } | ||
| 246 | |||
| 247 | test "zig fmt: container declaration, transform trailing comma" { | ||
| 248 | try testTransform( | ||
| 249 | \\const X = struct { | ||
| 250 | \\ foo: i32, bar: i8, }; | ||
| 427 | , | 251 | , |
| 428 | \\const X = struct { | 252 | \\const X = struct { |
| 429 | \\ foo: i32, bar: i8 | 253 | \\ foo: i32, |
| 254 | \\ bar: i8, | ||
| 430 | \\}; | 255 | \\}; |
| 431 | \\ | 256 | \\ |
| 432 | ); | 257 | ); |
| 433 | } | 258 | } |
| 434 | 259 | ||
| 435 | test "zig fmt: trailing comma in fn parameter list" { | 260 | test "zig fmt: remove empty lines at start/end of container decl" { |
| 436 | try testCanonical( | 261 | try testTransform( |
| 437 | \\pub fn f( | 262 | \\const X = struct { |
| 438 | \\ a: i32, | 263 | \\ |
| 439 | \\ b: i32, | 264 | \\ foo: i32, |
| 440 | \\) i32 {} | 265 | \\ |
| 441 | \\pub fn f( | 266 | \\ bar: i8, |
| 442 | \\ a: i32, | 267 | \\ |
| 443 | \\ b: i32, | 268 | \\}; |
| 444 | \\) align(8) i32 {} | 269 | \\ |
| 445 | \\pub fn f( | 270 | , |
| 446 | \\ a: i32, | 271 | \\const X = struct { |
| 272 | \\ foo: i32, | ||
| 273 | \\ | ||
| 274 | \\ bar: i8, | ||
| 275 | \\}; | ||
| 276 | \\ | ||
| 277 | ); | ||
| 278 | } | ||
| 279 | |||
| 280 | test "zig fmt: remove empty lines at start/end of block" { | ||
| 281 | try testTransform( | ||
| 282 | \\test { | ||
| 283 | \\ | ||
| 284 | \\ if (foo) { | ||
| 285 | \\ foo(); | ||
| 286 | \\ } | ||
| 287 | \\ | ||
| 288 | \\} | ||
| 289 | \\ | ||
| 290 | , | ||
| 291 | \\test { | ||
| 292 | \\ if (foo) { | ||
| 293 | \\ foo(); | ||
| 294 | \\ } | ||
| 295 | \\} | ||
| 296 | \\ | ||
| 297 | ); | ||
| 298 | } | ||
| 299 | |||
| 300 | test "zig fmt: allow empty line before commment at start of block" { | ||
| 301 | try testCanonical( | ||
| 302 | \\test { | ||
| 303 | \\ | ||
| 304 | \\ // foo | ||
| 305 | \\ const x = 42; | ||
| 306 | \\} | ||
| 307 | \\ | ||
| 308 | ); | ||
| 309 | } | ||
| 310 | |||
| 311 | test "zig fmt: allow empty line before commment at start of block" { | ||
| 312 | try testCanonical( | ||
| 313 | \\test { | ||
| 314 | \\ | ||
| 315 | \\ // foo | ||
| 316 | \\ const x = 42; | ||
| 317 | \\} | ||
| 318 | \\ | ||
| 319 | ); | ||
| 320 | } | ||
| 321 | |||
| 322 | test "zig fmt: trailing comma in fn parameter list" { | ||
| 323 | try testCanonical( | ||
| 324 | \\pub fn f( | ||
| 325 | \\ a: i32, | ||
| 326 | \\ b: i32, | ||
| 327 | \\) i32 {} | ||
| 328 | \\pub fn f( | ||
| 329 | \\ a: i32, | ||
| 330 | \\ b: i32, | ||
| 331 | \\) align(8) i32 {} | ||
| 332 | \\pub fn f( | ||
| 333 | \\ a: i32, | ||
| 447 | \\ b: i32, | 334 | \\ b: i32, |
| 448 | \\) linksection(".text") i32 {} | 335 | \\) linksection(".text") i32 {} |
| 449 | \\pub fn f( | 336 | \\pub fn f( |
| ... | @@ -480,6 +367,31 @@ test "zig fmt: comptime struct field" { | ... | @@ -480,6 +367,31 @@ test "zig fmt: comptime struct field" { |
| 480 | ); | 367 | ); |
| 481 | } | 368 | } |
| 482 | 369 | ||
| 370 | test "zig fmt: break from block" { | ||
| 371 | try testCanonical( | ||
| 372 | \\const a = blk: { | ||
| 373 | \\ break :blk 42; | ||
| 374 | \\}; | ||
| 375 | \\const b = blk: { | ||
| 376 | \\ break :blk; | ||
| 377 | \\}; | ||
| 378 | \\const c = { | ||
| 379 | \\ break 42; | ||
| 380 | \\}; | ||
| 381 | \\const d = { | ||
| 382 | \\ break; | ||
| 383 | \\}; | ||
| 384 | \\ | ||
| 385 | ); | ||
| 386 | } | ||
| 387 | |||
| 388 | test "zig fmt: grouped expressions (parentheses)" { | ||
| 389 | try testCanonical( | ||
| 390 | \\const r = (x + y) * (a + b); | ||
| 391 | \\ | ||
| 392 | ); | ||
| 393 | } | ||
| 394 | |||
| 483 | test "zig fmt: c pointer type" { | 395 | test "zig fmt: c pointer type" { |
| 484 | try testCanonical( | 396 | try testCanonical( |
| 485 | \\pub extern fn repro() [*c]const u8; | 397 | \\pub extern fn repro() [*c]const u8; |
| ... | @@ -535,6 +447,19 @@ test "zig fmt: anytype struct field" { | ... | @@ -535,6 +447,19 @@ test "zig fmt: anytype struct field" { |
| 535 | ); | 447 | ); |
| 536 | } | 448 | } |
| 537 | 449 | ||
| 450 | test "zig fmt: array types last token" { | ||
| 451 | try testCanonical( | ||
| 452 | \\test { | ||
| 453 | \\ const x = [40]u32; | ||
| 454 | \\} | ||
| 455 | \\ | ||
| 456 | \\test { | ||
| 457 | \\ const x = [40:0]u32; | ||
| 458 | \\} | ||
| 459 | \\ | ||
| 460 | ); | ||
| 461 | } | ||
| 462 | |||
| 538 | test "zig fmt: sentinel-terminated array type" { | 463 | test "zig fmt: sentinel-terminated array type" { |
| 539 | try testCanonical( | 464 | try testCanonical( |
| 540 | \\pub fn cStrToPrefixedFileW(s: [*:0]const u8) ![PATH_MAX_WIDE:0]u16 { | 465 | \\pub fn cStrToPrefixedFileW(s: [*:0]const u8) ![PATH_MAX_WIDE:0]u16 { |
| ... | @@ -553,6 +478,58 @@ test "zig fmt: sentinel-terminated slice type" { | ... | @@ -553,6 +478,58 @@ test "zig fmt: sentinel-terminated slice type" { |
| 553 | ); | 478 | ); |
| 554 | } | 479 | } |
| 555 | 480 | ||
| 481 | test "zig fmt: pointer-to-one with modifiers" { | ||
| 482 | try testCanonical( | ||
| 483 | \\const x: *u32 = undefined; | ||
| 484 | \\const y: *allowzero align(8) const volatile u32 = undefined; | ||
| 485 | \\const z: *allowzero align(8:4:2) const volatile u32 = undefined; | ||
| 486 | \\ | ||
| 487 | ); | ||
| 488 | } | ||
| 489 | |||
| 490 | test "zig fmt: pointer-to-many with modifiers" { | ||
| 491 | try testCanonical( | ||
| 492 | \\const x: [*]u32 = undefined; | ||
| 493 | \\const y: [*]allowzero align(8) const volatile u32 = undefined; | ||
| 494 | \\const z: [*]allowzero align(8:4:2) const volatile u32 = undefined; | ||
| 495 | \\ | ||
| 496 | ); | ||
| 497 | } | ||
| 498 | |||
| 499 | test "zig fmt: sentinel pointer with modifiers" { | ||
| 500 | try testCanonical( | ||
| 501 | \\const x: [*:42]u32 = undefined; | ||
| 502 | \\const y: [*:42]allowzero align(8) const volatile u32 = undefined; | ||
| 503 | \\const y: [*:42]allowzero align(8:4:2) const volatile u32 = undefined; | ||
| 504 | \\ | ||
| 505 | ); | ||
| 506 | } | ||
| 507 | |||
| 508 | test "zig fmt: c pointer with modifiers" { | ||
| 509 | try testCanonical( | ||
| 510 | \\const x: [*c]u32 = undefined; | ||
| 511 | \\const y: [*c]allowzero align(8) const volatile u32 = undefined; | ||
| 512 | \\const z: [*c]allowzero align(8:4:2) const volatile u32 = undefined; | ||
| 513 | \\ | ||
| 514 | ); | ||
| 515 | } | ||
| 516 | |||
| 517 | test "zig fmt: slice with modifiers" { | ||
| 518 | try testCanonical( | ||
| 519 | \\const x: []u32 = undefined; | ||
| 520 | \\const y: []allowzero align(8) const volatile u32 = undefined; | ||
| 521 | \\ | ||
| 522 | ); | ||
| 523 | } | ||
| 524 | |||
| 525 | test "zig fmt: sentinel slice with modifiers" { | ||
| 526 | try testCanonical( | ||
| 527 | \\const x: [:42]u32 = undefined; | ||
| 528 | \\const y: [:42]allowzero align(8) const volatile u32 = undefined; | ||
| 529 | \\ | ||
| 530 | ); | ||
| 531 | } | ||
| 532 | |||
| 556 | test "zig fmt: anon literal in array" { | 533 | test "zig fmt: anon literal in array" { |
| 557 | try testCanonical( | 534 | try testCanonical( |
| 558 | \\var arr: [2]Foo = .{ | 535 | \\var arr: [2]Foo = .{ |
| ... | @@ -581,100 +558,91 @@ test "zig fmt: alignment in anonymous literal" { | ... | @@ -581,100 +558,91 @@ test "zig fmt: alignment in anonymous literal" { |
| 581 | ); | 558 | ); |
| 582 | } | 559 | } |
| 583 | 560 | ||
| 584 | test "zig fmt: anon struct literal syntax" { | 561 | test "zig fmt: anon struct literal 0 element" { |
| 585 | try testCanonical( | 562 | try testCanonical( |
| 586 | \\const x = .{ | 563 | \\test { |
| 587 | \\ .a = b, | 564 | \\ const x = .{}; |
| 588 | \\ .c = d, | 565 | \\} |
| 589 | \\}; | ||
| 590 | \\ | 566 | \\ |
| 591 | ); | 567 | ); |
| 592 | } | 568 | } |
| 593 | 569 | ||
| 594 | test "zig fmt: anon list literal syntax" { | 570 | test "zig fmt: anon struct literal 1 element" { |
| 595 | try testCanonical( | 571 | try testCanonical( |
| 596 | \\const x = .{ a, b, c }; | 572 | \\test { |
| 573 | \\ const x = .{ .a = b }; | ||
| 574 | \\} | ||
| 597 | \\ | 575 | \\ |
| 598 | ); | 576 | ); |
| 599 | } | 577 | } |
| 600 | 578 | ||
| 601 | test "zig fmt: async function" { | 579 | test "zig fmt: anon struct literal 1 element comma" { |
| 602 | try testCanonical( | 580 | try testCanonical( |
| 603 | \\pub const Server = struct { | 581 | \\test { |
| 604 | \\ handleRequestFn: fn (*Server, *const std.net.Address, File) callconv(.Async) void, | 582 | \\ const x = .{ |
| 605 | \\}; | 583 | \\ .a = b, |
| 606 | \\test "hi" { | 584 | \\ }; |
| 607 | \\ var ptr = @ptrCast(fn (i32) callconv(.Async) void, other); | ||
| 608 | \\} | 585 | \\} |
| 609 | \\ | 586 | \\ |
| 610 | ); | 587 | ); |
| 611 | } | 588 | } |
| 612 | 589 | ||
| 613 | test "zig fmt: whitespace fixes" { | 590 | test "zig fmt: anon struct literal 2 element" { |
| 614 | try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n", | 591 | try testCanonical( |
| 615 | \\test "" { | 592 | \\test { |
| 616 | \\ const hi = x; | 593 | \\ const x = .{ .a = b, .c = d }; |
| 617 | \\} | 594 | \\} |
| 618 | \\// zig fmt: off | ||
| 619 | \\test ""{ | ||
| 620 | \\ const a = b;} | ||
| 621 | \\ | 595 | \\ |
| 622 | ); | 596 | ); |
| 623 | } | 597 | } |
| 624 | 598 | ||
| 625 | test "zig fmt: while else err prong with no block" { | 599 | test "zig fmt: anon struct literal 2 element comma" { |
| 626 | try testCanonical( | 600 | try testCanonical( |
| 627 | \\test "" { | 601 | \\test { |
| 628 | \\ const result = while (returnError()) |value| { | 602 | \\ const x = .{ |
| 629 | \\ break value; | 603 | \\ .a = b, |
| 630 | \\ } else |err| @as(i32, 2); | 604 | \\ .c = d, |
| 631 | \\ expect(result == 2); | 605 | \\ }; |
| 632 | \\} | 606 | \\} |
| 633 | \\ | 607 | \\ |
| 634 | ); | 608 | ); |
| 635 | } | 609 | } |
| 636 | 610 | ||
| 637 | test "zig fmt: tagged union with enum values" { | 611 | test "zig fmt: anon struct literal 3 element" { |
| 638 | try testCanonical( | 612 | try testCanonical( |
| 639 | \\const MultipleChoice2 = union(enum(u32)) { | 613 | \\test { |
| 640 | \\ Unspecified1: i32, | 614 | \\ const x = .{ .a = b, .c = d, .e = f }; |
| 641 | \\ A: f32 = 20, | 615 | \\} |
| 642 | \\ Unspecified2: void, | ||
| 643 | \\ B: bool = 40, | ||
| 644 | \\ Unspecified3: i32, | ||
| 645 | \\ C: i8 = 60, | ||
| 646 | \\ Unspecified4: void, | ||
| 647 | \\ D: void = 1000, | ||
| 648 | \\ Unspecified5: i32, | ||
| 649 | \\}; | ||
| 650 | \\ | 616 | \\ |
| 651 | ); | 617 | ); |
| 652 | } | 618 | } |
| 653 | 619 | ||
| 654 | test "zig fmt: allowzero pointer" { | 620 | test "zig fmt: anon struct literal 3 element comma" { |
| 655 | try testCanonical( | 621 | try testCanonical( |
| 656 | \\const T = [*]allowzero const u8; | 622 | \\test { |
| 623 | \\ const x = .{ | ||
| 624 | \\ .a = b, | ||
| 625 | \\ .c = d, | ||
| 626 | \\ .e = f, | ||
| 627 | \\ }; | ||
| 628 | \\} | ||
| 657 | \\ | 629 | \\ |
| 658 | ); | 630 | ); |
| 659 | } | 631 | } |
| 660 | 632 | ||
| 661 | test "zig fmt: enum literal" { | 633 | test "zig fmt: struct literal 0 element" { |
| 662 | try testCanonical( | 634 | try testCanonical( |
| 663 | \\const x = .hi; | 635 | \\test { |
| 636 | \\ const x = X{}; | ||
| 637 | \\} | ||
| 664 | \\ | 638 | \\ |
| 665 | ); | 639 | ); |
| 666 | } | 640 | } |
| 667 | 641 | ||
| 668 | test "zig fmt: enum literal inside array literal" { | 642 | test "zig fmt: struct literal 1 element" { |
| 669 | try testCanonical( | 643 | try testCanonical( |
| 670 | \\test "enums in arrays" { | 644 | \\test { |
| 671 | \\ var colors = []Color{.Green}; | 645 | \\ const x = X{ .a = b }; |
| 672 | \\ colors = []Colors{ .Green, .Cyan }; | ||
| 673 | \\ colors = []Colors{ | ||
| 674 | \\ .Grey, | ||
| 675 | \\ .Green, | ||
| 676 | \\ .Cyan, | ||
| 677 | \\ }; | ||
| 678 | \\} | 646 | \\} |
| 679 | \\ | 647 | \\ |
| 680 | ); | 648 | ); |
| ... | @@ -682,656 +650,1016 @@ test "zig fmt: enum literal inside array literal" { | ... | @@ -682,656 +650,1016 @@ test "zig fmt: enum literal inside array literal" { |
| 682 | 650 | ||
| 683 | test "zig fmt: Unicode code point literal larger than u8" { | 651 | test "zig fmt: Unicode code point literal larger than u8" { |
| 684 | try testCanonical( | 652 | try testCanonical( |
| 685 | \\const x = '\u{01f4a9}'; | 653 | \\test { |
| 654 | \\ const x = X{ | ||
| 655 | \\ .a = b, | ||
| 656 | \\ }; | ||
| 657 | \\} | ||
| 686 | \\ | 658 | \\ |
| 687 | ); | 659 | ); |
| 688 | } | 660 | } |
| 689 | 661 | ||
| 690 | test "zig fmt: infix operator and then multiline string literal" { | 662 | test "zig fmt: struct literal 2 element" { |
| 691 | try testCanonical( | 663 | try testCanonical( |
| 692 | \\const x = "" ++ | 664 | \\test { |
| 693 | \\ \\ hi | 665 | \\ const x = X{ .a = b, .c = d }; |
| 694 | \\; | 666 | \\} |
| 695 | \\ | 667 | \\ |
| 696 | ); | 668 | ); |
| 697 | } | 669 | } |
| 698 | 670 | ||
| 699 | test "zig fmt: infix operator and then multiline string literal" { | 671 | test "zig fmt: struct literal 2 element comma" { |
| 700 | try testCanonical( | 672 | try testCanonical( |
| 701 | \\const x = "" ++ | 673 | \\test { |
| 702 | \\ \\ hi0 | 674 | \\ const x = X{ |
| 703 | \\ \\ hi1 | 675 | \\ .a = b, |
| 704 | \\ \\ hi2 | 676 | \\ .c = d, |
| 705 | \\; | 677 | \\ }; |
| 678 | \\} | ||
| 706 | \\ | 679 | \\ |
| 707 | ); | 680 | ); |
| 708 | } | 681 | } |
| 709 | 682 | ||
| 710 | test "zig fmt: C pointers" { | 683 | test "zig fmt: struct literal 3 element" { |
| 711 | try testCanonical( | 684 | try testCanonical( |
| 712 | \\const Ptr = [*c]i32; | 685 | \\test { |
| 686 | \\ const x = X{ .a = b, .c = d, .e = f }; | ||
| 687 | \\} | ||
| 713 | \\ | 688 | \\ |
| 714 | ); | 689 | ); |
| 715 | } | 690 | } |
| 716 | 691 | ||
| 717 | test "zig fmt: threadlocal" { | 692 | test "zig fmt: struct literal 3 element comma" { |
| 718 | try testCanonical( | 693 | try testCanonical( |
| 719 | \\threadlocal var x: i32 = 1234; | 694 | \\test { |
| 695 | \\ const x = X{ | ||
| 696 | \\ .a = b, | ||
| 697 | \\ .c = d, | ||
| 698 | \\ .e = f, | ||
| 699 | \\ }; | ||
| 700 | \\} | ||
| 720 | \\ | 701 | \\ |
| 721 | ); | 702 | ); |
| 722 | } | 703 | } |
| 723 | 704 | ||
| 724 | test "zig fmt: linksection" { | 705 | test "zig fmt: anon list literal 1 element" { |
| 725 | try testCanonical( | 706 | try testCanonical( |
| 726 | \\export var aoeu: u64 linksection(".text.derp") = 1234; | 707 | \\test { |
| 727 | \\export fn _start() linksection(".text.boot") callconv(.Naked) noreturn {} | 708 | \\ const x = .{a}; |
| 709 | \\} | ||
| 728 | \\ | 710 | \\ |
| 729 | ); | 711 | ); |
| 730 | } | 712 | } |
| 731 | 713 | ||
| 732 | test "zig fmt: correctly move doc comments on struct fields" { | 714 | test "zig fmt: anon list literal 1 element comma" { |
| 733 | try testTransform( | 715 | try testCanonical( |
| 734 | \\pub const section_64 = extern struct { | 716 | \\test { |
| 735 | \\ sectname: [16]u8, /// name of this section | 717 | \\ const x = .{ |
| 736 | \\ segname: [16]u8, /// segment this section goes in | 718 | \\ a, |
| 737 | \\}; | 719 | \\ }; |
| 738 | , | 720 | \\} |
| 739 | \\pub const section_64 = extern struct { | ||
| 740 | \\ /// name of this section | ||
| 741 | \\ sectname: [16]u8, | ||
| 742 | \\ /// segment this section goes in | ||
| 743 | \\ segname: [16]u8, | ||
| 744 | \\}; | ||
| 745 | \\ | 721 | \\ |
| 746 | ); | 722 | ); |
| 747 | } | 723 | } |
| 748 | 724 | ||
| 749 | test "zig fmt: correctly space struct fields with doc comments" { | 725 | test "zig fmt: anon list literal 2 element" { |
| 750 | try testTransform( | 726 | try testCanonical( |
| 751 | \\pub const S = struct { | 727 | \\test { |
| 752 | \\ /// A | 728 | \\ const x = .{ a, b }; |
| 753 | \\ a: u8, | 729 | \\} |
| 754 | \\ /// B | ||
| 755 | \\ /// B (cont) | ||
| 756 | \\ b: u8, | ||
| 757 | \\ | ||
| 758 | \\ | ||
| 759 | \\ /// C | ||
| 760 | \\ c: u8, | ||
| 761 | \\}; | ||
| 762 | \\ | ||
| 763 | , | ||
| 764 | \\pub const S = struct { | ||
| 765 | \\ /// A | ||
| 766 | \\ a: u8, | ||
| 767 | \\ /// B | ||
| 768 | \\ /// B (cont) | ||
| 769 | \\ b: u8, | ||
| 770 | \\ | ||
| 771 | \\ /// C | ||
| 772 | \\ c: u8, | ||
| 773 | \\}; | ||
| 774 | \\ | 730 | \\ |
| 775 | ); | 731 | ); |
| 776 | } | 732 | } |
| 777 | 733 | ||
| 778 | test "zig fmt: doc comments on param decl" { | 734 | test "zig fmt: anon list literal 2 element comma" { |
| 779 | try testCanonical( | 735 | try testCanonical( |
| 780 | \\pub const Allocator = struct { | 736 | \\test { |
| 781 | \\ shrinkFn: fn ( | 737 | \\ const x = .{ |
| 782 | \\ self: *Allocator, | 738 | \\ a, |
| 783 | \\ /// Guaranteed to be the same as what was returned from most recent call to | 739 | \\ b, |
| 784 | \\ /// `allocFn`, `reallocFn`, or `shrinkFn`. | 740 | \\ }; |
| 785 | \\ old_mem: []u8, | 741 | \\} |
| 786 | \\ /// Guaranteed to be the same as what was returned from most recent call to | ||
| 787 | \\ /// `allocFn`, `reallocFn`, or `shrinkFn`. | ||
| 788 | \\ old_alignment: u29, | ||
| 789 | \\ /// Guaranteed to be less than or equal to `old_mem.len`. | ||
| 790 | \\ new_byte_count: usize, | ||
| 791 | \\ /// Guaranteed to be less than or equal to `old_alignment`. | ||
| 792 | \\ new_alignment: u29, | ||
| 793 | \\ ) []u8, | ||
| 794 | \\}; | ||
| 795 | \\ | 742 | \\ |
| 796 | ); | 743 | ); |
| 797 | } | 744 | } |
| 798 | 745 | ||
| 799 | test "zig fmt: aligned struct field" { | 746 | test "zig fmt: anon list literal 3 element" { |
| 800 | try testCanonical( | 747 | try testCanonical( |
| 801 | \\pub const S = struct { | 748 | \\test { |
| 802 | \\ f: i32 align(32), | 749 | \\ const x = .{ a, b, c }; |
| 803 | \\}; | 750 | \\} |
| 804 | \\ | ||
| 805 | ); | ||
| 806 | try testCanonical( | ||
| 807 | \\pub const S = struct { | ||
| 808 | \\ f: i32 align(32) = 1, | ||
| 809 | \\}; | ||
| 810 | \\ | 751 | \\ |
| 811 | ); | 752 | ); |
| 812 | } | 753 | } |
| 813 | 754 | ||
| 814 | test "zig fmt: comment to disable/enable zig fmt first" { | 755 | test "zig fmt: anon list literal 3 element comma" { |
| 815 | try testCanonical( | 756 | try testCanonical( |
| 816 | \\// Test trailing comma syntax | 757 | \\test { |
| 817 | \\// zig fmt: off | 758 | \\ const x = .{ |
| 759 | \\ a, | ||
| 760 | \\ // foo | ||
| 761 | \\ b, | ||
| 818 | \\ | 762 | \\ |
| 819 | \\const struct_trailing_comma = struct { x: i32, y: i32, }; | 763 | \\ c, |
| 820 | ); | 764 | \\ }; |
| 821 | } | 765 | \\} |
| 822 | |||
| 823 | test "zig fmt: comment to disable/enable zig fmt" { | ||
| 824 | try testTransform( | ||
| 825 | \\const a = b; | ||
| 826 | \\// zig fmt: off | ||
| 827 | \\const c = d; | ||
| 828 | \\// zig fmt: on | ||
| 829 | \\const e = f; | ||
| 830 | , | ||
| 831 | \\const a = b; | ||
| 832 | \\// zig fmt: off | ||
| 833 | \\const c = d; | ||
| 834 | \\// zig fmt: on | ||
| 835 | \\const e = f; | ||
| 836 | \\ | 766 | \\ |
| 837 | ); | 767 | ); |
| 838 | } | 768 | } |
| 839 | 769 | ||
| 840 | test "zig fmt: line comment following 'zig fmt: off'" { | 770 | test "zig fmt: array literal 0 element" { |
| 841 | try testCanonical( | 771 | try testCanonical( |
| 842 | \\// zig fmt: off | 772 | \\test { |
| 843 | \\// Test | 773 | \\ const x = [_]u32{}; |
| 844 | \\const e = f; | 774 | \\} |
| 775 | \\ | ||
| 845 | ); | 776 | ); |
| 846 | } | 777 | } |
| 847 | 778 | ||
| 848 | test "zig fmt: doc comment following 'zig fmt: off'" { | 779 | test "zig fmt: array literal 1 element" { |
| 849 | try testCanonical( | 780 | try testCanonical( |
| 850 | \\// zig fmt: off | 781 | \\test { |
| 851 | \\/// test | 782 | \\ const x = [_]u32{a}; |
| 852 | \\const e = f; | 783 | \\} |
| 784 | \\ | ||
| 853 | ); | 785 | ); |
| 854 | } | 786 | } |
| 855 | 787 | ||
| 856 | test "zig fmt: line and doc comment following 'zig fmt: off'" { | 788 | test "zig fmt: array literal 1 element comma" { |
| 857 | try testCanonical( | 789 | try testCanonical( |
| 858 | \\// zig fmt: off | 790 | \\test { |
| 859 | \\// test 1 | 791 | \\ const x = [1]u32{ |
| 860 | \\/// test 2 | 792 | \\ a, |
| 861 | \\const e = f; | 793 | \\ }; |
| 794 | \\} | ||
| 795 | \\ | ||
| 862 | ); | 796 | ); |
| 863 | } | 797 | } |
| 864 | 798 | ||
| 865 | test "zig fmt: doc and line comment following 'zig fmt: off'" { | 799 | test "zig fmt: array literal 2 element" { |
| 866 | try testCanonical( | 800 | try testCanonical( |
| 867 | \\// zig fmt: off | 801 | \\test { |
| 868 | \\/// test 1 | 802 | \\ const x = [_]u32{ a, b }; |
| 869 | \\// test 2 | 803 | \\} |
| 870 | \\const e = f; | 804 | \\ |
| 871 | ); | 805 | ); |
| 872 | } | 806 | } |
| 873 | 807 | ||
| 874 | test "zig fmt: alternating 'zig fmt: off' and 'zig fmt: on'" { | 808 | test "zig fmt: array literal 2 element comma" { |
| 875 | try testCanonical( | 809 | try testCanonical( |
| 876 | \\// zig fmt: off | 810 | \\test { |
| 877 | \\// zig fmt: on | 811 | \\ const x = [2]u32{ |
| 878 | \\// zig fmt: off | 812 | \\ a, |
| 879 | \\const e = f; | 813 | \\ b, |
| 880 | \\// zig fmt: off | 814 | \\ }; |
| 881 | \\// zig fmt: on | 815 | \\} |
| 882 | \\// zig fmt: off | ||
| 883 | \\const a = b; | ||
| 884 | \\// zig fmt: on | ||
| 885 | \\const c = d; | ||
| 886 | \\// zig fmt: on | ||
| 887 | \\ | 816 | \\ |
| 888 | ); | 817 | ); |
| 889 | } | 818 | } |
| 890 | 819 | ||
| 891 | test "zig fmt: line comment following 'zig fmt: on'" { | 820 | test "zig fmt: array literal 3 element" { |
| 892 | try testCanonical( | 821 | try testCanonical( |
| 893 | \\// zig fmt: off | 822 | \\test { |
| 894 | \\const e = f; | 823 | \\ const x = [_]u32{ a, b, c }; |
| 895 | \\// zig fmt: on | 824 | \\} |
| 896 | \\// test | ||
| 897 | \\const e = f; | ||
| 898 | \\ | 825 | \\ |
| 899 | ); | 826 | ); |
| 900 | } | 827 | } |
| 901 | 828 | ||
| 902 | test "zig fmt: doc comment following 'zig fmt: on'" { | 829 | test "zig fmt: array literal 3 element comma" { |
| 903 | try testCanonical( | 830 | try testCanonical( |
| 904 | \\// zig fmt: off | 831 | \\test { |
| 905 | \\const e = f; | 832 | \\ const x = [3]u32{ |
| 906 | \\// zig fmt: on | 833 | \\ a, |
| 907 | \\/// test | 834 | \\ b, |
| 908 | \\const e = f; | 835 | \\ c, |
| 836 | \\ }; | ||
| 837 | \\} | ||
| 909 | \\ | 838 | \\ |
| 910 | ); | 839 | ); |
| 911 | } | 840 | } |
| 912 | 841 | ||
| 913 | test "zig fmt: line and doc comment following 'zig fmt: on'" { | 842 | test "zig fmt: sentinel array literal 1 element" { |
| 914 | try testCanonical( | 843 | try testCanonical( |
| 915 | \\// zig fmt: off | 844 | \\test { |
| 916 | \\const e = f; | 845 | \\ const x = [_:9000]u32{a}; |
| 917 | \\// zig fmt: on | 846 | \\} |
| 918 | \\// test1 | ||
| 919 | \\/// test2 | ||
| 920 | \\const e = f; | ||
| 921 | \\ | 847 | \\ |
| 922 | ); | 848 | ); |
| 923 | } | 849 | } |
| 924 | 850 | ||
| 925 | test "zig fmt: doc and line comment following 'zig fmt: on'" { | 851 | test "zig fmt: slices" { |
| 926 | try testCanonical( | 852 | try testCanonical( |
| 927 | \\// zig fmt: off | 853 | \\const a = b[0..]; |
| 928 | \\const e = f; | 854 | \\const c = d[0..1]; |
| 929 | \\// zig fmt: on | 855 | \\const e = f[0..1 :0]; |
| 930 | \\/// test1 | ||
| 931 | \\// test2 | ||
| 932 | \\const e = f; | ||
| 933 | \\ | 856 | \\ |
| 934 | ); | 857 | ); |
| 935 | } | 858 | } |
| 936 | 859 | ||
| 937 | test "zig fmt: pointer of unknown length" { | 860 | test "zig fmt: slices with spaces in bounds" { |
| 938 | try testCanonical( | 861 | try testCanonical( |
| 939 | \\fn foo(ptr: [*]u8) void {} | 862 | \\const a = b[0 + 0 ..]; |
| 863 | \\const c = d[0 + 0 .. 1]; | ||
| 864 | \\const e = f[0 .. 1 + 1 :0]; | ||
| 940 | \\ | 865 | \\ |
| 941 | ); | 866 | ); |
| 942 | } | 867 | } |
| 943 | 868 | ||
| 944 | test "zig fmt: spaces around slice operator" { | 869 | test "zig fmt: block in slice expression" { |
| 945 | try testCanonical( | 870 | try testCanonical( |
| 946 | \\var a = b[c..d]; | 871 | \\const a = b[{ |
| 947 | \\var a = b[c..d :0]; | 872 | \\ _ = x; |
| 948 | \\var a = b[c + 1 .. d]; | 873 | \\}..]; |
| 949 | \\var a = b[c + 1 ..]; | 874 | \\const c = d[0..{ |
| 950 | \\var a = b[c .. d + 1]; | 875 | \\ _ = x; |
| 951 | \\var a = b[c .. d + 1 :0]; | 876 | \\ _ = y; |
| 952 | \\var a = b[c.a..d.e]; | 877 | \\}]; |
| 953 | \\var a = b[c.a..d.e :0]; | 878 | \\const e = f[0..1 :{ |
| 879 | \\ _ = x; | ||
| 880 | \\ _ = y; | ||
| 881 | \\ _ = z; | ||
| 882 | \\}]; | ||
| 954 | \\ | 883 | \\ |
| 955 | ); | 884 | ); |
| 956 | } | 885 | } |
| 957 | 886 | ||
| 958 | test "zig fmt: async call in if condition" { | 887 | test "zig fmt: async function" { |
| 959 | try testCanonical( | 888 | try testCanonical( |
| 960 | \\comptime { | 889 | \\pub const Server = struct { |
| 961 | \\ if (async b()) { | 890 | \\ handleRequestFn: fn (*Server, *const std.net.Address, File) callconv(.Async) void, |
| 962 | \\ a(); | 891 | \\}; |
| 963 | \\ } | 892 | \\test "hi" { |
| 893 | \\ var ptr = @ptrCast(fn (i32) callconv(.Async) void, other); | ||
| 964 | \\} | 894 | \\} |
| 965 | \\ | 895 | \\ |
| 966 | ); | 896 | ); |
| 967 | } | 897 | } |
| 968 | 898 | ||
| 969 | test "zig fmt: 2nd arg multiline string" { | 899 | test "zig fmt: whitespace fixes" { |
| 970 | try testCanonical( | 900 | try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n", |
| 971 | \\comptime { | 901 | \\test "" { |
| 972 | \\ cases.addAsm("hello world linux x86_64", | 902 | \\ const hi = x; |
| 973 | \\ \\.text | ||
| 974 | \\ , "Hello, world!\n"); | ||
| 975 | \\} | 903 | \\} |
| 904 | \\// zig fmt: off | ||
| 905 | \\test ""{ | ||
| 906 | \\ const a = b;} | ||
| 976 | \\ | 907 | \\ |
| 977 | ); | 908 | ); |
| 978 | } | 909 | } |
| 979 | 910 | ||
| 980 | test "zig fmt: 2nd arg multiline string many args" { | 911 | test "zig fmt: while else err prong with no block" { |
| 981 | try testCanonical( | 912 | try testCanonical( |
| 982 | \\comptime { | 913 | \\test "" { |
| 983 | \\ cases.addAsm("hello world linux x86_64", | 914 | \\ const result = while (returnError()) |value| { |
| 984 | \\ \\.text | 915 | \\ break value; |
| 985 | \\ , "Hello, world!\n", "Hello, world!\n"); | 916 | \\ } else |err| @as(i32, 2); |
| 917 | \\ expect(result == 2); | ||
| 986 | \\} | 918 | \\} |
| 987 | \\ | 919 | \\ |
| 988 | ); | 920 | ); |
| 989 | } | 921 | } |
| 990 | 922 | ||
| 991 | test "zig fmt: final arg multiline string" { | 923 | test "zig fmt: tagged union with enum values" { |
| 992 | try testCanonical( | 924 | try testCanonical( |
| 993 | \\comptime { | 925 | \\const MultipleChoice2 = union(enum(u32)) { |
| 994 | \\ cases.addAsm("hello world linux x86_64", "Hello, world!\n", | 926 | \\ Unspecified1: i32, |
| 995 | \\ \\.text | 927 | \\ A: f32 = 20, |
| 996 | \\ ); | 928 | \\ Unspecified2: void, |
| 997 | \\} | 929 | \\ B: bool = 40, |
| 930 | \\ Unspecified3: i32, | ||
| 931 | \\ C: i8 = 60, | ||
| 932 | \\ Unspecified4: void, | ||
| 933 | \\ D: void = 1000, | ||
| 934 | \\ Unspecified5: i32, | ||
| 935 | \\}; | ||
| 998 | \\ | 936 | \\ |
| 999 | ); | 937 | ); |
| 1000 | } | 938 | } |
| 1001 | 939 | ||
| 1002 | test "zig fmt: if condition wraps" { | 940 | test "zig fmt: tagged union enum tag last token" { |
| 1003 | try testTransform( | 941 | try testCanonical( |
| 1004 | \\comptime { | 942 | \\test { |
| 1005 | \\ if (cond and | 943 | \\ const U = union(enum(u32)) {}; |
| 1006 | \\ cond) { | ||
| 1007 | \\ return x; | ||
| 1008 | \\ } | ||
| 1009 | \\ while (cond and | ||
| 1010 | \\ cond) { | ||
| 1011 | \\ return x; | ||
| 1012 | \\ } | ||
| 1013 | \\ if (a == b and | ||
| 1014 | \\ c) { | ||
| 1015 | \\ a = b; | ||
| 1016 | \\ } | ||
| 1017 | \\ while (a == b and | ||
| 1018 | \\ c) { | ||
| 1019 | \\ a = b; | ||
| 1020 | \\ } | ||
| 1021 | \\ if ((cond and | ||
| 1022 | \\ cond)) { | ||
| 1023 | \\ return x; | ||
| 1024 | \\ } | ||
| 1025 | \\ while ((cond and | ||
| 1026 | \\ cond)) { | ||
| 1027 | \\ return x; | ||
| 1028 | \\ } | ||
| 1029 | \\ var a = if (a) |*f| x: { | ||
| 1030 | \\ break :x &a.b; | ||
| 1031 | \\ } else |err| err; | ||
| 1032 | \\ var a = if (cond and | ||
| 1033 | \\ cond) |*f| | ||
| 1034 | \\ x: { | ||
| 1035 | \\ break :x &a.b; | ||
| 1036 | \\ } else |err| err; | ||
| 1037 | \\} | 944 | \\} |
| 1038 | , | 945 | \\ |
| 1039 | \\comptime { | 946 | \\test { |
| 1040 | \\ if (cond and | 947 | \\ const U = union(enum(u32)) { foo }; |
| 1041 | \\ cond) | 948 | \\} |
| 1042 | \\ { | 949 | \\ |
| 1043 | \\ return x; | 950 | \\test { |
| 1044 | \\ } | 951 | \\ const U = union(enum(u32)) { |
| 1045 | \\ while (cond and | 952 | \\ foo, |
| 1046 | \\ cond) | 953 | \\ }; |
| 1047 | \\ { | ||
| 1048 | \\ return x; | ||
| 1049 | \\ } | ||
| 1050 | \\ if (a == b and | ||
| 1051 | \\ c) | ||
| 1052 | \\ { | ||
| 1053 | \\ a = b; | ||
| 1054 | \\ } | ||
| 1055 | \\ while (a == b and | ||
| 1056 | \\ c) | ||
| 1057 | \\ { | ||
| 1058 | \\ a = b; | ||
| 1059 | \\ } | ||
| 1060 | \\ if ((cond and | ||
| 1061 | \\ cond)) | ||
| 1062 | \\ { | ||
| 1063 | \\ return x; | ||
| 1064 | \\ } | ||
| 1065 | \\ while ((cond and | ||
| 1066 | \\ cond)) | ||
| 1067 | \\ { | ||
| 1068 | \\ return x; | ||
| 1069 | \\ } | ||
| 1070 | \\ var a = if (a) |*f| x: { | ||
| 1071 | \\ break :x &a.b; | ||
| 1072 | \\ } else |err| err; | ||
| 1073 | \\ var a = if (cond and | ||
| 1074 | \\ cond) |*f| | ||
| 1075 | \\ x: { | ||
| 1076 | \\ break :x &a.b; | ||
| 1077 | \\ } else |err| err; | ||
| 1078 | \\} | 954 | \\} |
| 1079 | \\ | 955 | \\ |
| 1080 | ); | 956 | ); |
| 1081 | } | 957 | } |
| 1082 | 958 | ||
| 1083 | test "zig fmt: if condition has line break but must not wrap" { | 959 | test "zig fmt: allowzero pointer" { |
| 1084 | try testCanonical( | 960 | try testCanonical( |
| 1085 | \\comptime { | 961 | \\const T = [*]allowzero const u8; |
| 1086 | \\ if (self.user_input_options.put( | ||
| 1087 | \\ name, | ||
| 1088 | \\ UserInputOption{ | ||
| 1089 | \\ .name = name, | ||
| 1090 | \\ .used = false, | ||
| 1091 | \\ }, | ||
| 1092 | \\ ) catch unreachable) |*prev_value| { | ||
| 1093 | \\ foo(); | ||
| 1094 | \\ bar(); | ||
| 1095 | \\ } | ||
| 1096 | \\ if (put( | ||
| 1097 | \\ a, | ||
| 1098 | \\ b, | ||
| 1099 | \\ )) { | ||
| 1100 | \\ foo(); | ||
| 1101 | \\ } | ||
| 1102 | \\} | ||
| 1103 | \\ | 962 | \\ |
| 1104 | ); | 963 | ); |
| 1105 | } | 964 | } |
| 1106 | 965 | ||
| 1107 | test "zig fmt: if condition has line break but must not wrap" { | 966 | test "zig fmt: enum literal" { |
| 1108 | try testCanonical( | 967 | try testCanonical( |
| 1109 | \\comptime { | 968 | \\const x = .hi; |
| 1110 | \\ if (self.user_input_options.put(name, UserInputOption{ | ||
| 1111 | \\ .name = name, | ||
| 1112 | \\ .used = false, | ||
| 1113 | \\ }) catch unreachable) |*prev_value| { | ||
| 1114 | \\ foo(); | ||
| 1115 | \\ bar(); | ||
| 1116 | \\ } | ||
| 1117 | \\ if (put( | ||
| 1118 | \\ a, | ||
| 1119 | \\ b, | ||
| 1120 | \\ )) { | ||
| 1121 | \\ foo(); | ||
| 1122 | \\ } | ||
| 1123 | \\} | ||
| 1124 | \\ | 969 | \\ |
| 1125 | ); | 970 | ); |
| 1126 | } | 971 | } |
| 1127 | 972 | ||
| 1128 | test "zig fmt: function call with multiline argument" { | 973 | test "zig fmt: enum literal inside array literal" { |
| 1129 | try testCanonical( | 974 | try testCanonical( |
| 1130 | \\comptime { | 975 | \\test "enums in arrays" { |
| 1131 | \\ self.user_input_options.put(name, UserInputOption{ | 976 | \\ var colors = []Color{.Green}; |
| 1132 | \\ .name = name, | 977 | \\ colors = []Colors{ .Green, .Cyan }; |
| 1133 | \\ .used = false, | 978 | \\ colors = []Colors{ |
| 1134 | \\ }); | 979 | \\ .Grey, |
| 980 | \\ .Green, | ||
| 981 | \\ .Cyan, | ||
| 982 | \\ }; | ||
| 1135 | \\} | 983 | \\} |
| 1136 | \\ | 984 | \\ |
| 1137 | ); | 985 | ); |
| 1138 | } | 986 | } |
| 1139 | 987 | ||
| 1140 | test "zig fmt: same-line doc comment on variable declaration" { | 988 | test "zig fmt: character literal larger than u8" { |
| 1141 | try testTransform( | 989 | try testCanonical( |
| 1142 | \\pub const MAP_ANONYMOUS = 0x1000; /// allocated from memory, swap space | 990 | \\const x = '\u{01f4a9}'; |
| 1143 | \\pub const MAP_FILE = 0x0000; /// map from file (default) | ||
| 1144 | \\ | ||
| 1145 | \\pub const EMEDIUMTYPE = 124; /// Wrong medium type | ||
| 1146 | \\ | ||
| 1147 | \\// nameserver query return codes | ||
| 1148 | \\pub const ENSROK = 0; /// DNS server returned answer with no data | ||
| 1149 | , | ||
| 1150 | \\/// allocated from memory, swap space | ||
| 1151 | \\pub const MAP_ANONYMOUS = 0x1000; | ||
| 1152 | \\/// map from file (default) | ||
| 1153 | \\pub const MAP_FILE = 0x0000; | ||
| 1154 | \\ | ||
| 1155 | \\/// Wrong medium type | ||
| 1156 | \\pub const EMEDIUMTYPE = 124; | ||
| 1157 | \\ | ||
| 1158 | \\// nameserver query return codes | ||
| 1159 | \\/// DNS server returned answer with no data | ||
| 1160 | \\pub const ENSROK = 0; | ||
| 1161 | \\ | 991 | \\ |
| 1162 | ); | 992 | ); |
| 1163 | } | 993 | } |
| 1164 | 994 | ||
| 1165 | test "zig fmt: if-else with comment before else" { | 995 | test "zig fmt: infix operator and then multiline string literal" { |
| 1166 | try testCanonical( | 996 | try testCanonical( |
| 1167 | \\comptime { | 997 | \\const x = "" ++ |
| 1168 | \\ // cexp(finite|nan +- i inf|nan) = nan + i nan | 998 | \\ \\ hi |
| 1169 | \\ if ((hx & 0x7fffffff) != 0x7f800000) { | 999 | \\; |
| 1170 | \\ return Complex(f32).new(y - y, y - y); | ||
| 1171 | \\ } // cexp(-inf +- i inf|nan) = 0 + i0 | ||
| 1172 | \\ else if (hx & 0x80000000 != 0) { | ||
| 1173 | \\ return Complex(f32).new(0, 0); | ||
| 1174 | \\ } // cexp(+inf +- i inf|nan) = inf + i nan | ||
| 1175 | \\ else { | ||
| 1176 | \\ return Complex(f32).new(x, y - y); | ||
| 1177 | \\ } | ||
| 1178 | \\} | ||
| 1179 | \\ | 1000 | \\ |
| 1180 | ); | 1001 | ); |
| 1181 | } | 1002 | } |
| 1182 | 1003 | ||
| 1183 | test "zig fmt: if nested" { | 1004 | test "zig fmt: infix operator and then multiline string literal" { |
| 1184 | try testCanonical( | 1005 | try testCanonical( |
| 1185 | \\pub fn foo() void { | 1006 | \\const x = "" ++ |
| 1186 | \\ return if ((aInt & bInt) >= 0) | 1007 | \\ \\ hi0 |
| 1187 | \\ if (aInt < bInt) | 1008 | \\ \\ hi1 |
| 1188 | \\ GE_LESS | 1009 | \\ \\ hi2 |
| 1189 | \\ else if (aInt == bInt) | 1010 | \\; |
| 1190 | \\ GE_EQUAL | ||
| 1191 | \\ else | ||
| 1192 | \\ GE_GREATER | ||
| 1193 | \\ else if (aInt > bInt) | ||
| 1194 | \\ GE_LESS | ||
| 1195 | \\ else if (aInt == bInt) | ||
| 1196 | \\ GE_EQUAL | ||
| 1197 | \\ else | ||
| 1198 | \\ GE_GREATER; | ||
| 1199 | \\} | ||
| 1200 | \\ | 1011 | \\ |
| 1201 | ); | 1012 | ); |
| 1202 | } | 1013 | } |
| 1203 | 1014 | ||
| 1204 | test "zig fmt: respect line breaks in if-else" { | 1015 | test "zig fmt: C pointers" { |
| 1205 | try testCanonical( | 1016 | try testCanonical( |
| 1206 | \\comptime { | 1017 | \\const Ptr = [*c]i32; |
| 1207 | \\ return if (cond) a else b; | ||
| 1208 | \\ return if (cond) | ||
| 1209 | \\ a | ||
| 1210 | \\ else | ||
| 1211 | \\ b; | ||
| 1212 | \\ return if (cond) | ||
| 1213 | \\ a | ||
| 1214 | \\ else if (cond) | ||
| 1215 | \\ b | ||
| 1216 | \\ else | ||
| 1217 | \\ c; | ||
| 1218 | \\} | ||
| 1219 | \\ | 1018 | \\ |
| 1220 | ); | 1019 | ); |
| 1221 | } | 1020 | } |
| 1222 | 1021 | ||
| 1223 | test "zig fmt: respect line breaks after infix operators" { | 1022 | test "zig fmt: threadlocal" { |
| 1224 | try testCanonical( | 1023 | try testCanonical( |
| 1225 | \\comptime { | 1024 | \\threadlocal var x: i32 = 1234; |
| 1226 | \\ self.crc = | ||
| 1227 | \\ lookup_tables[0][p[7]] ^ | ||
| 1228 | \\ lookup_tables[1][p[6]] ^ | ||
| 1229 | \\ lookup_tables[2][p[5]] ^ | ||
| 1230 | \\ lookup_tables[3][p[4]] ^ | ||
| 1231 | \\ lookup_tables[4][@truncate(u8, self.crc >> 24)] ^ | ||
| 1232 | \\ lookup_tables[5][@truncate(u8, self.crc >> 16)] ^ | ||
| 1233 | \\ lookup_tables[6][@truncate(u8, self.crc >> 8)] ^ | ||
| 1234 | \\ lookup_tables[7][@truncate(u8, self.crc >> 0)]; | ||
| 1235 | \\} | ||
| 1236 | \\ | 1025 | \\ |
| 1237 | ); | 1026 | ); |
| 1238 | } | 1027 | } |
| 1239 | 1028 | ||
| 1240 | test "zig fmt: fn decl with trailing comma" { | 1029 | test "zig fmt: linksection" { |
| 1241 | try testTransform( | 1030 | try testCanonical( |
| 1242 | \\fn foo(a: i32, b: i32,) void {} | 1031 | \\export var aoeu: u64 linksection(".text.derp") = 1234; |
| 1243 | , | 1032 | \\export fn _start() linksection(".text.boot") callconv(.Naked) noreturn {} |
| 1244 | \\fn foo( | ||
| 1245 | \\ a: i32, | ||
| 1246 | \\ b: i32, | ||
| 1247 | \\) void {} | ||
| 1248 | \\ | 1033 | \\ |
| 1249 | ); | 1034 | ); |
| 1250 | } | 1035 | } |
| 1251 | 1036 | ||
| 1252 | test "zig fmt: enum decl with no trailing comma" { | 1037 | test "zig fmt: correctly space struct fields with doc comments" { |
| 1253 | try testTransform( | 1038 | try testTransform( |
| 1254 | \\const StrLitKind = enum {Normal, C}; | 1039 | \\pub const S = struct { |
| 1040 | \\ /// A | ||
| 1041 | \\ a: u8, | ||
| 1042 | \\ /// B | ||
| 1043 | \\ /// B (cont) | ||
| 1044 | \\ b: u8, | ||
| 1045 | \\ | ||
| 1046 | \\ | ||
| 1047 | \\ /// C | ||
| 1048 | \\ c: u8, | ||
| 1049 | \\}; | ||
| 1050 | \\ | ||
| 1255 | , | 1051 | , |
| 1256 | \\const StrLitKind = enum { Normal, C }; | 1052 | \\pub const S = struct { |
| 1053 | \\ /// A | ||
| 1054 | \\ a: u8, | ||
| 1055 | \\ /// B | ||
| 1056 | \\ /// B (cont) | ||
| 1057 | \\ b: u8, | ||
| 1058 | \\ | ||
| 1059 | \\ /// C | ||
| 1060 | \\ c: u8, | ||
| 1061 | \\}; | ||
| 1257 | \\ | 1062 | \\ |
| 1258 | ); | 1063 | ); |
| 1259 | } | 1064 | } |
| 1260 | 1065 | ||
| 1261 | test "zig fmt: switch comment before prong" { | 1066 | test "zig fmt: doc comments on param decl" { |
| 1262 | try testCanonical( | 1067 | try testCanonical( |
| 1263 | \\comptime { | 1068 | \\pub const Allocator = struct { |
| 1264 | \\ switch (a) { | 1069 | \\ shrinkFn: fn ( |
| 1265 | \\ // hi | 1070 | \\ self: *Allocator, |
| 1266 | \\ 0 => {}, | 1071 | \\ /// Guaranteed to be the same as what was returned from most recent call to |
| 1267 | \\ } | 1072 | \\ /// `allocFn`, `reallocFn`, or `shrinkFn`. |
| 1268 | \\} | 1073 | \\ old_mem: []u8, |
| 1074 | \\ /// Guaranteed to be the same as what was returned from most recent call to | ||
| 1075 | \\ /// `allocFn`, `reallocFn`, or `shrinkFn`. | ||
| 1076 | \\ old_alignment: u29, | ||
| 1077 | \\ /// Guaranteed to be less than or equal to `old_mem.len`. | ||
| 1078 | \\ new_byte_count: usize, | ||
| 1079 | \\ /// Guaranteed to be less than or equal to `old_alignment`. | ||
| 1080 | \\ new_alignment: u29, | ||
| 1081 | \\ ) []u8, | ||
| 1082 | \\}; | ||
| 1269 | \\ | 1083 | \\ |
| 1270 | ); | 1084 | ); |
| 1271 | } | 1085 | } |
| 1272 | 1086 | ||
| 1273 | test "zig fmt: struct literal no trailing comma" { | 1087 | test "zig fmt: aligned struct field" { |
| 1274 | try testTransform( | 1088 | try testCanonical( |
| 1275 | \\const a = foo{ .x = 1, .y = 2 }; | 1089 | \\pub const S = struct { |
| 1276 | \\const a = foo{ .x = 1, | 1090 | \\ f: i32 align(32), |
| 1277 | \\ .y = 2 }; | 1091 | \\}; |
| 1278 | , | 1092 | \\ |
| 1279 | \\const a = foo{ .x = 1, .y = 2 }; | 1093 | ); |
| 1280 | \\const a = foo{ | 1094 | try testCanonical( |
| 1281 | \\ .x = 1, | 1095 | \\pub const S = struct { |
| 1282 | \\ .y = 2, | 1096 | \\ f: i32 align(32) = 1, |
| 1283 | \\}; | 1097 | \\}; |
| 1284 | \\ | 1098 | \\ |
| 1285 | ); | 1099 | ); |
| 1286 | } | 1100 | } |
| 1287 | 1101 | ||
| 1288 | test "zig fmt: struct literal containing a multiline expression" { | 1102 | test "zig fmt: comment to disable/enable zig fmt first" { |
| 1289 | try testTransform( | 1103 | try testCanonical( |
| 1290 | \\const a = A{ .x = if (f1()) 10 else 20 }; | 1104 | \\// Test trailing comma syntax |
| 1291 | \\const a = A{ .x = if (f1()) 10 else 20, }; | 1105 | \\// zig fmt: off |
| 1292 | \\const a = A{ .x = if (f1()) | ||
| 1293 | \\ 10 else 20 }; | ||
| 1294 | \\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100 }; | ||
| 1295 | \\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100, }; | ||
| 1296 | \\const a = A{ .x = if (f1()) | ||
| 1297 | \\ 10 else 20}; | ||
| 1298 | \\const a = A{ .x = switch(g) {0 => "ok", else => "no"} }; | ||
| 1299 | \\ | ||
| 1300 | , | ||
| 1301 | \\const a = A{ .x = if (f1()) 10 else 20 }; | ||
| 1302 | \\const a = A{ | ||
| 1303 | \\ .x = if (f1()) 10 else 20, | ||
| 1304 | \\}; | ||
| 1305 | \\const a = A{ | ||
| 1306 | \\ .x = if (f1()) | ||
| 1307 | \\ 10 | ||
| 1308 | \\ else | ||
| 1309 | \\ 20, | ||
| 1310 | \\}; | ||
| 1311 | \\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100 }; | ||
| 1312 | \\const a = A{ | ||
| 1313 | \\ .x = if (f1()) 10 else 20, | ||
| 1314 | \\ .y = f2() + 100, | ||
| 1315 | \\}; | ||
| 1316 | \\const a = A{ | ||
| 1317 | \\ .x = if (f1()) | ||
| 1318 | \\ 10 | ||
| 1319 | \\ else | ||
| 1320 | \\ 20, | ||
| 1321 | \\}; | ||
| 1322 | \\const a = A{ | ||
| 1323 | \\ .x = switch (g) { | ||
| 1324 | \\ 0 => "ok", | ||
| 1325 | \\ else => "no", | ||
| 1326 | \\ }, | ||
| 1327 | \\}; | ||
| 1328 | \\ | 1106 | \\ |
| 1107 | \\const struct_trailing_comma = struct { x: i32, y: i32, }; | ||
| 1329 | ); | 1108 | ); |
| 1330 | } | 1109 | } |
| 1331 | 1110 | ||
| 1332 | test "zig fmt: array literal with hint" { | 1111 | test "zig fmt: comment to disable/enable zig fmt" { |
| 1333 | try testTransform( | 1112 | try testTransform( |
| 1334 | \\const a = []u8{ | 1113 | \\const a = b; |
| 1114 | \\// zig fmt: off | ||
| 1115 | \\const c = d; | ||
| 1116 | \\// zig fmt: on | ||
| 1117 | \\const e = f; | ||
| 1118 | , | ||
| 1119 | \\const a = b; | ||
| 1120 | \\// zig fmt: off | ||
| 1121 | \\const c = d; | ||
| 1122 | \\// zig fmt: on | ||
| 1123 | \\const e = f; | ||
| 1124 | \\ | ||
| 1125 | ); | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | test "zig fmt: line comment following 'zig fmt: off'" { | ||
| 1129 | try testCanonical( | ||
| 1130 | \\// zig fmt: off | ||
| 1131 | \\// Test | ||
| 1132 | \\const e = f; | ||
| 1133 | ); | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | test "zig fmt: doc comment following 'zig fmt: off'" { | ||
| 1137 | try testCanonical( | ||
| 1138 | \\// zig fmt: off | ||
| 1139 | \\/// test | ||
| 1140 | \\const e = f; | ||
| 1141 | ); | ||
| 1142 | } | ||
| 1143 | |||
| 1144 | test "zig fmt: line and doc comment following 'zig fmt: off'" { | ||
| 1145 | try testCanonical( | ||
| 1146 | \\// zig fmt: off | ||
| 1147 | \\// test 1 | ||
| 1148 | \\/// test 2 | ||
| 1149 | \\const e = f; | ||
| 1150 | ); | ||
| 1151 | } | ||
| 1152 | |||
| 1153 | test "zig fmt: doc and line comment following 'zig fmt: off'" { | ||
| 1154 | try testCanonical( | ||
| 1155 | \\// zig fmt: off | ||
| 1156 | \\/// test 1 | ||
| 1157 | \\// test 2 | ||
| 1158 | \\const e = f; | ||
| 1159 | ); | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | test "zig fmt: alternating 'zig fmt: off' and 'zig fmt: on'" { | ||
| 1163 | try testCanonical( | ||
| 1164 | \\// zig fmt: off | ||
| 1165 | \\// zig fmt: on | ||
| 1166 | \\// zig fmt: off | ||
| 1167 | \\const e = f; | ||
| 1168 | \\// zig fmt: off | ||
| 1169 | \\// zig fmt: on | ||
| 1170 | \\// zig fmt: off | ||
| 1171 | \\const a = b; | ||
| 1172 | \\// zig fmt: on | ||
| 1173 | \\const c = d; | ||
| 1174 | \\// zig fmt: on | ||
| 1175 | \\ | ||
| 1176 | ); | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | test "zig fmt: line comment following 'zig fmt: on'" { | ||
| 1180 | try testCanonical( | ||
| 1181 | \\// zig fmt: off | ||
| 1182 | \\const e = f; | ||
| 1183 | \\// zig fmt: on | ||
| 1184 | \\// test | ||
| 1185 | \\const e = f; | ||
| 1186 | \\ | ||
| 1187 | ); | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | test "zig fmt: doc comment following 'zig fmt: on'" { | ||
| 1191 | try testCanonical( | ||
| 1192 | \\// zig fmt: off | ||
| 1193 | \\const e = f; | ||
| 1194 | \\// zig fmt: on | ||
| 1195 | \\/// test | ||
| 1196 | \\const e = f; | ||
| 1197 | \\ | ||
| 1198 | ); | ||
| 1199 | } | ||
| 1200 | |||
| 1201 | test "zig fmt: line and doc comment following 'zig fmt: on'" { | ||
| 1202 | try testCanonical( | ||
| 1203 | \\// zig fmt: off | ||
| 1204 | \\const e = f; | ||
| 1205 | \\// zig fmt: on | ||
| 1206 | \\// test1 | ||
| 1207 | \\/// test2 | ||
| 1208 | \\const e = f; | ||
| 1209 | \\ | ||
| 1210 | ); | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | test "zig fmt: doc and line comment following 'zig fmt: on'" { | ||
| 1214 | try testCanonical( | ||
| 1215 | \\// zig fmt: off | ||
| 1216 | \\const e = f; | ||
| 1217 | \\// zig fmt: on | ||
| 1218 | \\/// test1 | ||
| 1219 | \\// test2 | ||
| 1220 | \\const e = f; | ||
| 1221 | \\ | ||
| 1222 | ); | ||
| 1223 | } | ||
| 1224 | |||
| 1225 | test "zig fmt: 'zig fmt: (off|on)' works in the middle of code" { | ||
| 1226 | try testTransform( | ||
| 1227 | \\test "" { | ||
| 1228 | \\ const x = 42; | ||
| 1229 | \\ | ||
| 1230 | \\ if (foobar) |y| { | ||
| 1231 | \\ // zig fmt: off | ||
| 1232 | \\ }// zig fmt: on | ||
| 1233 | \\ | ||
| 1234 | \\ const z = 420; | ||
| 1235 | \\} | ||
| 1236 | \\ | ||
| 1237 | , | ||
| 1238 | \\test "" { | ||
| 1239 | \\ const x = 42; | ||
| 1240 | \\ | ||
| 1241 | \\ if (foobar) |y| { | ||
| 1242 | \\ // zig fmt: off | ||
| 1243 | \\ }// zig fmt: on | ||
| 1244 | \\ | ||
| 1245 | \\ const z = 420; | ||
| 1246 | \\} | ||
| 1247 | \\ | ||
| 1248 | ); | ||
| 1249 | } | ||
| 1250 | |||
| 1251 | test "zig fmt: pointer of unknown length" { | ||
| 1252 | try testCanonical( | ||
| 1253 | \\fn foo(ptr: [*]u8) void {} | ||
| 1254 | \\ | ||
| 1255 | ); | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | test "zig fmt: spaces around slice operator" { | ||
| 1259 | try testCanonical( | ||
| 1260 | \\var a = b[c..d]; | ||
| 1261 | \\var a = b[c..d :0]; | ||
| 1262 | \\var a = b[c + 1 .. d]; | ||
| 1263 | \\var a = b[c + 1 ..]; | ||
| 1264 | \\var a = b[c .. d + 1]; | ||
| 1265 | \\var a = b[c .. d + 1 :0]; | ||
| 1266 | \\var a = b[c.a..d.e]; | ||
| 1267 | \\var a = b[c.a..d.e :0]; | ||
| 1268 | \\ | ||
| 1269 | ); | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | test "zig fmt: async call in if condition" { | ||
| 1273 | try testCanonical( | ||
| 1274 | \\comptime { | ||
| 1275 | \\ if (async b()) { | ||
| 1276 | \\ a(); | ||
| 1277 | \\ } | ||
| 1278 | \\} | ||
| 1279 | \\ | ||
| 1280 | ); | ||
| 1281 | } | ||
| 1282 | |||
| 1283 | test "zig fmt: 2nd arg multiline string" { | ||
| 1284 | try testCanonical( | ||
| 1285 | \\comptime { | ||
| 1286 | \\ cases.addAsm("hello world linux x86_64", | ||
| 1287 | \\ \\.text | ||
| 1288 | \\ , "Hello, world!\n"); | ||
| 1289 | \\} | ||
| 1290 | \\ | ||
| 1291 | ); | ||
| 1292 | try testTransform( | ||
| 1293 | \\comptime { | ||
| 1294 | \\ cases.addAsm("hello world linux x86_64", | ||
| 1295 | \\ \\.text | ||
| 1296 | \\ , "Hello, world!\n",); | ||
| 1297 | \\} | ||
| 1298 | , | ||
| 1299 | \\comptime { | ||
| 1300 | \\ cases.addAsm( | ||
| 1301 | \\ "hello world linux x86_64", | ||
| 1302 | \\ \\.text | ||
| 1303 | \\ , | ||
| 1304 | \\ "Hello, world!\n", | ||
| 1305 | \\ ); | ||
| 1306 | \\} | ||
| 1307 | \\ | ||
| 1308 | ); | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | test "zig fmt: 2nd arg multiline string many args" { | ||
| 1312 | try testCanonical( | ||
| 1313 | \\comptime { | ||
| 1314 | \\ cases.addAsm("hello world linux x86_64", | ||
| 1315 | \\ \\.text | ||
| 1316 | \\ , "Hello, world!\n", "Hello, world!\n"); | ||
| 1317 | \\} | ||
| 1318 | \\ | ||
| 1319 | ); | ||
| 1320 | } | ||
| 1321 | |||
| 1322 | test "zig fmt: final arg multiline string" { | ||
| 1323 | try testCanonical( | ||
| 1324 | \\comptime { | ||
| 1325 | \\ cases.addAsm("hello world linux x86_64", "Hello, world!\n", | ||
| 1326 | \\ \\.text | ||
| 1327 | \\ ); | ||
| 1328 | \\} | ||
| 1329 | \\ | ||
| 1330 | ); | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | test "zig fmt: if condition wraps" { | ||
| 1334 | try testTransform( | ||
| 1335 | \\comptime { | ||
| 1336 | \\ if (cond and | ||
| 1337 | \\ cond) { | ||
| 1338 | \\ return x; | ||
| 1339 | \\ } | ||
| 1340 | \\ while (cond and | ||
| 1341 | \\ cond) { | ||
| 1342 | \\ return x; | ||
| 1343 | \\ } | ||
| 1344 | \\ if (a == b and | ||
| 1345 | \\ c) { | ||
| 1346 | \\ a = b; | ||
| 1347 | \\ } | ||
| 1348 | \\ while (a == b and | ||
| 1349 | \\ c) { | ||
| 1350 | \\ a = b; | ||
| 1351 | \\ } | ||
| 1352 | \\ if ((cond and | ||
| 1353 | \\ cond)) { | ||
| 1354 | \\ return x; | ||
| 1355 | \\ } | ||
| 1356 | \\ while ((cond and | ||
| 1357 | \\ cond)) { | ||
| 1358 | \\ return x; | ||
| 1359 | \\ } | ||
| 1360 | \\ var a = if (a) |*f| x: { | ||
| 1361 | \\ break :x &a.b; | ||
| 1362 | \\ } else |err| err; | ||
| 1363 | \\ var a = if (cond and | ||
| 1364 | \\ cond) |*f| | ||
| 1365 | \\ x: { | ||
| 1366 | \\ break :x &a.b; | ||
| 1367 | \\ } else |err| err; | ||
| 1368 | \\} | ||
| 1369 | , | ||
| 1370 | \\comptime { | ||
| 1371 | \\ if (cond and | ||
| 1372 | \\ cond) | ||
| 1373 | \\ { | ||
| 1374 | \\ return x; | ||
| 1375 | \\ } | ||
| 1376 | \\ while (cond and | ||
| 1377 | \\ cond) | ||
| 1378 | \\ { | ||
| 1379 | \\ return x; | ||
| 1380 | \\ } | ||
| 1381 | \\ if (a == b and | ||
| 1382 | \\ c) | ||
| 1383 | \\ { | ||
| 1384 | \\ a = b; | ||
| 1385 | \\ } | ||
| 1386 | \\ while (a == b and | ||
| 1387 | \\ c) | ||
| 1388 | \\ { | ||
| 1389 | \\ a = b; | ||
| 1390 | \\ } | ||
| 1391 | \\ if ((cond and | ||
| 1392 | \\ cond)) | ||
| 1393 | \\ { | ||
| 1394 | \\ return x; | ||
| 1395 | \\ } | ||
| 1396 | \\ while ((cond and | ||
| 1397 | \\ cond)) | ||
| 1398 | \\ { | ||
| 1399 | \\ return x; | ||
| 1400 | \\ } | ||
| 1401 | \\ var a = if (a) |*f| x: { | ||
| 1402 | \\ break :x &a.b; | ||
| 1403 | \\ } else |err| err; | ||
| 1404 | \\ var a = if (cond and | ||
| 1405 | \\ cond) |*f| | ||
| 1406 | \\ x: { | ||
| 1407 | \\ break :x &a.b; | ||
| 1408 | \\ } else |err| err; | ||
| 1409 | \\} | ||
| 1410 | \\ | ||
| 1411 | ); | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | test "zig fmt: if condition has line break but must not wrap" { | ||
| 1415 | try testCanonical( | ||
| 1416 | \\comptime { | ||
| 1417 | \\ if (self.user_input_options.put( | ||
| 1418 | \\ name, | ||
| 1419 | \\ UserInputOption{ | ||
| 1420 | \\ .name = name, | ||
| 1421 | \\ .used = false, | ||
| 1422 | \\ }, | ||
| 1423 | \\ ) catch unreachable) |*prev_value| { | ||
| 1424 | \\ foo(); | ||
| 1425 | \\ bar(); | ||
| 1426 | \\ } | ||
| 1427 | \\ if (put( | ||
| 1428 | \\ a, | ||
| 1429 | \\ b, | ||
| 1430 | \\ )) { | ||
| 1431 | \\ foo(); | ||
| 1432 | \\ } | ||
| 1433 | \\} | ||
| 1434 | \\ | ||
| 1435 | ); | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | test "zig fmt: if condition has line break but must not wrap (no fn call comma)" { | ||
| 1439 | try testCanonical( | ||
| 1440 | \\comptime { | ||
| 1441 | \\ if (self.user_input_options.put(name, UserInputOption{ | ||
| 1442 | \\ .name = name, | ||
| 1443 | \\ .used = false, | ||
| 1444 | \\ }) catch unreachable) |*prev_value| { | ||
| 1445 | \\ foo(); | ||
| 1446 | \\ bar(); | ||
| 1447 | \\ } | ||
| 1448 | \\ if (put( | ||
| 1449 | \\ a, | ||
| 1450 | \\ b, | ||
| 1451 | \\ )) { | ||
| 1452 | \\ foo(); | ||
| 1453 | \\ } | ||
| 1454 | \\} | ||
| 1455 | \\ | ||
| 1456 | ); | ||
| 1457 | } | ||
| 1458 | |||
| 1459 | test "zig fmt: function call with multiline argument" { | ||
| 1460 | try testCanonical( | ||
| 1461 | \\comptime { | ||
| 1462 | \\ self.user_input_options.put(name, UserInputOption{ | ||
| 1463 | \\ .name = name, | ||
| 1464 | \\ .used = false, | ||
| 1465 | \\ }); | ||
| 1466 | \\} | ||
| 1467 | \\ | ||
| 1468 | ); | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | test "zig fmt: if-else with comment before else" { | ||
| 1472 | try testCanonical( | ||
| 1473 | \\comptime { | ||
| 1474 | \\ // cexp(finite|nan +- i inf|nan) = nan + i nan | ||
| 1475 | \\ if ((hx & 0x7fffffff) != 0x7f800000) { | ||
| 1476 | \\ return Complex(f32).new(y - y, y - y); | ||
| 1477 | \\ } // cexp(-inf +- i inf|nan) = 0 + i0 | ||
| 1478 | \\ else if (hx & 0x80000000 != 0) { | ||
| 1479 | \\ return Complex(f32).new(0, 0); | ||
| 1480 | \\ } // cexp(+inf +- i inf|nan) = inf + i nan | ||
| 1481 | \\ else { | ||
| 1482 | \\ return Complex(f32).new(x, y - y); | ||
| 1483 | \\ } | ||
| 1484 | \\} | ||
| 1485 | \\ | ||
| 1486 | ); | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | test "zig fmt: if nested" { | ||
| 1490 | try testCanonical( | ||
| 1491 | \\pub fn foo() void { | ||
| 1492 | \\ return if ((aInt & bInt) >= 0) | ||
| 1493 | \\ if (aInt < bInt) | ||
| 1494 | \\ GE_LESS | ||
| 1495 | \\ else if (aInt == bInt) | ||
| 1496 | \\ GE_EQUAL | ||
| 1497 | \\ else | ||
| 1498 | \\ GE_GREATER | ||
| 1499 | \\ // comment | ||
| 1500 | \\ else if (aInt > bInt) | ||
| 1501 | \\ GE_LESS | ||
| 1502 | \\ else if (aInt == bInt) | ||
| 1503 | \\ GE_EQUAL | ||
| 1504 | \\ else | ||
| 1505 | \\ GE_GREATER; | ||
| 1506 | \\ // comment | ||
| 1507 | \\} | ||
| 1508 | \\ | ||
| 1509 | ); | ||
| 1510 | } | ||
| 1511 | |||
| 1512 | test "zig fmt: respect line breaks in if-else" { | ||
| 1513 | try testCanonical( | ||
| 1514 | \\comptime { | ||
| 1515 | \\ return if (cond) a else b; | ||
| 1516 | \\ return if (cond) | ||
| 1517 | \\ a | ||
| 1518 | \\ else | ||
| 1519 | \\ b; | ||
| 1520 | \\ return if (cond) | ||
| 1521 | \\ a | ||
| 1522 | \\ else if (cond) | ||
| 1523 | \\ b | ||
| 1524 | \\ else | ||
| 1525 | \\ c; | ||
| 1526 | \\} | ||
| 1527 | \\ | ||
| 1528 | ); | ||
| 1529 | } | ||
| 1530 | |||
| 1531 | test "zig fmt: respect line breaks after infix operators" { | ||
| 1532 | try testCanonical( | ||
| 1533 | \\comptime { | ||
| 1534 | \\ self.crc = | ||
| 1535 | \\ lookup_tables[0][p[7]] ^ | ||
| 1536 | \\ lookup_tables[1][p[6]] ^ | ||
| 1537 | \\ lookup_tables[2][p[5]] ^ | ||
| 1538 | \\ lookup_tables[3][p[4]] ^ | ||
| 1539 | \\ lookup_tables[4][@truncate(u8, self.crc >> 24)] ^ | ||
| 1540 | \\ lookup_tables[5][@truncate(u8, self.crc >> 16)] ^ | ||
| 1541 | \\ lookup_tables[6][@truncate(u8, self.crc >> 8)] ^ | ||
| 1542 | \\ lookup_tables[7][@truncate(u8, self.crc >> 0)]; | ||
| 1543 | \\} | ||
| 1544 | \\ | ||
| 1545 | ); | ||
| 1546 | } | ||
| 1547 | |||
| 1548 | test "zig fmt: fn decl with trailing comma" { | ||
| 1549 | try testTransform( | ||
| 1550 | \\fn foo(a: i32, b: i32,) void {} | ||
| 1551 | , | ||
| 1552 | \\fn foo( | ||
| 1553 | \\ a: i32, | ||
| 1554 | \\ b: i32, | ||
| 1555 | \\) void {} | ||
| 1556 | \\ | ||
| 1557 | ); | ||
| 1558 | } | ||
| 1559 | |||
| 1560 | test "zig fmt: enum decl with no trailing comma" { | ||
| 1561 | try testTransform( | ||
| 1562 | \\const StrLitKind = enum {Normal, C}; | ||
| 1563 | , | ||
| 1564 | \\const StrLitKind = enum { Normal, C }; | ||
| 1565 | \\ | ||
| 1566 | ); | ||
| 1567 | } | ||
| 1568 | |||
| 1569 | test "zig fmt: switch comment before prong" { | ||
| 1570 | try testCanonical( | ||
| 1571 | \\comptime { | ||
| 1572 | \\ switch (a) { | ||
| 1573 | \\ // hi | ||
| 1574 | \\ 0 => {}, | ||
| 1575 | \\ } | ||
| 1576 | \\} | ||
| 1577 | \\ | ||
| 1578 | ); | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | test "zig fmt: struct literal no trailing comma" { | ||
| 1582 | try testTransform( | ||
| 1583 | \\const a = foo{ .x = 1, .y = 2 }; | ||
| 1584 | \\const a = foo{ .x = 1, | ||
| 1585 | \\ .y = 2 }; | ||
| 1586 | \\const a = foo{ .x = 1, | ||
| 1587 | \\ .y = 2, }; | ||
| 1588 | , | ||
| 1589 | \\const a = foo{ .x = 1, .y = 2 }; | ||
| 1590 | \\const a = foo{ .x = 1, .y = 2 }; | ||
| 1591 | \\const a = foo{ | ||
| 1592 | \\ .x = 1, | ||
| 1593 | \\ .y = 2, | ||
| 1594 | \\}; | ||
| 1595 | \\ | ||
| 1596 | ); | ||
| 1597 | } | ||
| 1598 | |||
| 1599 | test "zig fmt: struct literal containing a multiline expression" { | ||
| 1600 | try testTransform( | ||
| 1601 | \\const a = A{ .x = if (f1()) 10 else 20 }; | ||
| 1602 | \\const a = A{ .x = if (f1()) 10 else 20, }; | ||
| 1603 | \\const a = A{ .x = if (f1()) | ||
| 1604 | \\ 10 else 20 }; | ||
| 1605 | \\const a = A{ .x = if (f1()) | ||
| 1606 | \\ 10 else 20,}; | ||
| 1607 | \\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100 }; | ||
| 1608 | \\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100, }; | ||
| 1609 | \\const a = A{ .x = if (f1()) | ||
| 1610 | \\ 10 else 20}; | ||
| 1611 | \\const a = A{ .x = if (f1()) | ||
| 1612 | \\ 10 else 20,}; | ||
| 1613 | \\const a = A{ .x = switch(g) {0 => "ok", else => "no"} }; | ||
| 1614 | \\const a = A{ .x = switch(g) {0 => "ok", else => "no"}, }; | ||
| 1615 | \\ | ||
| 1616 | , | ||
| 1617 | \\const a = A{ .x = if (f1()) 10 else 20 }; | ||
| 1618 | \\const a = A{ | ||
| 1619 | \\ .x = if (f1()) 10 else 20, | ||
| 1620 | \\}; | ||
| 1621 | \\const a = A{ .x = if (f1()) | ||
| 1622 | \\ 10 | ||
| 1623 | \\else | ||
| 1624 | \\ 20 }; | ||
| 1625 | \\const a = A{ | ||
| 1626 | \\ .x = if (f1()) | ||
| 1627 | \\ 10 | ||
| 1628 | \\ else | ||
| 1629 | \\ 20, | ||
| 1630 | \\}; | ||
| 1631 | \\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100 }; | ||
| 1632 | \\const a = A{ | ||
| 1633 | \\ .x = if (f1()) 10 else 20, | ||
| 1634 | \\ .y = f2() + 100, | ||
| 1635 | \\}; | ||
| 1636 | \\const a = A{ .x = if (f1()) | ||
| 1637 | \\ 10 | ||
| 1638 | \\else | ||
| 1639 | \\ 20 }; | ||
| 1640 | \\const a = A{ | ||
| 1641 | \\ .x = if (f1()) | ||
| 1642 | \\ 10 | ||
| 1643 | \\ else | ||
| 1644 | \\ 20, | ||
| 1645 | \\}; | ||
| 1646 | \\const a = A{ .x = switch (g) { | ||
| 1647 | \\ 0 => "ok", | ||
| 1648 | \\ else => "no", | ||
| 1649 | \\} }; | ||
| 1650 | \\const a = A{ | ||
| 1651 | \\ .x = switch (g) { | ||
| 1652 | \\ 0 => "ok", | ||
| 1653 | \\ else => "no", | ||
| 1654 | \\ }, | ||
| 1655 | \\}; | ||
| 1656 | \\ | ||
| 1657 | ); | ||
| 1658 | } | ||
| 1659 | |||
| 1660 | test "zig fmt: array literal with hint" { | ||
| 1661 | try testTransform( | ||
| 1662 | \\const a = []u8{ | ||
| 1335 | \\ 1, 2, // | 1663 | \\ 1, 2, // |
| 1336 | \\ 3, | 1664 | \\ 3, |
| 1337 | \\ 4, | 1665 | \\ 4, |
| ... | @@ -1368,19 +1696,19 @@ test "zig fmt: array literal with hint" { | ... | @@ -1368,19 +1696,19 @@ test "zig fmt: array literal with hint" { |
| 1368 | \\}; | 1696 | \\}; |
| 1369 | , | 1697 | , |
| 1370 | \\const a = []u8{ | 1698 | \\const a = []u8{ |
| 1371 | \\ 1, 2, | 1699 | \\ 1, 2, // |
| 1372 | \\ 3, 4, | 1700 | \\ 3, 4, |
| 1373 | \\ 5, 6, | 1701 | \\ 5, 6, |
| 1374 | \\ 7, | 1702 | \\ 7, |
| 1375 | \\}; | 1703 | \\}; |
| 1376 | \\const a = []u8{ | 1704 | \\const a = []u8{ |
| 1377 | \\ 1, 2, | 1705 | \\ 1, 2, // |
| 1378 | \\ 3, 4, | 1706 | \\ 3, 4, |
| 1379 | \\ 5, 6, | 1707 | \\ 5, 6, |
| 1380 | \\ 7, 8, | 1708 | \\ 7, 8, |
| 1381 | \\}; | 1709 | \\}; |
| 1382 | \\const a = []u8{ | 1710 | \\const a = []u8{ |
| 1383 | \\ 1, 2, | 1711 | \\ 1, 2, // |
| 1384 | \\ 3, 4, | 1712 | \\ 3, 4, |
| 1385 | \\ 5, | 1713 | \\ 5, |
| 1386 | \\ 6, // blah | 1714 | \\ 6, // blah |
| ... | @@ -1388,21 +1716,19 @@ test "zig fmt: array literal with hint" { | ... | @@ -1388,21 +1716,19 @@ test "zig fmt: array literal with hint" { |
| 1388 | \\ 8, | 1716 | \\ 8, |
| 1389 | \\}; | 1717 | \\}; |
| 1390 | \\const a = []u8{ | 1718 | \\const a = []u8{ |
| 1391 | \\ 1, 2, | 1719 | \\ 1, 2, // |
| 1392 | \\ 3, // | 1720 | \\ 3, // |
| 1393 | \\ 4, | 1721 | \\ 4, |
| 1394 | \\ 5, 6, | 1722 | \\ 5, |
| 1723 | \\ 6, | ||
| 1395 | \\ 7, | 1724 | \\ 7, |
| 1396 | \\}; | 1725 | \\}; |
| 1397 | \\const a = []u8{ | 1726 | \\const a = []u8{ |
| 1398 | \\ 1, | 1727 | \\ 1, |
| 1399 | \\ 2, | 1728 | \\ 2, |
| 1400 | \\ 3, | 1729 | \\ 3, 4, // |
| 1401 | \\ 4, | 1730 | \\ 5, 6, // |
| 1402 | \\ 5, | 1731 | \\ 7, 8, // |
| 1403 | \\ 6, | ||
| 1404 | \\ 7, | ||
| 1405 | \\ 8, | ||
| 1406 | \\}; | 1732 | \\}; |
| 1407 | \\ | 1733 | \\ |
| 1408 | ); | 1734 | ); |
| ... | @@ -1508,11 +1834,21 @@ test "zig fmt: empty block with only comment" { | ... | @@ -1508,11 +1834,21 @@ test "zig fmt: empty block with only comment" { |
| 1508 | ); | 1834 | ); |
| 1509 | } | 1835 | } |
| 1510 | 1836 | ||
| 1511 | test "zig fmt: no trailing comma on struct decl" { | 1837 | test "zig fmt: trailing commas on struct decl" { |
| 1512 | try testCanonical( | 1838 | try testTransform( |
| 1513 | \\const RoundParam = struct { | 1839 | \\const RoundParam = struct { |
| 1514 | \\ k: usize, s: u32, t: u32 | 1840 | \\ k: usize, s: u32, t: u32 |
| 1515 | \\}; | 1841 | \\}; |
| 1842 | \\const RoundParam = struct { | ||
| 1843 | \\ k: usize, s: u32, t: u32, | ||
| 1844 | \\}; | ||
| 1845 | , | ||
| 1846 | \\const RoundParam = struct { k: usize, s: u32, t: u32 }; | ||
| 1847 | \\const RoundParam = struct { | ||
| 1848 | \\ k: usize, | ||
| 1849 | \\ s: u32, | ||
| 1850 | \\ t: u32, | ||
| 1851 | \\}; | ||
| 1516 | \\ | 1852 | \\ |
| 1517 | ); | 1853 | ); |
| 1518 | } | 1854 | } |
| ... | @@ -1560,11 +1896,7 @@ test "zig fmt: simple asm" { | ... | @@ -1560,11 +1896,7 @@ test "zig fmt: simple asm" { |
| 1560 | \\ : [a] "x" (-> i32) | 1896 | \\ : [a] "x" (-> i32) |
| 1561 | \\ : [a] "x" (1) | 1897 | \\ : [a] "x" (1) |
| 1562 | \\ ); | 1898 | \\ ); |
| 1563 | \\ asm ("still not real assembly" | 1899 | \\ asm ("still not real assembly" ::: "a", "b"); |
| 1564 | \\ : | ||
| 1565 | \\ : | ||
| 1566 | \\ : "a", "b" | ||
| 1567 | \\ ); | ||
| 1568 | \\} | 1900 | \\} |
| 1569 | \\ | 1901 | \\ |
| 1570 | ); | 1902 | ); |
| ... | @@ -1581,7 +1913,7 @@ test "zig fmt: nested struct literal with one item" { | ... | @@ -1581,7 +1913,7 @@ test "zig fmt: nested struct literal with one item" { |
| 1581 | 1913 | ||
| 1582 | test "zig fmt: switch cases trailing comma" { | 1914 | test "zig fmt: switch cases trailing comma" { |
| 1583 | try testTransform( | 1915 | try testTransform( |
| 1584 | \\fn switch_cases(x: i32) void { | 1916 | \\test "switch cases trailing comma"{ |
| 1585 | \\ switch (x) { | 1917 | \\ switch (x) { |
| 1586 | \\ 1,2,3 => {}, | 1918 | \\ 1,2,3 => {}, |
| 1587 | \\ 4,5, => {}, | 1919 | \\ 4,5, => {}, |
| ... | @@ -1590,7 +1922,7 @@ test "zig fmt: switch cases trailing comma" { | ... | @@ -1590,7 +1922,7 @@ test "zig fmt: switch cases trailing comma" { |
| 1590 | \\ } | 1922 | \\ } |
| 1591 | \\} | 1923 | \\} |
| 1592 | , | 1924 | , |
| 1593 | \\fn switch_cases(x: i32) void { | 1925 | \\test "switch cases trailing comma" { |
| 1594 | \\ switch (x) { | 1926 | \\ switch (x) { |
| 1595 | \\ 1, 2, 3 => {}, | 1927 | \\ 1, 2, 3 => {}, |
| 1596 | \\ 4, | 1928 | \\ 4, |
| ... | @@ -1657,18 +1989,18 @@ test "zig fmt: line comment after doc comment" { | ... | @@ -1657,18 +1989,18 @@ test "zig fmt: line comment after doc comment" { |
| 1657 | ); | 1989 | ); |
| 1658 | } | 1990 | } |
| 1659 | 1991 | ||
| 1660 | test "zig fmt: float literal with exponent" { | 1992 | test "zig fmt: bit field alignment" { |
| 1661 | try testCanonical( | 1993 | try testCanonical( |
| 1662 | \\test "bit field alignment" { | 1994 | \\test { |
| 1663 | \\ assert(@TypeOf(&blah.b) == *align(1:3:6) const u3); | 1995 | \\ assert(@TypeOf(&blah.b) == *align(1:3:6) const u3); |
| 1664 | \\} | 1996 | \\} |
| 1665 | \\ | 1997 | \\ |
| 1666 | ); | 1998 | ); |
| 1667 | } | 1999 | } |
| 1668 | 2000 | ||
| 1669 | test "zig fmt: float literal with exponent" { | 2001 | test "zig fmt: nested switch" { |
| 1670 | try testCanonical( | 2002 | try testCanonical( |
| 1671 | \\test "aoeu" { | 2003 | \\test { |
| 1672 | \\ switch (state) { | 2004 | \\ switch (state) { |
| 1673 | \\ TermState.Start => switch (c) { | 2005 | \\ TermState.Start => switch (c) { |
| 1674 | \\ '\x1b' => state = TermState.Escape, | 2006 | \\ '\x1b' => state = TermState.Escape, |
| ... | @@ -1679,6 +2011,7 @@ test "zig fmt: float literal with exponent" { | ... | @@ -1679,6 +2011,7 @@ test "zig fmt: float literal with exponent" { |
| 1679 | \\ | 2011 | \\ |
| 1680 | ); | 2012 | ); |
| 1681 | } | 2013 | } |
| 2014 | |||
| 1682 | test "zig fmt: float literal with exponent" { | 2015 | test "zig fmt: float literal with exponent" { |
| 1683 | try testCanonical( | 2016 | try testCanonical( |
| 1684 | \\pub const f64_true_min = 4.94065645841246544177e-324; | 2017 | \\pub const f64_true_min = 4.94065645841246544177e-324; |
| ... | @@ -2135,7 +2468,7 @@ test "zig fmt: preserve spacing" { | ... | @@ -2135,7 +2468,7 @@ test "zig fmt: preserve spacing" { |
| 2135 | test "zig fmt: return types" { | 2468 | test "zig fmt: return types" { |
| 2136 | try testCanonical( | 2469 | try testCanonical( |
| 2137 | \\pub fn main() !void {} | 2470 | \\pub fn main() !void {} |
| 2138 | \\pub fn main() anytype {} | 2471 | \\pub fn main() FooBar {} |
| 2139 | \\pub fn main() i32 {} | 2472 | \\pub fn main() i32 {} |
| 2140 | \\ | 2473 | \\ |
| 2141 | ); | 2474 | ); |
| ... | @@ -2207,6 +2540,33 @@ test "zig fmt: return" { | ... | @@ -2207,6 +2540,33 @@ test "zig fmt: return" { |
| 2207 | ); | 2540 | ); |
| 2208 | } | 2541 | } |
| 2209 | 2542 | ||
| 2543 | test "zig fmt: function attributes" { | ||
| 2544 | try testCanonical( | ||
| 2545 | \\export fn foo() void {} | ||
| 2546 | \\pub export fn foo() void {} | ||
| 2547 | \\extern fn foo() void; | ||
| 2548 | \\pub extern fn foo() void; | ||
| 2549 | \\extern "c" fn foo() void; | ||
| 2550 | \\pub extern "c" fn foo() void; | ||
| 2551 | \\noinline fn foo() void {} | ||
| 2552 | \\pub noinline fn foo() void {} | ||
| 2553 | \\ | ||
| 2554 | ); | ||
| 2555 | } | ||
| 2556 | |||
| 2557 | test "zig fmt: nested pointers with ** tokens" { | ||
| 2558 | try testCanonical( | ||
| 2559 | \\const x: *u32 = undefined; | ||
| 2560 | \\const x: **u32 = undefined; | ||
| 2561 | \\const x: ***u32 = undefined; | ||
| 2562 | \\const x: ****u32 = undefined; | ||
| 2563 | \\const x: *****u32 = undefined; | ||
| 2564 | \\const x: ******u32 = undefined; | ||
| 2565 | \\const x: *******u32 = undefined; | ||
| 2566 | \\ | ||
| 2567 | ); | ||
| 2568 | } | ||
| 2569 | |||
| 2210 | test "zig fmt: pointer attributes" { | 2570 | test "zig fmt: pointer attributes" { |
| 2211 | try testCanonical( | 2571 | try testCanonical( |
| 2212 | \\extern fn f1(s: *align(*u8) u8) c_int; | 2572 | \\extern fn f1(s: *align(*u8) u8) c_int; |
| ... | @@ -2220,11 +2580,11 @@ test "zig fmt: pointer attributes" { | ... | @@ -2220,11 +2580,11 @@ test "zig fmt: pointer attributes" { |
| 2220 | 2580 | ||
| 2221 | test "zig fmt: slice attributes" { | 2581 | test "zig fmt: slice attributes" { |
| 2222 | try testCanonical( | 2582 | try testCanonical( |
| 2223 | \\extern fn f1(s: *align(*u8) u8) c_int; | 2583 | \\extern fn f1(s: []align(*u8) u8) c_int; |
| 2224 | \\extern fn f2(s: **align(1) *const *volatile u8) c_int; | 2584 | \\extern fn f2(s: []align(1) []const []volatile u8) c_int; |
| 2225 | \\extern fn f3(s: *align(1) const *align(1) volatile *const volatile u8) c_int; | 2585 | \\extern fn f3(s: []align(1) const [:0]align(1) volatile []const volatile u8) c_int; |
| 2226 | \\extern fn f4(s: *align(1) const volatile u8) c_int; | 2586 | \\extern fn f4(s: []align(1) const volatile u8) c_int; |
| 2227 | \\extern fn f5(s: [*:0]align(1) const volatile u8) c_int; | 2587 | \\extern fn f5(s: [:0]align(1) const volatile u8) c_int; |
| 2228 | \\ | 2588 | \\ |
| 2229 | ); | 2589 | ); |
| 2230 | } | 2590 | } |
| ... | @@ -2241,7 +2601,7 @@ test "zig fmt: test declaration" { | ... | @@ -2241,7 +2601,7 @@ test "zig fmt: test declaration" { |
| 2241 | 2601 | ||
| 2242 | test "zig fmt: infix operators" { | 2602 | test "zig fmt: infix operators" { |
| 2243 | try testCanonical( | 2603 | try testCanonical( |
| 2244 | \\test "infix operators" { | 2604 | \\test { |
| 2245 | \\ var i = undefined; | 2605 | \\ var i = undefined; |
| 2246 | \\ i = 2; | 2606 | \\ i = 2; |
| 2247 | \\ i *= 2; | 2607 | \\ i *= 2; |
| ... | @@ -2345,7 +2705,7 @@ test "zig fmt: call expression" { | ... | @@ -2345,7 +2705,7 @@ test "zig fmt: call expression" { |
| 2345 | 2705 | ||
| 2346 | test "zig fmt: anytype type" { | 2706 | test "zig fmt: anytype type" { |
| 2347 | try testCanonical( | 2707 | try testCanonical( |
| 2348 | \\fn print(args: anytype) anytype {} | 2708 | \\fn print(args: anytype) @This() {} |
| 2349 | \\ | 2709 | \\ |
| 2350 | ); | 2710 | ); |
| 2351 | } | 2711 | } |
| ... | @@ -2570,7 +2930,11 @@ test "zig fmt: catch" { | ... | @@ -2570,7 +2930,11 @@ test "zig fmt: catch" { |
| 2570 | \\test "catch" { | 2930 | \\test "catch" { |
| 2571 | \\ const a: anyerror!u8 = 0; | 2931 | \\ const a: anyerror!u8 = 0; |
| 2572 | \\ _ = a catch return; | 2932 | \\ _ = a catch return; |
| 2933 | \\ _ = a catch | ||
| 2934 | \\ return; | ||
| 2573 | \\ _ = a catch |err| return; | 2935 | \\ _ = a catch |err| return; |
| 2936 | \\ _ = a catch |err| | ||
| 2937 | \\ return; | ||
| 2574 | \\} | 2938 | \\} |
| 2575 | \\ | 2939 | \\ |
| 2576 | ); | 2940 | ); |
| ... | @@ -2746,12 +3110,6 @@ test "zig fmt: for" { | ... | @@ -2746,12 +3110,6 @@ test "zig fmt: for" { |
| 2746 | \\ d => {}, | 3110 | \\ d => {}, |
| 2747 | \\ }; | 3111 | \\ }; |
| 2748 | \\ | 3112 | \\ |
| 2749 | \\ for (a) |b| | ||
| 2750 | \\ switch (b) { | ||
| 2751 | \\ c => {}, | ||
| 2752 | \\ d => {}, | ||
| 2753 | \\ }; | ||
| 2754 | \\ | ||
| 2755 | \\ const res = for (a) |v, i| { | 3113 | \\ const res = for (a) |v, i| { |
| 2756 | \\ break v; | 3114 | \\ break v; |
| 2757 | \\ } else { | 3115 | \\ } else { |
| ... | @@ -2777,7 +3135,8 @@ test "zig fmt: for" { | ... | @@ -2777,7 +3135,8 @@ test "zig fmt: for" { |
| 2777 | \\test "fix for" { | 3135 | \\test "fix for" { |
| 2778 | \\ for (a) |x| | 3136 | \\ for (a) |x| |
| 2779 | \\ f(x) | 3137 | \\ f(x) |
| 2780 | \\ else continue; | 3138 | \\ else |
| 3139 | \\ continue; | ||
| 2781 | \\} | 3140 | \\} |
| 2782 | \\ | 3141 | \\ |
| 2783 | ); | 3142 | ); |
| ... | @@ -2948,7 +3307,7 @@ test "zig fmt: nosuspend" { | ... | @@ -2948,7 +3307,7 @@ test "zig fmt: nosuspend" { |
| 2948 | 3307 | ||
| 2949 | test "zig fmt: Block after if" { | 3308 | test "zig fmt: Block after if" { |
| 2950 | try testCanonical( | 3309 | try testCanonical( |
| 2951 | \\test "Block after if" { | 3310 | \\test { |
| 2952 | \\ if (true) { | 3311 | \\ if (true) { |
| 2953 | \\ const a = 0; | 3312 | \\ const a = 0; |
| 2954 | \\ } | 3313 | \\ } |
| ... | @@ -2961,7 +3320,7 @@ test "zig fmt: Block after if" { | ... | @@ -2961,7 +3320,7 @@ test "zig fmt: Block after if" { |
| 2961 | ); | 3320 | ); |
| 2962 | } | 3321 | } |
| 2963 | 3322 | ||
| 2964 | test "zig fmt: use" { | 3323 | test "zig fmt: usingnamespace" { |
| 2965 | try testCanonical( | 3324 | try testCanonical( |
| 2966 | \\usingnamespace @import("std"); | 3325 | \\usingnamespace @import("std"); |
| 2967 | \\pub usingnamespace @import("std"); | 3326 | \\pub usingnamespace @import("std"); |
| ... | @@ -3025,10 +3384,7 @@ test "zig fmt: inline asm parameter alignment" { | ... | @@ -3025,10 +3384,7 @@ test "zig fmt: inline asm parameter alignment" { |
| 3025 | \\ asm volatile ( | 3384 | \\ asm volatile ( |
| 3026 | \\ \\ foo | 3385 | \\ \\ foo |
| 3027 | \\ \\ bar | 3386 | \\ \\ bar |
| 3028 | \\ : | 3387 | \\ ::: "", ""); |
| 3029 | \\ : | ||
| 3030 | \\ : "", "" | ||
| 3031 | \\ ); | ||
| 3032 | \\ asm volatile ( | 3388 | \\ asm volatile ( |
| 3033 | \\ \\ foo | 3389 | \\ \\ foo |
| 3034 | \\ \\ bar | 3390 | \\ \\ bar |
| ... | @@ -3087,16 +3443,12 @@ test "zig fmt: file ends with struct field" { | ... | @@ -3087,16 +3443,12 @@ test "zig fmt: file ends with struct field" { |
| 3087 | } | 3443 | } |
| 3088 | 3444 | ||
| 3089 | test "zig fmt: comment after empty comment" { | 3445 | test "zig fmt: comment after empty comment" { |
| 3090 | try testTransform( | 3446 | try testCanonical( |
| 3091 | \\const x = true; // | 3447 | \\const x = true; // |
| 3092 | \\// | 3448 | \\// |
| 3093 | \\// | 3449 | \\// |
| 3094 | \\//a | 3450 | \\//a |
| 3095 | \\ | 3451 | \\ |
| 3096 | , | ||
| 3097 | \\const x = true; | ||
| 3098 | \\//a | ||
| 3099 | \\ | ||
| 3100 | ); | 3452 | ); |
| 3101 | } | 3453 | } |
| 3102 | 3454 | ||
| ... | @@ -3113,7 +3465,8 @@ test "zig fmt: line comment in array" { | ... | @@ -3113,7 +3465,8 @@ test "zig fmt: line comment in array" { |
| 3113 | , | 3465 | , |
| 3114 | \\test "a" { | 3466 | \\test "a" { |
| 3115 | \\ var arr = [_]u32{ | 3467 | \\ var arr = [_]u32{ |
| 3116 | \\ 0, // 1, | 3468 | \\ 0, |
| 3469 | \\ // 1, | ||
| 3117 | \\ // 2, | 3470 | \\ // 2, |
| 3118 | \\ }; | 3471 | \\ }; |
| 3119 | \\} | 3472 | \\} |
| ... | @@ -3141,7 +3494,8 @@ test "zig fmt: comment after params" { | ... | @@ -3141,7 +3494,8 @@ test "zig fmt: comment after params" { |
| 3141 | \\ | 3494 | \\ |
| 3142 | , | 3495 | , |
| 3143 | \\fn a( | 3496 | \\fn a( |
| 3144 | \\ b: u32, // c: u32, | 3497 | \\ b: u32, |
| 3498 | \\ // c: u32, | ||
| 3145 | \\ // d: u32, | 3499 | \\ // d: u32, |
| 3146 | \\) void {} | 3500 | \\) void {} |
| 3147 | \\ | 3501 | \\ |
| ... | @@ -3174,13 +3528,17 @@ test "zig fmt: comment in array initializer/access" { | ... | @@ -3174,13 +3528,17 @@ test "zig fmt: comment in array initializer/access" { |
| 3174 | \\ var c = b[ //aa | 3528 | \\ var c = b[ //aa |
| 3175 | \\ 0 | 3529 | \\ 0 |
| 3176 | \\ ]; | 3530 | \\ ]; |
| 3177 | \\ var d = [_ | 3531 | \\ var d = [ |
| 3532 | \\ _ | ||
| 3178 | \\ //aa | 3533 | \\ //aa |
| 3534 | \\ : | ||
| 3535 | \\ 0 | ||
| 3179 | \\ ]x{ //aa | 3536 | \\ ]x{ //aa |
| 3180 | \\ //bb | 3537 | \\ //bb |
| 3181 | \\ 9, | 3538 | \\ 9, |
| 3182 | \\ }; | 3539 | \\ }; |
| 3183 | \\ var e = d[0 | 3540 | \\ var e = d[ |
| 3541 | \\ 0 | ||
| 3184 | \\ //aa | 3542 | \\ //aa |
| 3185 | \\ ]; | 3543 | \\ ]; |
| 3186 | \\} | 3544 | \\} |
| ... | @@ -3199,7 +3557,8 @@ test "zig fmt: comments at several places in struct init" { | ... | @@ -3199,7 +3557,8 @@ test "zig fmt: comments at several places in struct init" { |
| 3199 | , | 3557 | , |
| 3200 | \\var bar = Bar{ | 3558 | \\var bar = Bar{ |
| 3201 | \\ .x = 10, // test | 3559 | \\ .x = 10, // test |
| 3202 | \\ .y = "test", // test | 3560 | \\ .y = "test", |
| 3561 | \\ // test | ||
| 3203 | \\}; | 3562 | \\}; |
| 3204 | \\ | 3563 | \\ |
| 3205 | ); | 3564 | ); |
| ... | @@ -3214,7 +3573,7 @@ test "zig fmt: comments at several places in struct init" { | ... | @@ -3214,7 +3573,7 @@ test "zig fmt: comments at several places in struct init" { |
| 3214 | ); | 3573 | ); |
| 3215 | } | 3574 | } |
| 3216 | 3575 | ||
| 3217 | test "zig fmt: top level doc comments" { | 3576 | test "zig fmt: container doc comments" { |
| 3218 | try testCanonical( | 3577 | try testCanonical( |
| 3219 | \\//! tld 1 | 3578 | \\//! tld 1 |
| 3220 | \\//! tld 2 | 3579 | \\//! tld 2 |
| ... | @@ -3235,25 +3594,25 @@ test "zig fmt: top level doc comments" { | ... | @@ -3235,25 +3594,25 @@ test "zig fmt: top level doc comments" { |
| 3235 | \\ //! B tld 2 | 3594 | \\ //! B tld 2 |
| 3236 | \\ //! B tld 3 | 3595 | \\ //! B tld 3 |
| 3237 | \\ | 3596 | \\ |
| 3238 | \\ /// b doc | 3597 | \\ /// B doc |
| 3239 | \\ b: u32, | 3598 | \\ b: u32, |
| 3240 | \\}; | 3599 | \\}; |
| 3241 | \\ | 3600 | \\ |
| 3242 | \\/// C doc | 3601 | \\/// C doc |
| 3243 | \\const C = struct { | 3602 | \\const C = union(enum) { // comment |
| 3244 | \\ //! C tld 1 | 3603 | \\ //! C tld 1 |
| 3245 | \\ //! C tld 2 | 3604 | \\ //! C tld 2 |
| 3246 | \\ //! C tld 3 | 3605 | \\ //! C tld 3 |
| 3606 | \\}; | ||
| 3247 | \\ | 3607 | \\ |
| 3248 | \\ /// c1 doc | 3608 | \\/// D doc |
| 3249 | \\ c1: u32, | 3609 | \\const D = union(Foo) { |
| 3250 | \\ | 3610 | \\ //! D tld 1 |
| 3251 | \\ //! C tld 4 | 3611 | \\ //! D tld 2 |
| 3252 | \\ //! C tld 5 | 3612 | \\ //! D tld 3 |
| 3253 | \\ //! C tld 6 | ||
| 3254 | \\ | 3613 | \\ |
| 3255 | \\ /// c2 doc | 3614 | \\ /// D doc |
| 3256 | \\ c2: u32, | 3615 | \\ b: u32, |
| 3257 | \\}; | 3616 | \\}; |
| 3258 | \\ | 3617 | \\ |
| 3259 | ); | 3618 | ); |
| ... | @@ -3275,8 +3634,31 @@ test "zig fmt: extern without container keyword returns error" { | ... | @@ -3275,8 +3634,31 @@ test "zig fmt: extern without container keyword returns error" { |
| 3275 | \\const container = extern {}; | 3634 | \\const container = extern {}; |
| 3276 | \\ | 3635 | \\ |
| 3277 | , &[_]Error{ | 3636 | , &[_]Error{ |
| 3278 | .ExpectedExpr, | 3637 | .expected_container, |
| 3279 | .ExpectedVarDeclOrFn, | 3638 | }); |
| 3639 | } | ||
| 3640 | |||
| 3641 | test "zig fmt: same line doc comment returns error" { | ||
| 3642 | try testError( | ||
| 3643 | \\const Foo = struct{ | ||
| 3644 | \\ bar: u32, /// comment | ||
| 3645 | \\ foo: u32, /// comment | ||
| 3646 | \\ /// commment | ||
| 3647 | \\}; | ||
| 3648 | \\ | ||
| 3649 | \\const a = 42; /// comment | ||
| 3650 | \\ | ||
| 3651 | \\extern fn foo() void; /// comment | ||
| 3652 | \\ | ||
| 3653 | \\/// comment | ||
| 3654 | \\ | ||
| 3655 | , &[_]Error{ | ||
| 3656 | .same_line_doc_comment, | ||
| 3657 | .same_line_doc_comment, | ||
| 3658 | .unattached_doc_comment, | ||
| 3659 | .same_line_doc_comment, | ||
| 3660 | .same_line_doc_comment, | ||
| 3661 | .unattached_doc_comment, | ||
| 3280 | }); | 3662 | }); |
| 3281 | } | 3663 | } |
| 3282 | 3664 | ||
| ... | @@ -3350,26 +3732,6 @@ test "zig fmt: hexadeciaml float literals with underscore separators" { | ... | @@ -3350,26 +3732,6 @@ test "zig fmt: hexadeciaml float literals with underscore separators" { |
| 3350 | ); | 3732 | ); |
| 3351 | } | 3733 | } |
| 3352 | 3734 | ||
| 3353 | test "zig fmt: convert async fn into callconv(.Async)" { | ||
| 3354 | try testTransform( | ||
| 3355 | \\async fn foo() void {} | ||
| 3356 | , | ||
| 3357 | \\fn foo() callconv(.Async) void {} | ||
| 3358 | \\ | ||
| 3359 | ); | ||
| 3360 | } | ||
| 3361 | |||
| 3362 | test "zig fmt: convert extern fn proto into callconv(.C)" { | ||
| 3363 | try testTransform( | ||
| 3364 | \\extern fn foo0() void {} | ||
| 3365 | \\const foo1 = extern fn () void; | ||
| 3366 | , | ||
| 3367 | \\extern fn foo0() void {} | ||
| 3368 | \\const foo1 = fn () callconv(.C) void; | ||
| 3369 | \\ | ||
| 3370 | ); | ||
| 3371 | } | ||
| 3372 | |||
| 3373 | test "zig fmt: C var args" { | 3735 | test "zig fmt: C var args" { |
| 3374 | try testCanonical( | 3736 | try testCanonical( |
| 3375 | \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; | 3737 | \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; |
| ... | @@ -3458,6 +3820,54 @@ test "zig fmt: test comments in field access chain" { | ... | @@ -3458,6 +3820,54 @@ test "zig fmt: test comments in field access chain" { |
| 3458 | ); | 3820 | ); |
| 3459 | } | 3821 | } |
| 3460 | 3822 | ||
| 3823 | test "zig fmt: allow line break before field access" { | ||
| 3824 | try testCanonical( | ||
| 3825 | \\test { | ||
| 3826 | \\ const w = foo.bar().zippy(zag).iguessthisisok(); | ||
| 3827 | \\ | ||
| 3828 | \\ const x = foo | ||
| 3829 | \\ .bar() | ||
| 3830 | \\ . // comment | ||
| 3831 | \\ // comment | ||
| 3832 | \\ swooop().zippy(zag) | ||
| 3833 | \\ .iguessthisisok(); | ||
| 3834 | \\ | ||
| 3835 | \\ const y = view.output.root.server.input_manager.default_seat.wlr_seat.name; | ||
| 3836 | \\ | ||
| 3837 | \\ const z = view.output.root.server | ||
| 3838 | \\ .input_manager // | ||
| 3839 | \\ .default_seat | ||
| 3840 | \\ . // comment | ||
| 3841 | \\ // another comment | ||
| 3842 | \\ wlr_seat.name; | ||
| 3843 | \\} | ||
| 3844 | \\ | ||
| 3845 | ); | ||
| 3846 | try testTransform( | ||
| 3847 | \\test { | ||
| 3848 | \\ const x = foo. | ||
| 3849 | \\ bar() | ||
| 3850 | \\ .zippy(zag).iguessthisisok(); | ||
| 3851 | \\ | ||
| 3852 | \\ const z = view.output.root.server. | ||
| 3853 | \\ input_manager. | ||
| 3854 | \\ default_seat.wlr_seat.name; | ||
| 3855 | \\} | ||
| 3856 | \\ | ||
| 3857 | , | ||
| 3858 | \\test { | ||
| 3859 | \\ const x = foo | ||
| 3860 | \\ .bar() | ||
| 3861 | \\ .zippy(zag).iguessthisisok(); | ||
| 3862 | \\ | ||
| 3863 | \\ const z = view.output.root.server | ||
| 3864 | \\ .input_manager | ||
| 3865 | \\ .default_seat.wlr_seat.name; | ||
| 3866 | \\} | ||
| 3867 | \\ | ||
| 3868 | ); | ||
| 3869 | } | ||
| 3870 | |||
| 3461 | test "zig fmt: Indent comma correctly after multiline string literals in arg list (trailing comma)" { | 3871 | test "zig fmt: Indent comma correctly after multiline string literals in arg list (trailing comma)" { |
| 3462 | try testCanonical( | 3872 | try testCanonical( |
| 3463 | \\fn foo() void { | 3873 | \\fn foo() void { |
| ... | @@ -3495,8 +3905,7 @@ test "zig fmt: Control flow statement as body of blockless if" { | ... | @@ -3495,8 +3905,7 @@ test "zig fmt: Control flow statement as body of blockless if" { |
| 3495 | \\ | 3905 | \\ |
| 3496 | \\ const zoom_node = if (focused_node == layout_first) while (it.next()) |node| { | 3906 | \\ const zoom_node = if (focused_node == layout_first) while (it.next()) |node| { |
| 3497 | \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node; | 3907 | \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node; |
| 3498 | \\ } else null else | 3908 | \\ } else null else focused_node; |
| 3499 | \\ focused_node; | ||
| 3500 | \\ | 3909 | \\ |
| 3501 | \\ const zoom_node = if (focused_node == layout_first) | 3910 | \\ const zoom_node = if (focused_node == layout_first) |
| 3502 | \\ if (it.next()) { | 3911 | \\ if (it.next()) { |
| ... | @@ -3513,14 +3922,13 @@ test "zig fmt: Control flow statement as body of blockless if" { | ... | @@ -3513,14 +3922,13 @@ test "zig fmt: Control flow statement as body of blockless if" { |
| 3513 | \\ | 3922 | \\ |
| 3514 | \\ const zoom_node = if (focused_node == layout_first) switch (nodes) { | 3923 | \\ const zoom_node = if (focused_node == layout_first) switch (nodes) { |
| 3515 | \\ 0 => 0, | 3924 | \\ 0 => 0, |
| 3516 | \\ } else | 3925 | \\ } else focused_node; |
| 3517 | \\ focused_node; | ||
| 3518 | \\} | 3926 | \\} |
| 3519 | \\ | 3927 | \\ |
| 3520 | ); | 3928 | ); |
| 3521 | } | 3929 | } |
| 3522 | 3930 | ||
| 3523 | test "zig fmt: " { | 3931 | test "zig fmt: regression test for #5722" { |
| 3524 | try testCanonical( | 3932 | try testCanonical( |
| 3525 | \\pub fn sendViewTags(self: Self) void { | 3933 | \\pub fn sendViewTags(self: Self) void { |
| 3526 | \\ var it = ViewStack(View).iterator(self.output.views.first, std.math.maxInt(u32)); | 3934 | \\ var it = ViewStack(View).iterator(self.output.views.first, std.math.maxInt(u32)); |
| ... | @@ -3580,8 +3988,8 @@ test "zig fmt: multiline string literals should play nice with array initializer | ... | @@ -3580,8 +3988,8 @@ test "zig fmt: multiline string literals should play nice with array initializer |
| 3580 | \\ 0, | 3988 | \\ 0, |
| 3581 | \\ }}}}}}}}; | 3989 | \\ }}}}}}}}; |
| 3582 | \\ myFunc(.{ | 3990 | \\ myFunc(.{ |
| 3583 | \\ "aaaaaaa", "bbbbbb", "ccccc", | 3991 | \\ "aaaaaaa", "bbbbbb", "ccccc", |
| 3584 | \\ "dddd", ("eee"), ("fff"), | 3992 | \\ "dddd", ("eee"), ("fff"), |
| 3585 | \\ ("gggg"), | 3993 | \\ ("gggg"), |
| 3586 | \\ // Line comment | 3994 | \\ // Line comment |
| 3587 | \\ \\Multiline String Literals can be quite long | 3995 | \\ \\Multiline String Literals can be quite long |
| ... | @@ -3610,9 +4018,11 @@ test "zig fmt: multiline string literals should play nice with array initializer | ... | @@ -3610,9 +4018,11 @@ test "zig fmt: multiline string literals should play nice with array initializer |
| 3610 | \\ ( | 4018 | \\ ( |
| 3611 | \\ \\ xxx | 4019 | \\ \\ xxx |
| 3612 | \\ ), | 4020 | \\ ), |
| 3613 | \\ "xxx", "xxx", | 4021 | \\ "xxx", |
| 4022 | \\ "xxx", | ||
| 3614 | \\ }, | 4023 | \\ }, |
| 3615 | \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, .{ "xxxxxxx", "xxx", "xxx", "xxx" }, | 4024 | \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, |
| 4025 | \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, | ||
| 3616 | \\ "aaaaaaa", "bbbbbb", "ccccc", // - | 4026 | \\ "aaaaaaa", "bbbbbb", "ccccc", // - |
| 3617 | \\ "dddd", ("eee"), ("fff"), | 4027 | \\ "dddd", ("eee"), ("fff"), |
| 3618 | \\ .{ | 4028 | \\ .{ |
| ... | @@ -3620,7 +4030,8 @@ test "zig fmt: multiline string literals should play nice with array initializer | ... | @@ -3620,7 +4030,8 @@ test "zig fmt: multiline string literals should play nice with array initializer |
| 3620 | \\ ( | 4030 | \\ ( |
| 3621 | \\ \\ xxx | 4031 | \\ \\ xxx |
| 3622 | \\ ), | 4032 | \\ ), |
| 3623 | \\ "xxxxxxxxxxxxxx", "xxx", | 4033 | \\ "xxxxxxxxxxxxxx", |
| 4034 | \\ "xxx", | ||
| 3624 | \\ }, | 4035 | \\ }, |
| 3625 | \\ .{ | 4036 | \\ .{ |
| 3626 | \\ ( | 4037 | \\ ( |
| ... | @@ -3636,10 +4047,10 @@ test "zig fmt: multiline string literals should play nice with array initializer | ... | @@ -3636,10 +4047,10 @@ test "zig fmt: multiline string literals should play nice with array initializer |
| 3636 | ); | 4047 | ); |
| 3637 | } | 4048 | } |
| 3638 | 4049 | ||
| 3639 | test "zig fmt: use of comments and Multiline string literals may force the parameters over multiple lines" { | 4050 | test "zig fmt: use of comments and multiline string literals may force the parameters over multiple lines" { |
| 3640 | try testCanonical( | 4051 | try testCanonical( |
| 3641 | \\pub fn makeMemUndefined(qzz: []u8) i1 { | 4052 | \\pub fn makeMemUndefined(qzz: []u8) i1 { |
| 3642 | \\ cases.add( // fixed bug #2032 | 4053 | \\ cases.add( // fixed bug foo |
| 3643 | \\ "compile diagnostic string for top level decl type", | 4054 | \\ "compile diagnostic string for top level decl type", |
| 3644 | \\ \\export fn entry() void { | 4055 | \\ \\export fn entry() void { |
| 3645 | \\ \\ var foo: u32 = @This(){}; | 4056 | \\ \\ var foo: u32 = @This(){}; |
| ... | @@ -3657,72 +4068,474 @@ test "zig fmt: use of comments and Multiline string literals may force the param | ... | @@ -3657,72 +4068,474 @@ test "zig fmt: use of comments and Multiline string literals may force the param |
| 3657 | \\ .MakeMemUndefined, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0)); | 4068 | \\ .MakeMemUndefined, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0)); |
| 3658 | \\} | 4069 | \\} |
| 3659 | \\ | 4070 | \\ |
| 3660 | \\// This looks like garbage don't do this | 4071 | \\// This looks like garbage don't do this |
| 3661 | \\const rparen = tree.prevToken( | 4072 | \\const rparen = tree.prevToken( |
| 3662 | \\// the first token for the annotation expressions is the left | 4073 | \\// the first token for the annotation expressions is the left |
| 3663 | \\// parenthesis, hence the need for two prevToken | 4074 | \\// parenthesis, hence the need for two prevToken |
| 3664 | \\ if (fn_proto.getAlignExpr()) |align_expr| | 4075 | \\if (fn_proto.getAlignExpr()) |align_expr| |
| 3665 | \\ tree.prevToken(tree.prevToken(align_expr.firstToken())) | 4076 | \\ tree.prevToken(tree.prevToken(align_expr.firstToken())) |
| 3666 | \\else if (fn_proto.getSectionExpr()) |section_expr| | 4077 | \\else if (fn_proto.getSectionExpr()) |section_expr| |
| 3667 | \\ tree.prevToken(tree.prevToken(section_expr.firstToken())) | 4078 | \\ tree.prevToken(tree.prevToken(section_expr.firstToken())) |
| 3668 | \\else if (fn_proto.getCallconvExpr()) |callconv_expr| | 4079 | \\else if (fn_proto.getCallconvExpr()) |callconv_expr| |
| 3669 | \\ tree.prevToken(tree.prevToken(callconv_expr.firstToken())) | 4080 | \\ tree.prevToken(tree.prevToken(callconv_expr.firstToken())) |
| 3670 | \\else switch (fn_proto.return_type) { | 4081 | \\else switch (fn_proto.return_type) { |
| 3671 | \\ .Explicit => |node| node.firstToken(), | 4082 | \\ .Explicit => |node| node.firstToken(), |
| 3672 | \\ .InferErrorSet => |node| tree.prevToken(node.firstToken()), | 4083 | \\ .InferErrorSet => |node| tree.prevToken(node.firstToken()), |
| 3673 | \\ .Invalid => unreachable, | 4084 | \\ .Invalid => unreachable, |
| 3674 | \\}); | 4085 | \\}); |
| 4086 | \\ | ||
| 4087 | ); | ||
| 4088 | } | ||
| 4089 | |||
| 4090 | test "zig fmt: single argument trailing commas in @builtins()" { | ||
| 4091 | try testCanonical( | ||
| 4092 | \\pub fn foo(qzz: []u8) i1 { | ||
| 4093 | \\ @panic( | ||
| 4094 | \\ foo, | ||
| 4095 | \\ ); | ||
| 4096 | \\ panic( | ||
| 4097 | \\ foo, | ||
| 4098 | \\ ); | ||
| 4099 | \\ @panic( | ||
| 4100 | \\ foo, | ||
| 4101 | \\ bar, | ||
| 4102 | \\ ); | ||
| 4103 | \\} | ||
| 4104 | \\ | ||
| 4105 | ); | ||
| 4106 | } | ||
| 4107 | |||
| 4108 | test "zig fmt: trailing comma should force multiline 1 column" { | ||
| 4109 | try testTransform( | ||
| 4110 | \\pub const UUID_NULL: uuid_t = [16]u8{0,0,0,0,}; | ||
| 4111 | \\ | ||
| 4112 | , | ||
| 4113 | \\pub const UUID_NULL: uuid_t = [16]u8{ | ||
| 4114 | \\ 0, | ||
| 4115 | \\ 0, | ||
| 4116 | \\ 0, | ||
| 4117 | \\ 0, | ||
| 4118 | \\}; | ||
| 4119 | \\ | ||
| 4120 | ); | ||
| 4121 | } | ||
| 4122 | |||
| 4123 | test "zig fmt: function params should align nicely" { | ||
| 4124 | try testCanonical( | ||
| 4125 | \\pub fn foo() void { | ||
| 4126 | \\ cases.addRuntimeSafety("slicing operator with sentinel", | ||
| 4127 | \\ \\const std = @import("std"); | ||
| 4128 | \\ ++ check_panic_msg ++ | ||
| 4129 | \\ \\pub fn main() void { | ||
| 4130 | \\ \\ var buf = [4]u8{'a','b','c',0}; | ||
| 4131 | \\ \\ const slice = buf[0..:0]; | ||
| 4132 | \\ \\} | ||
| 4133 | \\ ); | ||
| 4134 | \\} | ||
| 4135 | \\ | ||
| 4136 | ); | ||
| 4137 | } | ||
| 4138 | |||
| 4139 | test "zig fmt: fn proto end with anytype and comma" { | ||
| 4140 | try testCanonical( | ||
| 4141 | \\pub fn format( | ||
| 4142 | \\ out_stream: anytype, | ||
| 4143 | \\) !void {} | ||
| 4144 | \\ | ||
| 4145 | ); | ||
| 4146 | } | ||
| 4147 | |||
| 4148 | test "zig fmt: space after top level doc comment" { | ||
| 4149 | try testCanonical( | ||
| 4150 | \\//! top level doc comment | ||
| 4151 | \\ | ||
| 4152 | \\field: i32, | ||
| 4153 | \\ | ||
| 4154 | ); | ||
| 4155 | } | ||
| 4156 | |||
| 4157 | test "zig fmt: for loop with ptr payload and index" { | ||
| 4158 | try testCanonical( | ||
| 4159 | \\test { | ||
| 4160 | \\ for (self.entries.items) |*item, i| {} | ||
| 4161 | \\ for (self.entries.items) |*item, i| | ||
| 4162 | \\ a = b; | ||
| 4163 | \\ for (self.entries.items) |*item, i| a = b; | ||
| 4164 | \\} | ||
| 4165 | \\ | ||
| 4166 | ); | ||
| 4167 | } | ||
| 4168 | |||
| 4169 | test "zig fmt: proper indent line comment after multi-line single expr while loop" { | ||
| 4170 | try testCanonical( | ||
| 4171 | \\test { | ||
| 4172 | \\ while (a) : (b) | ||
| 4173 | \\ foo(); | ||
| 4174 | \\ | ||
| 4175 | \\ // bar | ||
| 4176 | \\ baz(); | ||
| 4177 | \\} | ||
| 4178 | \\ | ||
| 4179 | ); | ||
| 4180 | } | ||
| 4181 | |||
| 4182 | test "zig fmt: function with labeled block as return type" { | ||
| 4183 | try testCanonical( | ||
| 4184 | \\fn foo() t: { | ||
| 4185 | \\ break :t bar; | ||
| 4186 | \\} { | ||
| 4187 | \\ baz(); | ||
| 4188 | \\} | ||
| 4189 | \\ | ||
| 4190 | ); | ||
| 4191 | } | ||
| 4192 | |||
| 4193 | test "zig fmt: line comment after multiline single expr if statement with multiline string" { | ||
| 4194 | try testCanonical( | ||
| 4195 | \\test { | ||
| 4196 | \\ if (foo) | ||
| 4197 | \\ x = | ||
| 4198 | \\ \\hello | ||
| 4199 | \\ \\hello | ||
| 4200 | \\ \\ | ||
| 4201 | \\ ; | ||
| 4202 | \\ | ||
| 4203 | \\ // bar | ||
| 4204 | \\ baz(); | ||
| 4205 | \\ | ||
| 4206 | \\ if (foo) | ||
| 4207 | \\ x = | ||
| 4208 | \\ \\hello | ||
| 4209 | \\ \\hello | ||
| 4210 | \\ \\ | ||
| 4211 | \\ else | ||
| 4212 | \\ y = | ||
| 4213 | \\ \\hello | ||
| 4214 | \\ \\hello | ||
| 4215 | \\ \\ | ||
| 4216 | \\ ; | ||
| 4217 | \\ | ||
| 4218 | \\ // bar | ||
| 4219 | \\ baz(); | ||
| 4220 | \\} | ||
| 4221 | \\ | ||
| 4222 | ); | ||
| 4223 | } | ||
| 4224 | |||
| 4225 | test "zig fmt: respect extra newline between fn and pub usingnamespace" { | ||
| 4226 | try testCanonical( | ||
| 4227 | \\fn foo() void { | ||
| 4228 | \\ bar(); | ||
| 4229 | \\} | ||
| 4230 | \\ | ||
| 4231 | \\pub usingnamespace baz; | ||
| 3675 | \\ | 4232 | \\ |
| 3676 | ); | 4233 | ); |
| 3677 | } | 4234 | } |
| 3678 | 4235 | ||
| 3679 | test "zig fmt: single argument trailing commas in @builtins()" { | 4236 | test "zig fmt: respect extra newline between switch items" { |
| 3680 | try testCanonical( | 4237 | try testCanonical( |
| 3681 | \\pub fn foo(qzz: []u8) i1 { | 4238 | \\const a = switch (b) { |
| 3682 | \\ @panic( | 4239 | \\ .c => {}, |
| 3683 | \\ foo, | 4240 | \\ |
| 3684 | \\ ); | 4241 | \\ .d, |
| 3685 | \\ panic( | 4242 | \\ .e, |
| 3686 | \\ foo, | 4243 | \\ => f, |
| 3687 | \\ ); | 4244 | \\}; |
| 3688 | \\ @panic( | ||
| 3689 | \\ foo, | ||
| 3690 | \\ bar, | ||
| 3691 | \\ ); | ||
| 3692 | \\} | ||
| 3693 | \\ | 4245 | \\ |
| 3694 | ); | 4246 | ); |
| 3695 | } | 4247 | } |
| 3696 | 4248 | ||
| 3697 | test "zig fmt: trailing comma should force multiline 1 column" { | 4249 | test "zig fmt: insert trailing comma if there are comments between switch values" { |
| 3698 | try testTransform( | 4250 | try testTransform( |
| 3699 | \\pub const UUID_NULL: uuid_t = [16]u8{0,0,0,0,}; | 4251 | \\const a = switch (b) { |
| 4252 | \\ .c => {}, | ||
| 4253 | \\ | ||
| 4254 | \\ .d, // foobar | ||
| 4255 | \\ .e | ||
| 4256 | \\ => f, | ||
| 4257 | \\ | ||
| 4258 | \\ .g, .h | ||
| 4259 | \\ // comment | ||
| 4260 | \\ => i, | ||
| 4261 | \\}; | ||
| 3700 | \\ | 4262 | \\ |
| 3701 | , | 4263 | , |
| 3702 | \\pub const UUID_NULL: uuid_t = [16]u8{ | 4264 | \\const a = switch (b) { |
| 3703 | \\ 0, | 4265 | \\ .c => {}, |
| 3704 | \\ 0, | 4266 | \\ |
| 3705 | \\ 0, | 4267 | \\ .d, // foobar |
| 3706 | \\ 0, | 4268 | \\ .e, |
| 4269 | \\ => f, | ||
| 4270 | \\ | ||
| 4271 | \\ .g, | ||
| 4272 | \\ .h, | ||
| 4273 | \\ // comment | ||
| 4274 | \\ => i, | ||
| 3707 | \\}; | 4275 | \\}; |
| 3708 | \\ | 4276 | \\ |
| 3709 | ); | 4277 | ); |
| 3710 | } | 4278 | } |
| 3711 | 4279 | ||
| 3712 | test "zig fmt: function params should align nicely" { | 4280 | test "zig fmt: error for invalid bit range" { |
| 3713 | try testCanonical( | 4281 | try testError( |
| 3714 | \\pub fn foo() void { | 4282 | \\var x: []align(0:0:0)u8 = bar; |
| 3715 | \\ cases.addRuntimeSafety("slicing operator with sentinel", | 4283 | , &[_]Error{ |
| 3716 | \\ \\const std = @import("std"); | 4284 | .invalid_bit_range, |
| 3717 | \\ ++ check_panic_msg ++ | 4285 | }); |
| 3718 | \\ \\pub fn main() void { | 4286 | } |
| 3719 | \\ \\ var buf = [4]u8{'a','b','c',0}; | 4287 | |
| 3720 | \\ \\ const slice = buf[0..:0]; | 4288 | test "zig fmt: error for invalid align" { |
| 3721 | \\ \\} | 4289 | try testError( |
| 3722 | \\ ); | 4290 | \\var x: [10]align(10)u8 = bar; |
| 4291 | , &[_]Error{ | ||
| 4292 | .invalid_align, | ||
| 4293 | }); | ||
| 4294 | } | ||
| 4295 | |||
| 4296 | test "recovery: top level" { | ||
| 4297 | try testError( | ||
| 4298 | \\test "" {inline} | ||
| 4299 | \\test "" {inline} | ||
| 4300 | , &[_]Error{ | ||
| 4301 | .expected_inlinable, | ||
| 4302 | .expected_inlinable, | ||
| 4303 | }); | ||
| 4304 | } | ||
| 4305 | |||
| 4306 | test "recovery: block statements" { | ||
| 4307 | try testError( | ||
| 4308 | \\test "" { | ||
| 4309 | \\ foo + +; | ||
| 4310 | \\ inline; | ||
| 3723 | \\} | 4311 | \\} |
| 3724 | \\ | 4312 | , &[_]Error{ |
| 3725 | ); | 4313 | .invalid_token, |
| 4314 | .expected_inlinable, | ||
| 4315 | }); | ||
| 4316 | } | ||
| 4317 | |||
| 4318 | test "recovery: missing comma" { | ||
| 4319 | try testError( | ||
| 4320 | \\test "" { | ||
| 4321 | \\ switch (foo) { | ||
| 4322 | \\ 2 => {} | ||
| 4323 | \\ 3 => {} | ||
| 4324 | \\ else => { | ||
| 4325 | \\ foo && bar +; | ||
| 4326 | \\ } | ||
| 4327 | \\ } | ||
| 4328 | \\} | ||
| 4329 | , &[_]Error{ | ||
| 4330 | .expected_token, | ||
| 4331 | .expected_token, | ||
| 4332 | .invalid_and, | ||
| 4333 | .invalid_token, | ||
| 4334 | }); | ||
| 4335 | } | ||
| 4336 | |||
| 4337 | test "recovery: extra qualifier" { | ||
| 4338 | try testError( | ||
| 4339 | \\const a: *const const u8; | ||
| 4340 | \\test "" | ||
| 4341 | , &[_]Error{ | ||
| 4342 | .extra_const_qualifier, | ||
| 4343 | .expected_block, | ||
| 4344 | }); | ||
| 4345 | } | ||
| 4346 | |||
| 4347 | test "recovery: missing return type" { | ||
| 4348 | try testError( | ||
| 4349 | \\fn foo() { | ||
| 4350 | \\ a && b; | ||
| 4351 | \\} | ||
| 4352 | \\test "" | ||
| 4353 | , &[_]Error{ | ||
| 4354 | .expected_return_type, | ||
| 4355 | .invalid_and, | ||
| 4356 | .expected_block, | ||
| 4357 | }); | ||
| 4358 | } | ||
| 4359 | |||
| 4360 | test "recovery: continue after invalid decl" { | ||
| 4361 | try testError( | ||
| 4362 | \\fn foo { | ||
| 4363 | \\ inline; | ||
| 4364 | \\} | ||
| 4365 | \\pub test "" { | ||
| 4366 | \\ async a && b; | ||
| 4367 | \\} | ||
| 4368 | , &[_]Error{ | ||
| 4369 | .expected_token, | ||
| 4370 | .expected_pub_item, | ||
| 4371 | .expected_param_list, | ||
| 4372 | .invalid_and, | ||
| 4373 | }); | ||
| 4374 | try testError( | ||
| 4375 | \\threadlocal test "" { | ||
| 4376 | \\ @a && b; | ||
| 4377 | \\} | ||
| 4378 | , &[_]Error{ | ||
| 4379 | .expected_var_decl, | ||
| 4380 | .expected_param_list, | ||
| 4381 | .invalid_and, | ||
| 4382 | }); | ||
| 4383 | } | ||
| 4384 | |||
| 4385 | test "recovery: invalid extern/inline" { | ||
| 4386 | try testError( | ||
| 4387 | \\inline test "" { a && b; } | ||
| 4388 | , &[_]Error{ | ||
| 4389 | .expected_fn, | ||
| 4390 | .invalid_and, | ||
| 4391 | }); | ||
| 4392 | try testError( | ||
| 4393 | \\extern "" test "" { a && b; } | ||
| 4394 | , &[_]Error{ | ||
| 4395 | .expected_var_decl_or_fn, | ||
| 4396 | .invalid_and, | ||
| 4397 | }); | ||
| 4398 | } | ||
| 4399 | |||
| 4400 | test "recovery: missing semicolon" { | ||
| 4401 | try testError( | ||
| 4402 | \\test "" { | ||
| 4403 | \\ comptime a && b | ||
| 4404 | \\ c && d | ||
| 4405 | \\ @foo | ||
| 4406 | \\} | ||
| 4407 | , &[_]Error{ | ||
| 4408 | .invalid_and, | ||
| 4409 | .expected_token, | ||
| 4410 | .invalid_and, | ||
| 4411 | .expected_token, | ||
| 4412 | .expected_param_list, | ||
| 4413 | .expected_token, | ||
| 4414 | }); | ||
| 4415 | } | ||
| 4416 | |||
| 4417 | test "recovery: invalid container members" { | ||
| 4418 | try testError( | ||
| 4419 | \\usingnamespace; | ||
| 4420 | \\foo+ | ||
| 4421 | \\bar@, | ||
| 4422 | \\while (a == 2) { test "" {}} | ||
| 4423 | \\test "" { | ||
| 4424 | \\ a && b | ||
| 4425 | \\} | ||
| 4426 | , &[_]Error{ | ||
| 4427 | .expected_expr, | ||
| 4428 | .expected_token, | ||
| 4429 | .expected_container_members, | ||
| 4430 | .invalid_and, | ||
| 4431 | .expected_token, | ||
| 4432 | }); | ||
| 4433 | } | ||
| 4434 | |||
| 4435 | // TODO after https://github.com/ziglang/zig/issues/35 is implemented, | ||
| 4436 | // we should be able to recover from this *at any indentation level*, | ||
| 4437 | // reporting a parse error and yet also parsing all the decls even | ||
| 4438 | // inside structs. | ||
| 4439 | test "recovery: extra '}' at top level" { | ||
| 4440 | try testError( | ||
| 4441 | \\}}} | ||
| 4442 | \\test "" { | ||
| 4443 | \\ a && b; | ||
| 4444 | \\} | ||
| 4445 | , &[_]Error{ | ||
| 4446 | .expected_token, | ||
| 4447 | }); | ||
| 4448 | } | ||
| 4449 | |||
| 4450 | test "recovery: mismatched bracket at top level" { | ||
| 4451 | try testError( | ||
| 4452 | \\const S = struct { | ||
| 4453 | \\ arr: 128]?G | ||
| 4454 | \\}; | ||
| 4455 | , &[_]Error{ | ||
| 4456 | .expected_token, | ||
| 4457 | }); | ||
| 4458 | } | ||
| 4459 | |||
| 4460 | test "recovery: invalid global error set access" { | ||
| 4461 | try testError( | ||
| 4462 | \\test "" { | ||
| 4463 | \\ error && foo; | ||
| 4464 | \\} | ||
| 4465 | , &[_]Error{ | ||
| 4466 | .expected_token, | ||
| 4467 | .expected_token, | ||
| 4468 | .invalid_and, | ||
| 4469 | }); | ||
| 4470 | } | ||
| 4471 | |||
| 4472 | test "recovery: invalid asterisk after pointer dereference" { | ||
| 4473 | try testError( | ||
| 4474 | \\test "" { | ||
| 4475 | \\ var sequence = "repeat".*** 10; | ||
| 4476 | \\} | ||
| 4477 | , &[_]Error{ | ||
| 4478 | .asterisk_after_ptr_deref, | ||
| 4479 | }); | ||
| 4480 | try testError( | ||
| 4481 | \\test "" { | ||
| 4482 | \\ var sequence = "repeat".** 10&&a; | ||
| 4483 | \\} | ||
| 4484 | , &[_]Error{ | ||
| 4485 | .asterisk_after_ptr_deref, | ||
| 4486 | .invalid_and, | ||
| 4487 | }); | ||
| 4488 | } | ||
| 4489 | |||
| 4490 | test "recovery: missing semicolon after if, for, while stmt" { | ||
| 4491 | try testError( | ||
| 4492 | \\test "" { | ||
| 4493 | \\ if (foo) bar | ||
| 4494 | \\ for (foo) |a| bar | ||
| 4495 | \\ while (foo) bar | ||
| 4496 | \\ a && b; | ||
| 4497 | \\} | ||
| 4498 | , &[_]Error{ | ||
| 4499 | .expected_semi_or_else, | ||
| 4500 | .expected_semi_or_else, | ||
| 4501 | .expected_semi_or_else, | ||
| 4502 | .invalid_and, | ||
| 4503 | }); | ||
| 4504 | } | ||
| 4505 | |||
| 4506 | test "recovery: invalid comptime" { | ||
| 4507 | try testError( | ||
| 4508 | \\comptime | ||
| 4509 | , &[_]Error{ | ||
| 4510 | .expected_block_or_field, | ||
| 4511 | }); | ||
| 4512 | } | ||
| 4513 | |||
| 4514 | test "recovery: missing block after for/while loops" { | ||
| 4515 | try testError( | ||
| 4516 | \\test "" { while (foo) } | ||
| 4517 | , &[_]Error{ | ||
| 4518 | .expected_block_or_assignment, | ||
| 4519 | }); | ||
| 4520 | try testError( | ||
| 4521 | \\test "" { for (foo) |bar| } | ||
| 4522 | , &[_]Error{ | ||
| 4523 | .expected_block_or_assignment, | ||
| 4524 | }); | ||
| 4525 | } | ||
| 4526 | |||
| 4527 | test "recovery: missing for payload" { | ||
| 4528 | try testError( | ||
| 4529 | \\comptime { | ||
| 4530 | \\ const a = for(a) {}; | ||
| 4531 | \\ const a: for(a) {}; | ||
| 4532 | \\ for(a) {} | ||
| 4533 | \\} | ||
| 4534 | , &[_]Error{ | ||
| 4535 | .expected_loop_payload, | ||
| 4536 | .expected_loop_payload, | ||
| 4537 | .expected_loop_payload, | ||
| 4538 | }); | ||
| 3726 | } | 4539 | } |
| 3727 | 4540 | ||
| 3728 | const std = @import("std"); | 4541 | const std = @import("std"); |
| ... | @@ -3736,12 +4549,12 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined; | ... | @@ -3736,12 +4549,12 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined; |
| 3736 | fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 { | 4549 | fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 { |
| 3737 | const stderr = io.getStdErr().writer(); | 4550 | const stderr = io.getStdErr().writer(); |
| 3738 | 4551 | ||
| 3739 | const tree = try std.zig.parse(allocator, source); | 4552 | var tree = try std.zig.parse(allocator, source); |
| 3740 | defer tree.deinit(); | 4553 | defer tree.deinit(allocator); |
| 3741 | 4554 | ||
| 3742 | for (tree.errors) |*parse_error| { | 4555 | for (tree.errors) |parse_error| { |
| 3743 | const token = tree.token_locs[parse_error.loc()]; | 4556 | const token_start = tree.tokens.items(.start)[parse_error.token]; |
| 3744 | const loc = tree.tokenLocation(0, parse_error.loc()); | 4557 | const loc = tree.tokenLocation(0, parse_error.token); |
| 3745 | try stderr.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 }); | 4558 | try stderr.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 }); |
| 3746 | try tree.renderError(parse_error, stderr); | 4559 | try tree.renderError(parse_error, stderr); |
| 3747 | try stderr.print("\n{s}\n", .{source[loc.line_start..loc.line_end]}); | 4560 | try stderr.print("\n{s}\n", .{source[loc.line_start..loc.line_end]}); |
| ... | @@ -3750,13 +4563,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b | ... | @@ -3750,13 +4563,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b |
| 3750 | while (i < loc.column) : (i += 1) { | 4563 | while (i < loc.column) : (i += 1) { |
| 3751 | try stderr.writeAll(" "); | 4564 | try stderr.writeAll(" "); |
| 3752 | } | 4565 | } |
| 3753 | } | 4566 | try stderr.writeAll("^"); |
| 3754 | { | ||
| 3755 | const caret_count = token.end - token.start; | ||
| 3756 | var i: usize = 0; | ||
| 3757 | while (i < caret_count) : (i += 1) { | ||
| 3758 | try stderr.writeAll("~"); | ||
| 3759 | } | ||
| 3760 | } | 4567 | } |
| 3761 | try stderr.writeAll("\n"); | 4568 | try stderr.writeAll("\n"); |
| 3762 | } | 4569 | } |
| ... | @@ -3764,12 +4571,9 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b | ... | @@ -3764,12 +4571,9 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b |
| 3764 | return error.ParseError; | 4571 | return error.ParseError; |
| 3765 | } | 4572 | } |
| 3766 | 4573 | ||
| 3767 | var buffer = std.ArrayList(u8).init(allocator); | 4574 | const formatted = try tree.render(allocator); |
| 3768 | errdefer buffer.deinit(); | 4575 | anything_changed.* = !mem.eql(u8, formatted, source); |
| 3769 | 4576 | return formatted; | |
| 3770 | const writer = buffer.writer(); | ||
| 3771 | anything_changed.* = try std.zig.render(allocator, writer, tree); | ||
| 3772 | return buffer.toOwnedSlice(); | ||
| 3773 | } | 4577 | } |
| 3774 | fn testTransform(source: []const u8, expected_source: []const u8) !void { | 4578 | fn testTransform(source: []const u8, expected_source: []const u8) !void { |
| 3775 | const needed_alloc_count = x: { | 4579 | const needed_alloc_count = x: { |
| ... | @@ -3822,14 +4626,14 @@ fn testCanonical(source: []const u8) !void { | ... | @@ -3822,14 +4626,14 @@ fn testCanonical(source: []const u8) !void { |
| 3822 | return testTransform(source, source); | 4626 | return testTransform(source, source); |
| 3823 | } | 4627 | } |
| 3824 | 4628 | ||
| 3825 | const Error = std.meta.Tag(std.zig.ast.Error); | 4629 | const Error = std.zig.ast.Error.Tag; |
| 3826 | 4630 | ||
| 3827 | fn testError(source: []const u8, expected_errors: []const Error) !void { | 4631 | fn testError(source: []const u8, expected_errors: []const Error) !void { |
| 3828 | const tree = try std.zig.parse(std.testing.allocator, source); | 4632 | var tree = try std.zig.parse(std.testing.allocator, source); |
| 3829 | defer tree.deinit(); | 4633 | defer tree.deinit(std.testing.allocator); |
| 3830 | 4634 | ||
| 3831 | std.testing.expect(tree.errors.len == expected_errors.len); | 4635 | std.testing.expect(tree.errors.len == expected_errors.len); |
| 3832 | for (expected_errors) |expected, i| { | 4636 | for (expected_errors) |expected, i| { |
| 3833 | std.testing.expect(expected == tree.errors[i]); | 4637 | std.testing.expectEqual(expected, tree.errors[i].tag); |
| 3834 | } | 4638 | } |
| 3835 | } | 4639 | } |
lib/std/zig/render.zig+2385-2340| ... | @@ -6,6 +6,7 @@ | ... | @@ -6,6 +6,7 @@ |
| 6 | const std = @import("../std.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const assert = std.debug.assert; | 7 | const assert = std.debug.assert; |
| 8 | const mem = std.mem; | 8 | const mem = std.mem; |
| 9 | const Allocator = std.mem.Allocator; | ||
| 9 | const meta = std.meta; | 10 | const meta = std.meta; |
| 10 | const ast = std.zig.ast; | 11 | const ast = std.zig.ast; |
| 11 | const Token = std.zig.Token; | 12 | const Token = std.zig.Token; |
| ... | @@ -13,2657 +14,2555 @@ const Token = std.zig.Token; | ... | @@ -13,2657 +14,2555 @@ const Token = std.zig.Token; |
| 13 | const indent_delta = 4; | 14 | const indent_delta = 4; |
| 14 | const asm_indent_delta = 2; | 15 | const asm_indent_delta = 2; |
| 15 | 16 | ||
| 16 | pub const Error = error{ | 17 | pub const Error = ast.Tree.RenderError; |
| 17 | /// Ran out of memory allocating call stack frames to complete rendering. | ||
| 18 | OutOfMemory, | ||
| 19 | }; | ||
| 20 | 18 | ||
| 21 | /// Returns whether anything changed | 19 | const Ais = AutoIndentingStream(std.ArrayList(u8).Writer); |
| 22 | pub fn render(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree) (@TypeOf(stream).Error || Error)!bool { | ||
| 23 | // cannot render an invalid tree | ||
| 24 | std.debug.assert(tree.errors.len == 0); | ||
| 25 | 20 | ||
| 26 | var change_detection_stream = std.io.changeDetectionStream(tree.source, stream); | 21 | pub fn renderTree(buffer: *std.ArrayList(u8), tree: ast.Tree) Error!void { |
| 27 | var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, change_detection_stream.writer()); | 22 | assert(tree.errors.len == 0); // Cannot render an invalid tree. |
| 23 | var auto_indenting_stream = Ais{ | ||
| 24 | .indent_delta = indent_delta, | ||
| 25 | .underlying_writer = buffer.writer(), | ||
| 26 | }; | ||
| 27 | const ais = &auto_indenting_stream; | ||
| 28 | 28 | ||
| 29 | try renderRoot(allocator, &auto_indenting_stream, tree); | 29 | // Render all the line comments at the beginning of the file. |
| 30 | const comment_end_loc = tree.tokens.items(.start)[0]; | ||
| 31 | _ = try renderComments(ais, tree, 0, comment_end_loc); | ||
| 30 | 32 | ||
| 31 | return change_detection_stream.changeDetected(); | 33 | if (tree.tokens.items(.tag)[0] == .container_doc_comment) { |
| 32 | } | 34 | try renderContainerDocComments(ais, tree, 0); |
| 35 | } | ||
| 33 | 36 | ||
| 34 | fn renderRoot( | 37 | try renderMembers(buffer.allocator, ais, tree, tree.rootDecls()); |
| 35 | allocator: *mem.Allocator, | 38 | |
| 36 | ais: anytype, | 39 | if (ais.disabled_offset) |disabled_offset| { |
| 37 | tree: *ast.Tree, | 40 | try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..]); |
| 38 | ) (@TypeOf(ais.*).Error || Error)!void { | ||
| 39 | |||
| 40 | // render all the line comments at the beginning of the file | ||
| 41 | for (tree.token_ids) |token_id, i| { | ||
| 42 | if (token_id != .LineComment) break; | ||
| 43 | const token_loc = tree.token_locs[i]; | ||
| 44 | try ais.writer().print("{s}\n", .{mem.trimRight(u8, tree.tokenSliceLoc(token_loc), " ")}); | ||
| 45 | const next_token = tree.token_locs[i + 1]; | ||
| 46 | const loc = tree.tokenLocationLoc(token_loc.end, next_token); | ||
| 47 | if (loc.line >= 2) { | ||
| 48 | try ais.insertNewline(); | ||
| 49 | } | ||
| 50 | } | 41 | } |
| 42 | } | ||
| 51 | 43 | ||
| 52 | var decl_i: ast.NodeIndex = 0; | 44 | /// Render all members in the given slice, keeping empty lines where appropriate |
| 53 | const root_decls = tree.root_node.decls(); | 45 | fn renderMembers(gpa: *Allocator, ais: *Ais, tree: ast.Tree, members: []const ast.Node.Index) Error!void { |
| 46 | if (members.len == 0) return; | ||
| 47 | try renderMember(gpa, ais, tree, members[0], .newline); | ||
| 48 | for (members[1..]) |member| { | ||
| 49 | try renderExtraNewline(ais, tree, member); | ||
| 50 | try renderMember(gpa, ais, tree, member, .newline); | ||
| 51 | } | ||
| 52 | } | ||
| 54 | 53 | ||
| 55 | if (root_decls.len == 0) return; | 54 | fn renderMember(gpa: *Allocator, ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) Error!void { |
| 56 | while (true) { | 55 | const token_tags = tree.tokens.items(.tag); |
| 57 | var decl = root_decls[decl_i]; | 56 | const main_tokens = tree.nodes.items(.main_token); |
| 58 | 57 | const datas = tree.nodes.items(.data); | |
| 59 | // This loop does the following: | 58 | try renderDocComments(ais, tree, tree.firstToken(decl)); |
| 60 | // | 59 | switch (tree.nodes.items(.tag)[decl]) { |
| 61 | // - Iterates through line/doc comment tokens that precedes the current | 60 | .fn_decl => { |
| 62 | // decl. | 61 | // Some examples: |
| 63 | // - Figures out the first token index (`copy_start_token_index`) which | 62 | // pub extern "foo" fn ... |
| 64 | // hasn't been copied to the output stream yet. | 63 | // export fn ... |
| 65 | // - Detects `zig fmt: (off|on)` in the line comment tokens, and | 64 | const fn_proto = datas[decl].lhs; |
| 66 | // determines whether the current decl should be reformatted or not. | 65 | const fn_token = main_tokens[fn_proto]; |
| 67 | // | 66 | // Go back to the first token we should render here. |
| 68 | var token_index = decl.firstToken(); | 67 | var i = fn_token; |
| 69 | var fmt_active = true; | 68 | while (i > 0) { |
| 70 | var found_fmt_directive = false; | 69 | i -= 1; |
| 71 | 70 | switch (token_tags[i]) { | |
| 72 | var copy_start_token_index = token_index; | 71 | .keyword_extern, |
| 73 | 72 | .keyword_export, | |
| 74 | while (token_index != 0) { | 73 | .keyword_pub, |
| 75 | token_index -= 1; | 74 | .string_literal, |
| 76 | const token_id = tree.token_ids[token_index]; | 75 | .keyword_inline, |
| 77 | switch (token_id) { | 76 | .keyword_noinline, |
| 78 | .LineComment => {}, | 77 | => continue, |
| 79 | .DocComment => { | 78 | |
| 80 | copy_start_token_index = token_index; | 79 | else => { |
| 81 | continue; | 80 | i += 1; |
| 82 | }, | 81 | break; |
| 83 | else => break, | 82 | }, |
| 83 | } | ||
| 84 | } | 84 | } |
| 85 | 85 | while (i < fn_token) : (i += 1) { | |
| 86 | const token_loc = tree.token_locs[token_index]; | 86 | if (token_tags[i] == .keyword_inline) { |
| 87 | if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: off")) { | 87 | // TODO remove this special case when 0.9.0 is released. |
| 88 | if (!found_fmt_directive) { | 88 | // See the commit that introduced this comment for more details. |
| 89 | fmt_active = false; | 89 | continue; |
| 90 | found_fmt_directive = true; | ||
| 91 | } | 90 | } |
| 92 | } else if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: on")) { | 91 | try renderToken(ais, tree, i, .space); |
| 93 | if (!found_fmt_directive) { | 92 | } |
| 94 | fmt_active = true; | 93 | assert(datas[decl].rhs != 0); |
| 95 | found_fmt_directive = true; | 94 | try renderExpression(gpa, ais, tree, fn_proto, .space); |
| 95 | return renderExpression(gpa, ais, tree, datas[decl].rhs, space); | ||
| 96 | }, | ||
| 97 | .fn_proto_simple, | ||
| 98 | .fn_proto_multi, | ||
| 99 | .fn_proto_one, | ||
| 100 | .fn_proto, | ||
| 101 | => { | ||
| 102 | // Extern function prototypes are parsed as these tags. | ||
| 103 | // Go back to the first token we should render here. | ||
| 104 | const fn_token = main_tokens[decl]; | ||
| 105 | var i = fn_token; | ||
| 106 | while (i > 0) { | ||
| 107 | i -= 1; | ||
| 108 | switch (token_tags[i]) { | ||
| 109 | .keyword_extern, | ||
| 110 | .keyword_export, | ||
| 111 | .keyword_pub, | ||
| 112 | .string_literal, | ||
| 113 | .keyword_inline, | ||
| 114 | .keyword_noinline, | ||
| 115 | => continue, | ||
| 116 | |||
| 117 | else => { | ||
| 118 | i += 1; | ||
| 119 | break; | ||
| 120 | }, | ||
| 96 | } | 121 | } |
| 97 | } | 122 | } |
| 98 | } | 123 | while (i < fn_token) : (i += 1) { |
| 124 | try renderToken(ais, tree, i, .space); | ||
| 125 | } | ||
| 126 | try renderExpression(gpa, ais, tree, decl, .none); | ||
| 127 | return renderToken(ais, tree, tree.lastToken(decl) + 1, space); // semicolon | ||
| 128 | }, | ||
| 99 | 129 | ||
| 100 | if (!fmt_active) { | 130 | .@"usingnamespace" => { |
| 101 | // Reformatting is disabled for the current decl and possibly some | 131 | const main_token = main_tokens[decl]; |
| 102 | // more decls that follow. | 132 | const expr = datas[decl].lhs; |
| 103 | // Find the next `decl` for which reformatting is re-enabled. | 133 | if (main_token > 0 and token_tags[main_token - 1] == .keyword_pub) { |
| 104 | token_index = decl.firstToken(); | 134 | try renderToken(ais, tree, main_token - 1, .space); // pub |
| 105 | |||
| 106 | while (!fmt_active) { | ||
| 107 | decl_i += 1; | ||
| 108 | if (decl_i >= root_decls.len) { | ||
| 109 | // If there's no next reformatted `decl`, just copy the | ||
| 110 | // remaining input tokens and bail out. | ||
| 111 | const start = tree.token_locs[copy_start_token_index].start; | ||
| 112 | try copyFixingWhitespace(ais, tree.source[start..]); | ||
| 113 | return; | ||
| 114 | } | ||
| 115 | decl = root_decls[decl_i]; | ||
| 116 | var decl_first_token_index = decl.firstToken(); | ||
| 117 | |||
| 118 | while (token_index < decl_first_token_index) : (token_index += 1) { | ||
| 119 | const token_id = tree.token_ids[token_index]; | ||
| 120 | switch (token_id) { | ||
| 121 | .LineComment => {}, | ||
| 122 | .Eof => unreachable, | ||
| 123 | else => continue, | ||
| 124 | } | ||
| 125 | const token_loc = tree.token_locs[token_index]; | ||
| 126 | if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: on")) { | ||
| 127 | fmt_active = true; | ||
| 128 | } else if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: off")) { | ||
| 129 | fmt_active = false; | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | 135 | } |
| 136 | try renderToken(ais, tree, main_token, .space); // usingnamespace | ||
| 137 | try renderExpression(gpa, ais, tree, expr, .none); | ||
| 138 | return renderToken(ais, tree, tree.lastToken(expr) + 1, space); // ; | ||
| 139 | }, | ||
| 133 | 140 | ||
| 134 | // Found the next `decl` for which reformatting is enabled. Copy | 141 | .global_var_decl => return renderVarDecl(gpa, ais, tree, tree.globalVarDecl(decl)), |
| 135 | // the input tokens before the `decl` that haven't been copied yet. | 142 | .local_var_decl => return renderVarDecl(gpa, ais, tree, tree.localVarDecl(decl)), |
| 136 | var copy_end_token_index = decl.firstToken(); | 143 | .simple_var_decl => return renderVarDecl(gpa, ais, tree, tree.simpleVarDecl(decl)), |
| 137 | token_index = copy_end_token_index; | 144 | .aligned_var_decl => return renderVarDecl(gpa, ais, tree, tree.alignedVarDecl(decl)), |
| 138 | while (token_index != 0) { | 145 | |
| 139 | token_index -= 1; | 146 | .test_decl => { |
| 140 | const token_id = tree.token_ids[token_index]; | 147 | const test_token = main_tokens[decl]; |
| 141 | switch (token_id) { | 148 | try renderToken(ais, tree, test_token, .space); |
| 142 | .LineComment => {}, | 149 | if (token_tags[test_token + 1] == .string_literal) { |
| 143 | .DocComment => { | 150 | try renderToken(ais, tree, test_token + 1, .space); |
| 144 | copy_end_token_index = token_index; | ||
| 145 | continue; | ||
| 146 | }, | ||
| 147 | else => break, | ||
| 148 | } | ||
| 149 | } | 151 | } |
| 152 | try renderExpression(gpa, ais, tree, datas[decl].rhs, space); | ||
| 153 | }, | ||
| 150 | 154 | ||
| 151 | const start = tree.token_locs[copy_start_token_index].start; | 155 | .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), space), |
| 152 | const end = tree.token_locs[copy_end_token_index].start; | 156 | .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), space), |
| 153 | try copyFixingWhitespace(ais, tree.source[start..end]); | 157 | .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), space), |
| 154 | } | 158 | .@"comptime" => return renderExpression(gpa, ais, tree, decl, space), |
| 155 | 159 | ||
| 156 | try renderTopLevelDecl(allocator, ais, tree, decl); | 160 | .root => unreachable, |
| 157 | decl_i += 1; | 161 | else => unreachable, |
| 158 | if (decl_i >= root_decls.len) return; | ||
| 159 | try renderExtraNewline(tree, ais, root_decls[decl_i]); | ||
| 160 | } | 162 | } |
| 161 | } | 163 | } |
| 162 | 164 | ||
| 163 | fn renderExtraNewline(tree: *ast.Tree, ais: anytype, node: *ast.Node) @TypeOf(ais.*).Error!void { | 165 | /// Render all expressions in the slice, keeping empty lines where appropriate |
| 164 | return renderExtraNewlineToken(tree, ais, node.firstToken()); | 166 | fn renderExpressions(gpa: *Allocator, ais: *Ais, tree: ast.Tree, expressions: []const ast.Node.Index, space: Space) Error!void { |
| 165 | } | 167 | if (expressions.len == 0) return; |
| 166 | 168 | try renderExpression(gpa, ais, tree, expressions[0], space); | |
| 167 | fn renderExtraNewlineToken( | 169 | for (expressions[1..]) |expression| { |
| 168 | tree: *ast.Tree, | 170 | try renderExtraNewline(ais, tree, expression); |
| 169 | ais: anytype, | 171 | try renderExpression(gpa, ais, tree, expression, space); |
| 170 | first_token: ast.TokenIndex, | ||
| 171 | ) @TypeOf(ais.*).Error!void { | ||
| 172 | var prev_token = first_token; | ||
| 173 | if (prev_token == 0) return; | ||
| 174 | var newline_threshold: usize = 2; | ||
| 175 | while (tree.token_ids[prev_token - 1] == .DocComment) { | ||
| 176 | if (tree.tokenLocation(tree.token_locs[prev_token - 1].end, prev_token).line == 1) { | ||
| 177 | newline_threshold += 1; | ||
| 178 | } | ||
| 179 | prev_token -= 1; | ||
| 180 | } | ||
| 181 | const prev_token_end = tree.token_locs[prev_token - 1].end; | ||
| 182 | const loc = tree.tokenLocation(prev_token_end, first_token); | ||
| 183 | if (loc.line >= newline_threshold) { | ||
| 184 | try ais.insertNewline(); | ||
| 185 | } | 172 | } |
| 186 | } | 173 | } |
| 187 | 174 | ||
| 188 | fn renderTopLevelDecl(allocator: *mem.Allocator, ais: anytype, tree: *ast.Tree, decl: *ast.Node) (@TypeOf(ais.*).Error || Error)!void { | 175 | fn renderExpression(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void { |
| 189 | try renderContainerDecl(allocator, ais, tree, decl, .Newline); | 176 | const token_tags = tree.tokens.items(.tag); |
| 190 | } | 177 | const main_tokens = tree.nodes.items(.main_token); |
| 178 | const node_tags = tree.nodes.items(.tag); | ||
| 179 | const datas = tree.nodes.items(.data); | ||
| 180 | switch (node_tags[node]) { | ||
| 181 | .identifier, | ||
| 182 | .integer_literal, | ||
| 183 | .float_literal, | ||
| 184 | .char_literal, | ||
| 185 | .true_literal, | ||
| 186 | .false_literal, | ||
| 187 | .null_literal, | ||
| 188 | .unreachable_literal, | ||
| 189 | .undefined_literal, | ||
| 190 | .anyframe_literal, | ||
| 191 | .string_literal, | ||
| 192 | => return renderToken(ais, tree, main_tokens[node], space), | ||
| 193 | |||
| 194 | .multiline_string_literal => { | ||
| 195 | var locked_indents = ais.lockOneShotIndent(); | ||
| 196 | try ais.maybeInsertNewline(); | ||
| 191 | 197 | ||
| 192 | fn renderContainerDecl(allocator: *mem.Allocator, ais: anytype, tree: *ast.Tree, decl: *ast.Node, space: Space) (@TypeOf(ais.*).Error || Error)!void { | 198 | var i = datas[node].lhs; |
| 193 | switch (decl.tag) { | 199 | while (i <= datas[node].rhs) : (i += 1) try renderToken(ais, tree, i, .newline); |
| 194 | .FnProto => { | ||
| 195 | const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); | ||
| 196 | 200 | ||
| 197 | try renderDocComments(tree, ais, fn_proto, fn_proto.getDocComments()); | 201 | while (locked_indents > 0) : (locked_indents -= 1) ais.popIndent(); |
| 198 | 202 | ||
| 199 | if (fn_proto.getBodyNode()) |body_node| { | 203 | switch (space) { |
| 200 | try renderExpression(allocator, ais, tree, decl, .Space); | 204 | .none, .space, .newline, .skip => {}, |
| 201 | try renderExpression(allocator, ais, tree, body_node, space); | 205 | .semicolon => if (token_tags[i] == .semicolon) try renderToken(ais, tree, i, .newline), |
| 202 | } else { | 206 | .comma => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .newline), |
| 203 | try renderExpression(allocator, ais, tree, decl, .None); | 207 | .comma_space => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .space), |
| 204 | try renderToken(tree, ais, tree.nextToken(decl.lastToken()), space); | ||
| 205 | } | 208 | } |
| 206 | }, | 209 | }, |
| 207 | 210 | ||
| 208 | .Use => { | 211 | .error_value => { |
| 209 | const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl); | 212 | try renderToken(ais, tree, main_tokens[node], .none); |
| 213 | try renderToken(ais, tree, main_tokens[node] + 1, .none); | ||
| 214 | return renderToken(ais, tree, main_tokens[node] + 2, space); | ||
| 215 | }, | ||
| 216 | |||
| 217 | .@"anytype" => return renderToken(ais, tree, main_tokens[node], space), | ||
| 210 | 218 | ||
| 211 | if (use_decl.visib_token) |visib_token| { | 219 | .block_two, |
| 212 | try renderToken(tree, ais, visib_token, .Space); // pub | 220 | .block_two_semicolon, |
| 221 | => { | ||
| 222 | const statements = [2]ast.Node.Index{ datas[node].lhs, datas[node].rhs }; | ||
| 223 | if (datas[node].lhs == 0) { | ||
| 224 | return renderBlock(gpa, ais, tree, node, statements[0..0], space); | ||
| 225 | } else if (datas[node].rhs == 0) { | ||
| 226 | return renderBlock(gpa, ais, tree, node, statements[0..1], space); | ||
| 227 | } else { | ||
| 228 | return renderBlock(gpa, ais, tree, node, statements[0..2], space); | ||
| 213 | } | 229 | } |
| 214 | try renderToken(tree, ais, use_decl.use_token, .Space); // usingnamespace | 230 | }, |
| 215 | try renderExpression(allocator, ais, tree, use_decl.expr, .None); | 231 | .block, |
| 216 | try renderToken(tree, ais, use_decl.semicolon_token, space); // ; | 232 | .block_semicolon, |
| 233 | => { | ||
| 234 | const statements = tree.extra_data[datas[node].lhs..datas[node].rhs]; | ||
| 235 | return renderBlock(gpa, ais, tree, node, statements, space); | ||
| 217 | }, | 236 | }, |
| 218 | 237 | ||
| 219 | .VarDecl => { | 238 | .@"errdefer" => { |
| 220 | const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); | 239 | const defer_token = main_tokens[node]; |
| 240 | const payload_token = datas[node].lhs; | ||
| 241 | const expr = datas[node].rhs; | ||
| 221 | 242 | ||
| 222 | try renderDocComments(tree, ais, var_decl, var_decl.getDocComments()); | 243 | try renderToken(ais, tree, defer_token, .space); |
| 223 | try renderVarDecl(allocator, ais, tree, var_decl); | 244 | if (payload_token != 0) { |
| 245 | try renderToken(ais, tree, payload_token - 1, .none); // | | ||
| 246 | try renderToken(ais, tree, payload_token, .none); // identifier | ||
| 247 | try renderToken(ais, tree, payload_token + 1, .space); // | | ||
| 248 | } | ||
| 249 | return renderExpression(gpa, ais, tree, expr, space); | ||
| 224 | }, | 250 | }, |
| 225 | 251 | ||
| 226 | .TestDecl => { | 252 | .@"defer" => { |
| 227 | const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); | 253 | const defer_token = main_tokens[node]; |
| 254 | const expr = datas[node].rhs; | ||
| 255 | try renderToken(ais, tree, defer_token, .space); | ||
| 256 | return renderExpression(gpa, ais, tree, expr, space); | ||
| 257 | }, | ||
| 258 | .@"comptime", .@"nosuspend" => { | ||
| 259 | const comptime_token = main_tokens[node]; | ||
| 260 | const block = datas[node].lhs; | ||
| 261 | try renderToken(ais, tree, comptime_token, .space); | ||
| 262 | return renderExpression(gpa, ais, tree, block, space); | ||
| 263 | }, | ||
| 228 | 264 | ||
| 229 | try renderDocComments(tree, ais, test_decl, test_decl.doc_comments); | 265 | .@"suspend" => { |
| 230 | try renderToken(tree, ais, test_decl.test_token, .Space); | 266 | const suspend_token = main_tokens[node]; |
| 231 | if (test_decl.name) |name| | 267 | const body = datas[node].lhs; |
| 232 | try renderExpression(allocator, ais, tree, name, .Space); | 268 | if (body != 0) { |
| 233 | try renderExpression(allocator, ais, tree, test_decl.body_node, space); | 269 | try renderToken(ais, tree, suspend_token, .space); |
| 270 | return renderExpression(gpa, ais, tree, body, space); | ||
| 271 | } else { | ||
| 272 | return renderToken(ais, tree, suspend_token, space); | ||
| 273 | } | ||
| 234 | }, | 274 | }, |
| 235 | 275 | ||
| 236 | .ContainerField => { | 276 | .@"catch" => { |
| 237 | const field = @fieldParentPtr(ast.Node.ContainerField, "base", decl); | 277 | const main_token = main_tokens[node]; |
| 278 | const fallback_first = tree.firstToken(datas[node].rhs); | ||
| 238 | 279 | ||
| 239 | try renderDocComments(tree, ais, field, field.doc_comments); | 280 | const same_line = tree.tokensOnSameLine(main_token, fallback_first); |
| 240 | if (field.comptime_token) |t| { | 281 | const after_op_space = if (same_line) Space.space else Space.newline; |
| 241 | try renderToken(tree, ais, t, .Space); // comptime | ||
| 242 | } | ||
| 243 | 282 | ||
| 244 | const src_has_trailing_comma = blk: { | 283 | try renderExpression(gpa, ais, tree, datas[node].lhs, .space); // target |
| 245 | const maybe_comma = tree.nextToken(field.lastToken()); | ||
| 246 | break :blk tree.token_ids[maybe_comma] == .Comma; | ||
| 247 | }; | ||
| 248 | 284 | ||
| 249 | // The trailing comma is emitted at the end, but if it's not present | 285 | if (token_tags[fallback_first - 1] == .pipe) { |
| 250 | // we still have to respect the specified `space` parameter | 286 | try renderToken(ais, tree, main_token, .space); // catch keyword |
| 251 | const last_token_space: Space = if (src_has_trailing_comma) .None else space; | 287 | try renderToken(ais, tree, main_token + 1, .none); // pipe |
| 252 | 288 | try renderToken(ais, tree, main_token + 2, .none); // payload identifier | |
| 253 | if (field.type_expr == null and field.value_expr == null) { | 289 | try renderToken(ais, tree, main_token + 3, after_op_space); // pipe |
| 254 | try renderToken(tree, ais, field.name_token, last_token_space); // name | ||
| 255 | } else if (field.type_expr != null and field.value_expr == null) { | ||
| 256 | try renderToken(tree, ais, field.name_token, .None); // name | ||
| 257 | try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // : | ||
| 258 | |||
| 259 | if (field.align_expr) |align_value_expr| { | ||
| 260 | try renderExpression(allocator, ais, tree, field.type_expr.?, .Space); // type | ||
| 261 | const lparen_token = tree.prevToken(align_value_expr.firstToken()); | ||
| 262 | const align_kw = tree.prevToken(lparen_token); | ||
| 263 | const rparen_token = tree.nextToken(align_value_expr.lastToken()); | ||
| 264 | try renderToken(tree, ais, align_kw, .None); // align | ||
| 265 | try renderToken(tree, ais, lparen_token, .None); // ( | ||
| 266 | try renderExpression(allocator, ais, tree, align_value_expr, .None); // alignment | ||
| 267 | try renderToken(tree, ais, rparen_token, last_token_space); // ) | ||
| 268 | } else { | ||
| 269 | try renderExpression(allocator, ais, tree, field.type_expr.?, last_token_space); // type | ||
| 270 | } | ||
| 271 | } else if (field.type_expr == null and field.value_expr != null) { | ||
| 272 | try renderToken(tree, ais, field.name_token, .Space); // name | ||
| 273 | try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // = | ||
| 274 | try renderExpression(allocator, ais, tree, field.value_expr.?, last_token_space); // value | ||
| 275 | } else { | 290 | } else { |
| 276 | try renderToken(tree, ais, field.name_token, .None); // name | 291 | assert(token_tags[fallback_first - 1] == .keyword_catch); |
| 277 | try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // : | 292 | try renderToken(ais, tree, main_token, after_op_space); // catch keyword |
| 278 | |||
| 279 | if (field.align_expr) |align_value_expr| { | ||
| 280 | try renderExpression(allocator, ais, tree, field.type_expr.?, .Space); // type | ||
| 281 | const lparen_token = tree.prevToken(align_value_expr.firstToken()); | ||
| 282 | const align_kw = tree.prevToken(lparen_token); | ||
| 283 | const rparen_token = tree.nextToken(align_value_expr.lastToken()); | ||
| 284 | try renderToken(tree, ais, align_kw, .None); // align | ||
| 285 | try renderToken(tree, ais, lparen_token, .None); // ( | ||
| 286 | try renderExpression(allocator, ais, tree, align_value_expr, .None); // alignment | ||
| 287 | try renderToken(tree, ais, rparen_token, .Space); // ) | ||
| 288 | } else { | ||
| 289 | try renderExpression(allocator, ais, tree, field.type_expr.?, .Space); // type | ||
| 290 | } | ||
| 291 | try renderToken(tree, ais, tree.prevToken(field.value_expr.?.firstToken()), .Space); // = | ||
| 292 | try renderExpression(allocator, ais, tree, field.value_expr.?, last_token_space); // value | ||
| 293 | } | 293 | } |
| 294 | 294 | ||
| 295 | if (src_has_trailing_comma) { | 295 | ais.pushIndentOneShot(); |
| 296 | const comma = tree.nextToken(field.lastToken()); | 296 | try renderExpression(gpa, ais, tree, datas[node].rhs, space); // fallback |
| 297 | try renderToken(tree, ais, comma, space); | ||
| 298 | } | ||
| 299 | }, | 297 | }, |
| 300 | 298 | ||
| 301 | .Comptime => { | 299 | .field_access => { |
| 302 | assert(!decl.requireSemiColon()); | 300 | const main_token = main_tokens[node]; |
| 303 | try renderExpression(allocator, ais, tree, decl, space); | 301 | const field_access = datas[node]; |
| 304 | }, | ||
| 305 | 302 | ||
| 306 | .DocComment => { | 303 | try renderExpression(gpa, ais, tree, field_access.lhs, .none); |
| 307 | const comment = @fieldParentPtr(ast.Node.DocComment, "base", decl); | 304 | |
| 308 | const kind = tree.token_ids[comment.first_line]; | 305 | // Allow a line break between the lhs and the dot if the lhs and rhs |
| 309 | try renderToken(tree, ais, comment.first_line, .Newline); | 306 | // are on different lines. |
| 310 | var tok_i = comment.first_line + 1; | 307 | const lhs_last_token = tree.lastToken(field_access.lhs); |
| 311 | while (true) : (tok_i += 1) { | 308 | const same_line = tree.tokensOnSameLine(lhs_last_token, main_token + 1); |
| 312 | const tok_id = tree.token_ids[tok_i]; | 309 | if (!same_line) { |
| 313 | if (tok_id == kind) { | 310 | if (!hasComment(tree, lhs_last_token, main_token)) try ais.insertNewline(); |
| 314 | try renderToken(tree, ais, tok_i, .Newline); | 311 | ais.pushIndentOneShot(); |
| 315 | } else if (tok_id == .LineComment) { | ||
| 316 | continue; | ||
| 317 | } else { | ||
| 318 | break; | ||
| 319 | } | ||
| 320 | } | 312 | } |
| 313 | |||
| 314 | try renderToken(ais, tree, main_token, .none); | ||
| 315 | |||
| 316 | // This check ensures that zag() is indented in the following example: | ||
| 317 | // const x = foo | ||
| 318 | // .bar() | ||
| 319 | // . // comment | ||
| 320 | // zag(); | ||
| 321 | if (!same_line and hasComment(tree, main_token, main_token + 1)) { | ||
| 322 | ais.pushIndentOneShot(); | ||
| 323 | } | ||
| 324 | |||
| 325 | return renderToken(ais, tree, field_access.rhs, space); | ||
| 321 | }, | 326 | }, |
| 322 | else => unreachable, | ||
| 323 | } | ||
| 324 | } | ||
| 325 | 327 | ||
| 326 | fn renderExpression( | 328 | .error_union, |
| 327 | allocator: *mem.Allocator, | 329 | .switch_range, |
| 328 | ais: anytype, | ||
| 329 | tree: *ast.Tree, | ||
| 330 | base: *ast.Node, | ||
| 331 | space: Space, | ||
| 332 | ) (@TypeOf(ais.*).Error || Error)!void { | ||
| 333 | switch (base.tag) { | ||
| 334 | .Identifier, | ||
| 335 | .IntegerLiteral, | ||
| 336 | .FloatLiteral, | ||
| 337 | .StringLiteral, | ||
| 338 | .CharLiteral, | ||
| 339 | .BoolLiteral, | ||
| 340 | .NullLiteral, | ||
| 341 | .Unreachable, | ||
| 342 | .ErrorType, | ||
| 343 | .UndefinedLiteral, | ||
| 344 | => { | 330 | => { |
| 345 | const casted_node = base.cast(ast.Node.OneToken).?; | 331 | const infix = datas[node]; |
| 346 | return renderToken(tree, ais, casted_node.token, space); | 332 | try renderExpression(gpa, ais, tree, infix.lhs, .none); |
| 333 | try renderToken(ais, tree, main_tokens[node], .none); | ||
| 334 | return renderExpression(gpa, ais, tree, infix.rhs, space); | ||
| 347 | }, | 335 | }, |
| 348 | 336 | ||
| 349 | .AnyType => { | 337 | .add, |
| 350 | const any_type = base.castTag(.AnyType).?; | 338 | .add_wrap, |
| 351 | if (mem.eql(u8, tree.tokenSlice(any_type.token), "var")) { | 339 | .array_cat, |
| 352 | // TODO remove in next release cycle | 340 | .array_mult, |
| 353 | try ais.writer().writeAll("anytype"); | 341 | .assign, |
| 354 | if (space == .Comma) try ais.writer().writeAll(",\n"); | 342 | .assign_bit_and, |
| 355 | return; | 343 | .assign_bit_or, |
| 344 | .assign_bit_shift_left, | ||
| 345 | .assign_bit_shift_right, | ||
| 346 | .assign_bit_xor, | ||
| 347 | .assign_div, | ||
| 348 | .assign_sub, | ||
| 349 | .assign_sub_wrap, | ||
| 350 | .assign_mod, | ||
| 351 | .assign_add, | ||
| 352 | .assign_add_wrap, | ||
| 353 | .assign_mul, | ||
| 354 | .assign_mul_wrap, | ||
| 355 | .bang_equal, | ||
| 356 | .bit_and, | ||
| 357 | .bit_or, | ||
| 358 | .bit_shift_left, | ||
| 359 | .bit_shift_right, | ||
| 360 | .bit_xor, | ||
| 361 | .bool_and, | ||
| 362 | .bool_or, | ||
| 363 | .div, | ||
| 364 | .equal_equal, | ||
| 365 | .greater_or_equal, | ||
| 366 | .greater_than, | ||
| 367 | .less_or_equal, | ||
| 368 | .less_than, | ||
| 369 | .merge_error_sets, | ||
| 370 | .mod, | ||
| 371 | .mul, | ||
| 372 | .mul_wrap, | ||
| 373 | .sub, | ||
| 374 | .sub_wrap, | ||
| 375 | .@"orelse", | ||
| 376 | => { | ||
| 377 | const infix = datas[node]; | ||
| 378 | try renderExpression(gpa, ais, tree, infix.lhs, .space); | ||
| 379 | const op_token = main_tokens[node]; | ||
| 380 | if (tree.tokensOnSameLine(op_token, op_token + 1)) { | ||
| 381 | try renderToken(ais, tree, op_token, .space); | ||
| 382 | } else { | ||
| 383 | ais.pushIndent(); | ||
| 384 | try renderToken(ais, tree, op_token, .newline); | ||
| 385 | ais.popIndent(); | ||
| 356 | } | 386 | } |
| 357 | return renderToken(tree, ais, any_type.token, space); | 387 | ais.pushIndentOneShot(); |
| 388 | return renderExpression(gpa, ais, tree, infix.rhs, space); | ||
| 358 | }, | 389 | }, |
| 359 | 390 | ||
| 360 | .Block, .LabeledBlock => { | 391 | .bit_not, |
| 361 | const block: struct { | 392 | .bool_not, |
| 362 | label: ?ast.TokenIndex, | 393 | .negation, |
| 363 | statements: []*ast.Node, | 394 | .negation_wrap, |
| 364 | lbrace: ast.TokenIndex, | 395 | .optional_type, |
| 365 | rbrace: ast.TokenIndex, | 396 | .address_of, |
| 366 | } = b: { | 397 | => { |
| 367 | if (base.castTag(.Block)) |block| { | 398 | try renderToken(ais, tree, main_tokens[node], .none); |
| 368 | break :b .{ | 399 | return renderExpression(gpa, ais, tree, datas[node].lhs, space); |
| 369 | .label = null, | 400 | }, |
| 370 | .statements = block.statements(), | ||
| 371 | .lbrace = block.lbrace, | ||
| 372 | .rbrace = block.rbrace, | ||
| 373 | }; | ||
| 374 | } else if (base.castTag(.LabeledBlock)) |block| { | ||
| 375 | break :b .{ | ||
| 376 | .label = block.label, | ||
| 377 | .statements = block.statements(), | ||
| 378 | .lbrace = block.lbrace, | ||
| 379 | .rbrace = block.rbrace, | ||
| 380 | }; | ||
| 381 | } else { | ||
| 382 | unreachable; | ||
| 383 | } | ||
| 384 | }; | ||
| 385 | 401 | ||
| 386 | if (block.label) |label| { | 402 | .@"try", |
| 387 | try renderToken(tree, ais, label, Space.None); | 403 | .@"resume", |
| 388 | try renderToken(tree, ais, tree.nextToken(label), Space.Space); | 404 | .@"await", |
| 389 | } | 405 | => { |
| 406 | try renderToken(ais, tree, main_tokens[node], .space); | ||
| 407 | return renderExpression(gpa, ais, tree, datas[node].lhs, space); | ||
| 408 | }, | ||
| 390 | 409 | ||
| 391 | if (block.statements.len == 0) { | 410 | .array_type => return renderArrayType(gpa, ais, tree, tree.arrayType(node), space), |
| 392 | ais.pushIndentNextLine(); | 411 | .array_type_sentinel => return renderArrayType(gpa, ais, tree, tree.arrayTypeSentinel(node), space), |
| 393 | defer ais.popIndent(); | ||
| 394 | try renderToken(tree, ais, block.lbrace, Space.None); | ||
| 395 | } else { | ||
| 396 | ais.pushIndentNextLine(); | ||
| 397 | defer ais.popIndent(); | ||
| 398 | 412 | ||
| 399 | try renderToken(tree, ais, block.lbrace, Space.Newline); | 413 | .ptr_type_aligned => return renderPtrType(gpa, ais, tree, tree.ptrTypeAligned(node), space), |
| 414 | .ptr_type_sentinel => return renderPtrType(gpa, ais, tree, tree.ptrTypeSentinel(node), space), | ||
| 415 | .ptr_type => return renderPtrType(gpa, ais, tree, tree.ptrType(node), space), | ||
| 416 | .ptr_type_bit_range => return renderPtrType(gpa, ais, tree, tree.ptrTypeBitRange(node), space), | ||
| 400 | 417 | ||
| 401 | for (block.statements) |statement, i| { | 418 | .array_init_one, .array_init_one_comma => { |
| 402 | try renderStatement(allocator, ais, tree, statement); | 419 | var elements: [1]ast.Node.Index = undefined; |
| 420 | return renderArrayInit(gpa, ais, tree, tree.arrayInitOne(&elements, node), space); | ||
| 421 | }, | ||
| 422 | .array_init_dot_two, .array_init_dot_two_comma => { | ||
| 423 | var elements: [2]ast.Node.Index = undefined; | ||
| 424 | return renderArrayInit(gpa, ais, tree, tree.arrayInitDotTwo(&elements, node), space); | ||
| 425 | }, | ||
| 426 | .array_init_dot, | ||
| 427 | .array_init_dot_comma, | ||
| 428 | => return renderArrayInit(gpa, ais, tree, tree.arrayInitDot(node), space), | ||
| 429 | .array_init, | ||
| 430 | .array_init_comma, | ||
| 431 | => return renderArrayInit(gpa, ais, tree, tree.arrayInit(node), space), | ||
| 432 | |||
| 433 | .struct_init_one, .struct_init_one_comma => { | ||
| 434 | var fields: [1]ast.Node.Index = undefined; | ||
| 435 | return renderStructInit(gpa, ais, tree, node, tree.structInitOne(&fields, node), space); | ||
| 436 | }, | ||
| 437 | .struct_init_dot_two, .struct_init_dot_two_comma => { | ||
| 438 | var fields: [2]ast.Node.Index = undefined; | ||
| 439 | return renderStructInit(gpa, ais, tree, node, tree.structInitDotTwo(&fields, node), space); | ||
| 440 | }, | ||
| 441 | .struct_init_dot, | ||
| 442 | .struct_init_dot_comma, | ||
| 443 | => return renderStructInit(gpa, ais, tree, node, tree.structInitDot(node), space), | ||
| 444 | .struct_init, | ||
| 445 | .struct_init_comma, | ||
| 446 | => return renderStructInit(gpa, ais, tree, node, tree.structInit(node), space), | ||
| 447 | |||
| 448 | .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => { | ||
| 449 | var params: [1]ast.Node.Index = undefined; | ||
| 450 | return renderCall(gpa, ais, tree, tree.callOne(&params, node), space); | ||
| 451 | }, | ||
| 403 | 452 | ||
| 404 | if (i + 1 < block.statements.len) { | 453 | .call, |
| 405 | try renderExtraNewline(tree, ais, block.statements[i + 1]); | 454 | .call_comma, |
| 406 | } | 455 | .async_call, |
| 407 | } | 456 | .async_call_comma, |
| 408 | } | 457 | => return renderCall(gpa, ais, tree, tree.callFull(node), space), |
| 409 | return renderToken(tree, ais, block.rbrace, space); | 458 | |
| 459 | .array_access => { | ||
| 460 | const suffix = datas[node]; | ||
| 461 | const lbracket = tree.firstToken(suffix.rhs) - 1; | ||
| 462 | const rbracket = tree.lastToken(suffix.rhs) + 1; | ||
| 463 | const one_line = tree.tokensOnSameLine(lbracket, rbracket); | ||
| 464 | const inner_space = if (one_line) Space.none else Space.newline; | ||
| 465 | try renderExpression(gpa, ais, tree, suffix.lhs, .none); | ||
| 466 | ais.pushIndentNextLine(); | ||
| 467 | try renderToken(ais, tree, lbracket, inner_space); // [ | ||
| 468 | try renderExpression(gpa, ais, tree, suffix.rhs, inner_space); | ||
| 469 | ais.popIndent(); | ||
| 470 | return renderToken(ais, tree, rbracket, space); // ] | ||
| 410 | }, | 471 | }, |
| 411 | 472 | ||
| 412 | .Defer => { | 473 | .slice_open => return renderSlice(gpa, ais, tree, tree.sliceOpen(node), space), |
| 413 | const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); | 474 | .slice => return renderSlice(gpa, ais, tree, tree.slice(node), space), |
| 475 | .slice_sentinel => return renderSlice(gpa, ais, tree, tree.sliceSentinel(node), space), | ||
| 414 | 476 | ||
| 415 | try renderToken(tree, ais, defer_node.defer_token, Space.Space); | 477 | .deref => { |
| 416 | if (defer_node.payload) |payload| { | 478 | try renderExpression(gpa, ais, tree, datas[node].lhs, .none); |
| 417 | try renderExpression(allocator, ais, tree, payload, Space.Space); | 479 | return renderToken(ais, tree, main_tokens[node], space); |
| 418 | } | ||
| 419 | return renderExpression(allocator, ais, tree, defer_node.expr, space); | ||
| 420 | }, | 480 | }, |
| 421 | .Comptime => { | ||
| 422 | const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base); | ||
| 423 | 481 | ||
| 424 | try renderToken(tree, ais, comptime_node.comptime_token, Space.Space); | 482 | .unwrap_optional => { |
| 425 | return renderExpression(allocator, ais, tree, comptime_node.expr, space); | 483 | try renderExpression(gpa, ais, tree, datas[node].lhs, .none); |
| 484 | try renderToken(ais, tree, main_tokens[node], .none); | ||
| 485 | return renderToken(ais, tree, datas[node].rhs, space); | ||
| 426 | }, | 486 | }, |
| 427 | .Nosuspend => { | 487 | |
| 428 | const nosuspend_node = @fieldParentPtr(ast.Node.Nosuspend, "base", base); | 488 | .@"break" => { |
| 429 | if (mem.eql(u8, tree.tokenSlice(nosuspend_node.nosuspend_token), "noasync")) { | 489 | const main_token = main_tokens[node]; |
| 430 | // TODO: remove this | 490 | const label_token = datas[node].lhs; |
| 431 | try ais.writer().writeAll("nosuspend "); | 491 | const target = datas[node].rhs; |
| 432 | } else { | 492 | if (label_token == 0 and target == 0) { |
| 433 | try renderToken(tree, ais, nosuspend_node.nosuspend_token, Space.Space); | 493 | try renderToken(ais, tree, main_token, space); // break keyword |
| 494 | } else if (label_token == 0 and target != 0) { | ||
| 495 | try renderToken(ais, tree, main_token, .space); // break keyword | ||
| 496 | try renderExpression(gpa, ais, tree, target, space); | ||
| 497 | } else if (label_token != 0 and target == 0) { | ||
| 498 | try renderToken(ais, tree, main_token, .space); // break keyword | ||
| 499 | try renderToken(ais, tree, label_token - 1, .none); // colon | ||
| 500 | try renderToken(ais, tree, label_token, space); // identifier | ||
| 501 | } else if (label_token != 0 and target != 0) { | ||
| 502 | try renderToken(ais, tree, main_token, .space); // break keyword | ||
| 503 | try renderToken(ais, tree, label_token - 1, .none); // colon | ||
| 504 | try renderToken(ais, tree, label_token, .space); // identifier | ||
| 505 | try renderExpression(gpa, ais, tree, target, space); | ||
| 434 | } | 506 | } |
| 435 | return renderExpression(allocator, ais, tree, nosuspend_node.expr, space); | ||
| 436 | }, | 507 | }, |
| 437 | 508 | ||
| 438 | .Suspend => { | 509 | .@"continue" => { |
| 439 | const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); | 510 | const main_token = main_tokens[node]; |
| 440 | 511 | const label = datas[node].lhs; | |
| 441 | if (suspend_node.body) |body| { | 512 | if (label != 0) { |
| 442 | try renderToken(tree, ais, suspend_node.suspend_token, Space.Space); | 513 | try renderToken(ais, tree, main_token, .space); // continue |
| 443 | return renderExpression(allocator, ais, tree, body, space); | 514 | try renderToken(ais, tree, label - 1, .none); // : |
| 515 | return renderToken(ais, tree, label, space); // label | ||
| 444 | } else { | 516 | } else { |
| 445 | return renderToken(tree, ais, suspend_node.suspend_token, space); | 517 | return renderToken(ais, tree, main_token, space); // continue |
| 446 | } | 518 | } |
| 447 | }, | 519 | }, |
| 448 | 520 | ||
| 449 | .Catch => { | 521 | .@"return" => { |
| 450 | const infix_op_node = @fieldParentPtr(ast.Node.Catch, "base", base); | 522 | if (datas[node].lhs != 0) { |
| 451 | 523 | try renderToken(ais, tree, main_tokens[node], .space); | |
| 452 | const op_space = Space.Space; | 524 | try renderExpression(gpa, ais, tree, datas[node].lhs, space); |
| 453 | try renderExpression(allocator, ais, tree, infix_op_node.lhs, op_space); | 525 | } else { |
| 454 | 526 | try renderToken(ais, tree, main_tokens[node], space); | |
| 455 | const after_op_space = blk: { | ||
| 456 | const same_line = tree.tokensOnSameLine(infix_op_node.op_token, tree.nextToken(infix_op_node.op_token)); | ||
| 457 | break :blk if (same_line) op_space else Space.Newline; | ||
| 458 | }; | ||
| 459 | |||
| 460 | try renderToken(tree, ais, infix_op_node.op_token, after_op_space); | ||
| 461 | |||
| 462 | if (infix_op_node.payload) |payload| { | ||
| 463 | try renderExpression(allocator, ais, tree, payload, Space.Space); | ||
| 464 | } | 527 | } |
| 528 | }, | ||
| 465 | 529 | ||
| 530 | .grouped_expression => { | ||
| 531 | try renderToken(ais, tree, main_tokens[node], .none); // lparen | ||
| 466 | ais.pushIndentOneShot(); | 532 | ais.pushIndentOneShot(); |
| 467 | return renderExpression(allocator, ais, tree, infix_op_node.rhs, space); | 533 | try renderExpression(gpa, ais, tree, datas[node].lhs, .none); |
| 534 | return renderToken(ais, tree, datas[node].rhs, space); // rparen | ||
| 468 | }, | 535 | }, |
| 469 | 536 | ||
| 470 | .Add, | 537 | .container_decl, |
| 471 | .AddWrap, | 538 | .container_decl_trailing, |
| 472 | .ArrayCat, | 539 | => return renderContainerDecl(gpa, ais, tree, node, tree.containerDecl(node), space), |
| 473 | .ArrayMult, | ||
| 474 | .Assign, | ||
| 475 | .AssignBitAnd, | ||
| 476 | .AssignBitOr, | ||
| 477 | .AssignBitShiftLeft, | ||
| 478 | .AssignBitShiftRight, | ||
| 479 | .AssignBitXor, | ||
| 480 | .AssignDiv, | ||
| 481 | .AssignSub, | ||
| 482 | .AssignSubWrap, | ||
| 483 | .AssignMod, | ||
| 484 | .AssignAdd, | ||
| 485 | .AssignAddWrap, | ||
| 486 | .AssignMul, | ||
| 487 | .AssignMulWrap, | ||
| 488 | .BangEqual, | ||
| 489 | .BitAnd, | ||
| 490 | .BitOr, | ||
| 491 | .BitShiftLeft, | ||
| 492 | .BitShiftRight, | ||
| 493 | .BitXor, | ||
| 494 | .BoolAnd, | ||
| 495 | .BoolOr, | ||
| 496 | .Div, | ||
| 497 | .EqualEqual, | ||
| 498 | .ErrorUnion, | ||
| 499 | .GreaterOrEqual, | ||
| 500 | .GreaterThan, | ||
| 501 | .LessOrEqual, | ||
| 502 | .LessThan, | ||
| 503 | .MergeErrorSets, | ||
| 504 | .Mod, | ||
| 505 | .Mul, | ||
| 506 | .MulWrap, | ||
| 507 | .Period, | ||
| 508 | .Range, | ||
| 509 | .Sub, | ||
| 510 | .SubWrap, | ||
| 511 | .OrElse, | ||
| 512 | => { | ||
| 513 | const infix_op_node = @fieldParentPtr(ast.Node.SimpleInfixOp, "base", base); | ||
| 514 | 540 | ||
| 515 | const op_space = switch (base.tag) { | 541 | .container_decl_two, .container_decl_two_trailing => { |
| 516 | .Period, .ErrorUnion, .Range => Space.None, | 542 | var buffer: [2]ast.Node.Index = undefined; |
| 517 | else => Space.Space, | 543 | return renderContainerDecl(gpa, ais, tree, node, tree.containerDeclTwo(&buffer, node), space); |
| 518 | }; | 544 | }, |
| 519 | try renderExpression(allocator, ais, tree, infix_op_node.lhs, op_space); | 545 | .container_decl_arg, |
| 546 | .container_decl_arg_trailing, | ||
| 547 | => return renderContainerDecl(gpa, ais, tree, node, tree.containerDeclArg(node), space), | ||
| 520 | 548 | ||
| 521 | const after_op_space = blk: { | 549 | .tagged_union, |
| 522 | const loc = tree.tokenLocation(tree.token_locs[infix_op_node.op_token].end, tree.nextToken(infix_op_node.op_token)); | 550 | .tagged_union_trailing, |
| 523 | break :blk if (loc.line == 0) op_space else Space.Newline; | 551 | => return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnion(node), space), |
| 524 | }; | ||
| 525 | 552 | ||
| 526 | { | 553 | .tagged_union_two, .tagged_union_two_trailing => { |
| 527 | ais.pushIndent(); | 554 | var buffer: [2]ast.Node.Index = undefined; |
| 528 | defer ais.popIndent(); | 555 | return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnionTwo(&buffer, node), space); |
| 529 | try renderToken(tree, ais, infix_op_node.op_token, after_op_space); | 556 | }, |
| 557 | .tagged_union_enum_tag, | ||
| 558 | .tagged_union_enum_tag_trailing, | ||
| 559 | => return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnionEnumTag(node), space), | ||
| 560 | |||
| 561 | .error_set_decl => { | ||
| 562 | const error_token = main_tokens[node]; | ||
| 563 | const lbrace = error_token + 1; | ||
| 564 | const rbrace = datas[node].rhs; | ||
| 565 | |||
| 566 | try renderToken(ais, tree, error_token, .none); | ||
| 567 | |||
| 568 | if (lbrace + 1 == rbrace) { | ||
| 569 | // There is nothing between the braces so render condensed: `error{}` | ||
| 570 | try renderToken(ais, tree, lbrace, .none); | ||
| 571 | return renderToken(ais, tree, rbrace, space); | ||
| 572 | } else if (lbrace + 2 == rbrace and token_tags[lbrace + 1] == .identifier) { | ||
| 573 | // There is exactly one member and no trailing comma or | ||
| 574 | // comments, so render without surrounding spaces: `error{Foo}` | ||
| 575 | try renderToken(ais, tree, lbrace, .none); | ||
| 576 | try renderToken(ais, tree, lbrace + 1, .none); // identifier | ||
| 577 | return renderToken(ais, tree, rbrace, space); | ||
| 578 | } else if (token_tags[rbrace - 1] == .comma) { | ||
| 579 | // There is a trailing comma so render each member on a new line. | ||
| 580 | ais.pushIndentNextLine(); | ||
| 581 | try renderToken(ais, tree, lbrace, .newline); | ||
| 582 | var i = lbrace + 1; | ||
| 583 | while (i < rbrace) : (i += 1) { | ||
| 584 | if (i > lbrace + 1) try renderExtraNewlineToken(ais, tree, i); | ||
| 585 | switch (token_tags[i]) { | ||
| 586 | .doc_comment => try renderToken(ais, tree, i, .newline), | ||
| 587 | .identifier => try renderToken(ais, tree, i, .comma), | ||
| 588 | .comma => {}, | ||
| 589 | else => unreachable, | ||
| 590 | } | ||
| 591 | } | ||
| 592 | ais.popIndent(); | ||
| 593 | return renderToken(ais, tree, rbrace, space); | ||
| 594 | } else { | ||
| 595 | // There is no trailing comma so render everything on one line. | ||
| 596 | try renderToken(ais, tree, lbrace, .space); | ||
| 597 | var i = lbrace + 1; | ||
| 598 | while (i < rbrace) : (i += 1) { | ||
| 599 | switch (token_tags[i]) { | ||
| 600 | .doc_comment => unreachable, // TODO | ||
| 601 | .identifier => try renderToken(ais, tree, i, .comma_space), | ||
| 602 | .comma => {}, | ||
| 603 | else => unreachable, | ||
| 604 | } | ||
| 605 | } | ||
| 606 | return renderToken(ais, tree, rbrace, space); | ||
| 530 | } | 607 | } |
| 531 | ais.pushIndentOneShot(); | ||
| 532 | return renderExpression(allocator, ais, tree, infix_op_node.rhs, space); | ||
| 533 | }, | 608 | }, |
| 534 | 609 | ||
| 535 | .BitNot, | 610 | .builtin_call_two, .builtin_call_two_comma => { |
| 536 | .BoolNot, | 611 | if (datas[node].lhs == 0) { |
| 537 | .Negation, | 612 | return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{}, space); |
| 538 | .NegationWrap, | 613 | } else if (datas[node].rhs == 0) { |
| 539 | .OptionalType, | 614 | return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{datas[node].lhs}, space); |
| 540 | .AddressOf, | 615 | } else { |
| 541 | => { | 616 | return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{ datas[node].lhs, datas[node].rhs }, space); |
| 542 | const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base); | 617 | } |
| 543 | try renderToken(tree, ais, casted_node.op_token, Space.None); | ||
| 544 | return renderExpression(allocator, ais, tree, casted_node.rhs, space); | ||
| 545 | }, | 618 | }, |
| 546 | 619 | .builtin_call, .builtin_call_comma => { | |
| 547 | .Try, | 620 | const params = tree.extra_data[datas[node].lhs..datas[node].rhs]; |
| 548 | .Resume, | 621 | return renderBuiltinCall(gpa, ais, tree, main_tokens[node], params, space); |
| 549 | .Await, | ||
| 550 | => { | ||
| 551 | const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base); | ||
| 552 | try renderToken(tree, ais, casted_node.op_token, Space.Space); | ||
| 553 | return renderExpression(allocator, ais, tree, casted_node.rhs, space); | ||
| 554 | }, | 622 | }, |
| 555 | 623 | ||
| 556 | .ArrayType => { | 624 | .fn_proto_simple => { |
| 557 | const array_type = @fieldParentPtr(ast.Node.ArrayType, "base", base); | 625 | var params: [1]ast.Node.Index = undefined; |
| 558 | return renderArrayType( | 626 | return renderFnProto(gpa, ais, tree, tree.fnProtoSimple(&params, node), space); |
| 559 | allocator, | ||
| 560 | ais, | ||
| 561 | tree, | ||
| 562 | array_type.op_token, | ||
| 563 | array_type.rhs, | ||
| 564 | array_type.len_expr, | ||
| 565 | null, | ||
| 566 | space, | ||
| 567 | ); | ||
| 568 | }, | 627 | }, |
| 569 | .ArrayTypeSentinel => { | 628 | .fn_proto_multi => return renderFnProto(gpa, ais, tree, tree.fnProtoMulti(node), space), |
| 570 | const array_type = @fieldParentPtr(ast.Node.ArrayTypeSentinel, "base", base); | 629 | .fn_proto_one => { |
| 571 | return renderArrayType( | 630 | var params: [1]ast.Node.Index = undefined; |
| 572 | allocator, | 631 | return renderFnProto(gpa, ais, tree, tree.fnProtoOne(&params, node), space); |
| 573 | ais, | ||
| 574 | tree, | ||
| 575 | array_type.op_token, | ||
| 576 | array_type.rhs, | ||
| 577 | array_type.len_expr, | ||
| 578 | array_type.sentinel, | ||
| 579 | space, | ||
| 580 | ); | ||
| 581 | }, | 632 | }, |
| 582 | 633 | .fn_proto => return renderFnProto(gpa, ais, tree, tree.fnProto(node), space), | |
| 583 | .PtrType => { | 634 | |
| 584 | const ptr_type = @fieldParentPtr(ast.Node.PtrType, "base", base); | 635 | .anyframe_type => { |
| 585 | const op_tok_id = tree.token_ids[ptr_type.op_token]; | 636 | const main_token = main_tokens[node]; |
| 586 | switch (op_tok_id) { | 637 | if (datas[node].rhs != 0) { |
| 587 | .Asterisk, .AsteriskAsterisk => try ais.writer().writeByte('*'), | 638 | try renderToken(ais, tree, main_token, .none); // anyframe |
| 588 | .LBracket => if (tree.token_ids[ptr_type.op_token + 2] == .Identifier) | 639 | try renderToken(ais, tree, main_token + 1, .none); // -> |
| 589 | try ais.writer().writeAll("[*c") | 640 | return renderExpression(gpa, ais, tree, datas[node].rhs, space); |
| 590 | else | 641 | } else { |
| 591 | try ais.writer().writeAll("[*"), | 642 | return renderToken(ais, tree, main_token, space); // anyframe |
| 592 | else => unreachable, | ||
| 593 | } | ||
| 594 | if (ptr_type.ptr_info.sentinel) |sentinel| { | ||
| 595 | const colon_token = tree.prevToken(sentinel.firstToken()); | ||
| 596 | try renderToken(tree, ais, colon_token, Space.None); // : | ||
| 597 | const sentinel_space = switch (op_tok_id) { | ||
| 598 | .LBracket => Space.None, | ||
| 599 | else => Space.Space, | ||
| 600 | }; | ||
| 601 | try renderExpression(allocator, ais, tree, sentinel, sentinel_space); | ||
| 602 | } | ||
| 603 | switch (op_tok_id) { | ||
| 604 | .Asterisk, .AsteriskAsterisk => {}, | ||
| 605 | .LBracket => try ais.writer().writeByte(']'), | ||
| 606 | else => unreachable, | ||
| 607 | } | ||
| 608 | if (ptr_type.ptr_info.allowzero_token) |allowzero_token| { | ||
| 609 | try renderToken(tree, ais, allowzero_token, Space.Space); // allowzero | ||
| 610 | } | ||
| 611 | if (ptr_type.ptr_info.align_info) |align_info| { | ||
| 612 | const lparen_token = tree.prevToken(align_info.node.firstToken()); | ||
| 613 | const align_token = tree.prevToken(lparen_token); | ||
| 614 | |||
| 615 | try renderToken(tree, ais, align_token, Space.None); // align | ||
| 616 | try renderToken(tree, ais, lparen_token, Space.None); // ( | ||
| 617 | |||
| 618 | try renderExpression(allocator, ais, tree, align_info.node, Space.None); | ||
| 619 | |||
| 620 | if (align_info.bit_range) |bit_range| { | ||
| 621 | const colon1 = tree.prevToken(bit_range.start.firstToken()); | ||
| 622 | const colon2 = tree.prevToken(bit_range.end.firstToken()); | ||
| 623 | |||
| 624 | try renderToken(tree, ais, colon1, Space.None); // : | ||
| 625 | try renderExpression(allocator, ais, tree, bit_range.start, Space.None); | ||
| 626 | try renderToken(tree, ais, colon2, Space.None); // : | ||
| 627 | try renderExpression(allocator, ais, tree, bit_range.end, Space.None); | ||
| 628 | |||
| 629 | const rparen_token = tree.nextToken(bit_range.end.lastToken()); | ||
| 630 | try renderToken(tree, ais, rparen_token, Space.Space); // ) | ||
| 631 | } else { | ||
| 632 | const rparen_token = tree.nextToken(align_info.node.lastToken()); | ||
| 633 | try renderToken(tree, ais, rparen_token, Space.Space); // ) | ||
| 634 | } | ||
| 635 | } | ||
| 636 | if (ptr_type.ptr_info.const_token) |const_token| { | ||
| 637 | try renderToken(tree, ais, const_token, Space.Space); // const | ||
| 638 | } | ||
| 639 | if (ptr_type.ptr_info.volatile_token) |volatile_token| { | ||
| 640 | try renderToken(tree, ais, volatile_token, Space.Space); // volatile | ||
| 641 | } | 643 | } |
| 642 | return renderExpression(allocator, ais, tree, ptr_type.rhs, space); | ||
| 643 | }, | 644 | }, |
| 644 | 645 | ||
| 645 | .SliceType => { | 646 | .@"switch", |
| 646 | const slice_type = @fieldParentPtr(ast.Node.SliceType, "base", base); | 647 | .switch_comma, |
| 647 | try renderToken(tree, ais, slice_type.op_token, Space.None); // [ | 648 | => { |
| 648 | if (slice_type.ptr_info.sentinel) |sentinel| { | 649 | const switch_token = main_tokens[node]; |
| 649 | const colon_token = tree.prevToken(sentinel.firstToken()); | 650 | const condition = datas[node].lhs; |
| 650 | try renderToken(tree, ais, colon_token, Space.None); // : | 651 | const extra = tree.extraData(datas[node].rhs, ast.Node.SubRange); |
| 651 | try renderExpression(allocator, ais, tree, sentinel, Space.None); | 652 | const cases = tree.extra_data[extra.start..extra.end]; |
| 652 | try renderToken(tree, ais, tree.nextToken(sentinel.lastToken()), Space.None); // ] | 653 | const rparen = tree.lastToken(condition) + 1; |
| 654 | |||
| 655 | try renderToken(ais, tree, switch_token, .space); // switch keyword | ||
| 656 | try renderToken(ais, tree, switch_token + 1, .none); // lparen | ||
| 657 | try renderExpression(gpa, ais, tree, condition, .none); // condtion expression | ||
| 658 | try renderToken(ais, tree, rparen, .space); // rparen | ||
| 659 | |||
| 660 | ais.pushIndentNextLine(); | ||
| 661 | if (cases.len == 0) { | ||
| 662 | try renderToken(ais, tree, rparen + 1, .none); // lbrace | ||
| 653 | } else { | 663 | } else { |
| 654 | try renderToken(tree, ais, tree.nextToken(slice_type.op_token), Space.None); // ] | 664 | try renderToken(ais, tree, rparen + 1, .newline); // lbrace |
| 665 | try renderExpressions(gpa, ais, tree, cases, .comma); | ||
| 655 | } | 666 | } |
| 667 | ais.popIndent(); | ||
| 668 | return renderToken(ais, tree, tree.lastToken(node), space); // rbrace | ||
| 669 | }, | ||
| 656 | 670 | ||
| 657 | if (slice_type.ptr_info.allowzero_token) |allowzero_token| { | 671 | .switch_case_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space), |
| 658 | try renderToken(tree, ais, allowzero_token, Space.Space); // allowzero | 672 | .switch_case => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space), |
| 659 | } | ||
| 660 | if (slice_type.ptr_info.align_info) |align_info| { | ||
| 661 | const lparen_token = tree.prevToken(align_info.node.firstToken()); | ||
| 662 | const align_token = tree.prevToken(lparen_token); | ||
| 663 | 673 | ||
| 664 | try renderToken(tree, ais, align_token, Space.None); // align | 674 | .while_simple => return renderWhile(gpa, ais, tree, tree.whileSimple(node), space), |
| 665 | try renderToken(tree, ais, lparen_token, Space.None); // ( | 675 | .while_cont => return renderWhile(gpa, ais, tree, tree.whileCont(node), space), |
| 676 | .@"while" => return renderWhile(gpa, ais, tree, tree.whileFull(node), space), | ||
| 677 | .for_simple => return renderWhile(gpa, ais, tree, tree.forSimple(node), space), | ||
| 678 | .@"for" => return renderWhile(gpa, ais, tree, tree.forFull(node), space), | ||
| 666 | 679 | ||
| 667 | try renderExpression(allocator, ais, tree, align_info.node, Space.None); | 680 | .if_simple => return renderIf(gpa, ais, tree, tree.ifSimple(node), space), |
| 681 | .@"if" => return renderIf(gpa, ais, tree, tree.ifFull(node), space), | ||
| 668 | 682 | ||
| 669 | if (align_info.bit_range) |bit_range| { | 683 | .asm_simple => return renderAsm(gpa, ais, tree, tree.asmSimple(node), space), |
| 670 | const colon1 = tree.prevToken(bit_range.start.firstToken()); | 684 | .@"asm" => return renderAsm(gpa, ais, tree, tree.asmFull(node), space), |
| 671 | const colon2 = tree.prevToken(bit_range.end.firstToken()); | ||
| 672 | 685 | ||
| 673 | try renderToken(tree, ais, colon1, Space.None); // : | 686 | .enum_literal => { |
| 674 | try renderExpression(allocator, ais, tree, bit_range.start, Space.None); | 687 | try renderToken(ais, tree, main_tokens[node] - 1, .none); // . |
| 675 | try renderToken(tree, ais, colon2, Space.None); // : | 688 | return renderToken(ais, tree, main_tokens[node], space); // name |
| 676 | try renderExpression(allocator, ais, tree, bit_range.end, Space.None); | 689 | }, |
| 677 | 690 | ||
| 678 | const rparen_token = tree.nextToken(bit_range.end.lastToken()); | 691 | .fn_decl => unreachable, |
| 679 | try renderToken(tree, ais, rparen_token, Space.Space); // ) | 692 | .container_field => unreachable, |
| 680 | } else { | 693 | .container_field_init => unreachable, |
| 681 | const rparen_token = tree.nextToken(align_info.node.lastToken()); | 694 | .container_field_align => unreachable, |
| 682 | try renderToken(tree, ais, rparen_token, Space.Space); // ) | 695 | .root => unreachable, |
| 683 | } | 696 | .global_var_decl => unreachable, |
| 697 | .local_var_decl => unreachable, | ||
| 698 | .simple_var_decl => unreachable, | ||
| 699 | .aligned_var_decl => unreachable, | ||
| 700 | .@"usingnamespace" => unreachable, | ||
| 701 | .test_decl => unreachable, | ||
| 702 | .asm_output => unreachable, | ||
| 703 | .asm_input => unreachable, | ||
| 704 | } | ||
| 705 | } | ||
| 706 | |||
| 707 | fn renderArrayType( | ||
| 708 | gpa: *Allocator, | ||
| 709 | ais: *Ais, | ||
| 710 | tree: ast.Tree, | ||
| 711 | array_type: ast.full.ArrayType, | ||
| 712 | space: Space, | ||
| 713 | ) Error!void { | ||
| 714 | const rbracket = tree.firstToken(array_type.ast.elem_type) - 1; | ||
| 715 | const one_line = tree.tokensOnSameLine(array_type.ast.lbracket, rbracket); | ||
| 716 | const inner_space = if (one_line) Space.none else Space.newline; | ||
| 717 | ais.pushIndentNextLine(); | ||
| 718 | try renderToken(ais, tree, array_type.ast.lbracket, inner_space); // lbracket | ||
| 719 | try renderExpression(gpa, ais, tree, array_type.ast.elem_count, inner_space); | ||
| 720 | if (array_type.ast.sentinel) |sentinel| { | ||
| 721 | try renderToken(ais, tree, tree.firstToken(sentinel) - 1, inner_space); // colon | ||
| 722 | try renderExpression(gpa, ais, tree, sentinel, inner_space); | ||
| 723 | } | ||
| 724 | ais.popIndent(); | ||
| 725 | try renderToken(ais, tree, rbracket, .none); // rbracket | ||
| 726 | return renderExpression(gpa, ais, tree, array_type.ast.elem_type, space); | ||
| 727 | } | ||
| 728 | |||
| 729 | fn renderPtrType( | ||
| 730 | gpa: *Allocator, | ||
| 731 | ais: *Ais, | ||
| 732 | tree: ast.Tree, | ||
| 733 | ptr_type: ast.full.PtrType, | ||
| 734 | space: Space, | ||
| 735 | ) Error!void { | ||
| 736 | switch (ptr_type.size) { | ||
| 737 | .One => { | ||
| 738 | // Since ** tokens exist and the same token is shared by two | ||
| 739 | // nested pointer types, we check to see if we are the parent | ||
| 740 | // in such a relationship. If so, skip rendering anything for | ||
| 741 | // this pointer type and rely on the child to render our asterisk | ||
| 742 | // as well when it renders the ** token. | ||
| 743 | if (tree.tokens.items(.tag)[ptr_type.ast.main_token] == .asterisk_asterisk and | ||
| 744 | ptr_type.ast.main_token == tree.nodes.items(.main_token)[ptr_type.ast.child_type]) | ||
| 745 | { | ||
| 746 | return renderExpression(gpa, ais, tree, ptr_type.ast.child_type, space); | ||
| 684 | } | 747 | } |
| 685 | if (slice_type.ptr_info.const_token) |const_token| { | 748 | try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk |
| 686 | try renderToken(tree, ais, const_token, Space.Space); | 749 | }, |
| 750 | .Many => { | ||
| 751 | if (ptr_type.ast.sentinel == 0) { | ||
| 752 | try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket | ||
| 753 | try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk | ||
| 754 | try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket | ||
| 755 | } else { | ||
| 756 | try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket | ||
| 757 | try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk | ||
| 758 | try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon | ||
| 759 | try renderExpression(gpa, ais, tree, ptr_type.ast.sentinel, .none); | ||
| 760 | try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket | ||
| 687 | } | 761 | } |
| 688 | if (slice_type.ptr_info.volatile_token) |volatile_token| { | 762 | }, |
| 689 | try renderToken(tree, ais, volatile_token, Space.Space); | 763 | .C => { |
| 764 | try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket | ||
| 765 | try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk | ||
| 766 | try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // c | ||
| 767 | try renderToken(ais, tree, ptr_type.ast.main_token + 2, .none); // rbracket | ||
| 768 | }, | ||
| 769 | .Slice => { | ||
| 770 | if (ptr_type.ast.sentinel == 0) { | ||
| 771 | try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket | ||
| 772 | try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket | ||
| 773 | } else { | ||
| 774 | try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket | ||
| 775 | try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon | ||
| 776 | try renderExpression(gpa, ais, tree, ptr_type.ast.sentinel, .none); | ||
| 777 | try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket | ||
| 690 | } | 778 | } |
| 691 | return renderExpression(allocator, ais, tree, slice_type.rhs, space); | ||
| 692 | }, | 779 | }, |
| 780 | } | ||
| 693 | 781 | ||
| 694 | .ArrayInitializer, .ArrayInitializerDot => { | 782 | if (ptr_type.allowzero_token) |allowzero_token| { |
| 695 | var rtoken: ast.TokenIndex = undefined; | 783 | try renderToken(ais, tree, allowzero_token, .space); |
| 696 | var exprs: []*ast.Node = undefined; | 784 | } |
| 697 | const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.tag) { | ||
| 698 | .ArrayInitializerDot => blk: { | ||
| 699 | const casted = @fieldParentPtr(ast.Node.ArrayInitializerDot, "base", base); | ||
| 700 | rtoken = casted.rtoken; | ||
| 701 | exprs = casted.list(); | ||
| 702 | break :blk .{ .dot = casted.dot }; | ||
| 703 | }, | ||
| 704 | .ArrayInitializer => blk: { | ||
| 705 | const casted = @fieldParentPtr(ast.Node.ArrayInitializer, "base", base); | ||
| 706 | rtoken = casted.rtoken; | ||
| 707 | exprs = casted.list(); | ||
| 708 | break :blk .{ .node = casted.lhs }; | ||
| 709 | }, | ||
| 710 | else => unreachable, | ||
| 711 | }; | ||
| 712 | |||
| 713 | const lbrace = switch (lhs) { | ||
| 714 | .dot => |dot| tree.nextToken(dot), | ||
| 715 | .node => |node| tree.nextToken(node.lastToken()), | ||
| 716 | }; | ||
| 717 | 785 | ||
| 718 | switch (lhs) { | 786 | if (ptr_type.ast.align_node != 0) { |
| 719 | .dot => |dot| try renderToken(tree, ais, dot, Space.None), | 787 | const align_first = tree.firstToken(ptr_type.ast.align_node); |
| 720 | .node => |node| try renderExpression(allocator, ais, tree, node, Space.None), | 788 | try renderToken(ais, tree, align_first - 2, .none); // align |
| 721 | } | 789 | try renderToken(ais, tree, align_first - 1, .none); // lparen |
| 790 | try renderExpression(gpa, ais, tree, ptr_type.ast.align_node, .none); | ||
| 791 | if (ptr_type.ast.bit_range_start != 0) { | ||
| 792 | assert(ptr_type.ast.bit_range_end != 0); | ||
| 793 | try renderToken(ais, tree, tree.firstToken(ptr_type.ast.bit_range_start) - 1, .none); // colon | ||
| 794 | try renderExpression(gpa, ais, tree, ptr_type.ast.bit_range_start, .none); | ||
| 795 | try renderToken(ais, tree, tree.firstToken(ptr_type.ast.bit_range_end) - 1, .none); // colon | ||
| 796 | try renderExpression(gpa, ais, tree, ptr_type.ast.bit_range_end, .none); | ||
| 797 | try renderToken(ais, tree, tree.lastToken(ptr_type.ast.bit_range_end) + 1, .space); // rparen | ||
| 798 | } else { | ||
| 799 | try renderToken(ais, tree, tree.lastToken(ptr_type.ast.align_node) + 1, .space); // rparen | ||
| 800 | } | ||
| 801 | } | ||
| 722 | 802 | ||
| 723 | if (exprs.len == 0) { | 803 | if (ptr_type.const_token) |const_token| { |
| 724 | try renderToken(tree, ais, lbrace, Space.None); | 804 | try renderToken(ais, tree, const_token, .space); |
| 725 | return renderToken(tree, ais, rtoken, space); | 805 | } |
| 726 | } | ||
| 727 | 806 | ||
| 728 | if (exprs.len == 1 and exprs[0].tag != .MultilineStringLiteral and tree.token_ids[exprs[0].*.lastToken() + 1] == .RBrace) { | 807 | if (ptr_type.volatile_token) |volatile_token| { |
| 729 | const expr = exprs[0]; | 808 | try renderToken(ais, tree, volatile_token, .space); |
| 809 | } | ||
| 730 | 810 | ||
| 731 | try renderToken(tree, ais, lbrace, Space.None); | 811 | try renderExpression(gpa, ais, tree, ptr_type.ast.child_type, space); |
| 732 | try renderExpression(allocator, ais, tree, expr, Space.None); | 812 | } |
| 733 | return renderToken(tree, ais, rtoken, space); | ||
| 734 | } | ||
| 735 | 813 | ||
| 736 | // scan to find row size | 814 | fn renderSlice( |
| 737 | if (rowSize(tree, exprs, rtoken) != null) { | 815 | gpa: *Allocator, |
| 738 | { | 816 | ais: *Ais, |
| 739 | ais.pushIndentNextLine(); | 817 | tree: ast.Tree, |
| 740 | defer ais.popIndent(); | 818 | slice: ast.full.Slice, |
| 741 | try renderToken(tree, ais, lbrace, Space.Newline); | 819 | space: Space, |
| 742 | 820 | ) Error!void { | |
| 743 | var expr_index: usize = 0; | 821 | const node_tags = tree.nodes.items(.tag); |
| 744 | while (rowSize(tree, exprs[expr_index..], rtoken)) |row_size| { | 822 | const after_start_space_bool = nodeCausesSliceOpSpace(node_tags[slice.ast.start]) or |
| 745 | const row_exprs = exprs[expr_index..]; | 823 | if (slice.ast.end != 0) nodeCausesSliceOpSpace(node_tags[slice.ast.end]) else false; |
| 746 | // A place to store the width of each expression and its column's maximum | 824 | const after_start_space = if (after_start_space_bool) Space.space else Space.none; |
| 747 | var widths = try allocator.alloc(usize, row_exprs.len + row_size); | 825 | const after_dots_space = if (slice.ast.end != 0) after_start_space else Space.none; |
| 748 | defer allocator.free(widths); | 826 | |
| 749 | mem.set(usize, widths, 0); | 827 | try renderExpression(gpa, ais, tree, slice.ast.sliced, .none); |
| 750 | 828 | try renderToken(ais, tree, slice.ast.lbracket, .none); // lbracket | |
| 751 | var expr_newlines = try allocator.alloc(bool, row_exprs.len); | 829 | |
| 752 | defer allocator.free(expr_newlines); | 830 | const start_last = tree.lastToken(slice.ast.start); |
| 753 | mem.set(bool, expr_newlines, false); | 831 | try renderExpression(gpa, ais, tree, slice.ast.start, after_start_space); |
| 754 | 832 | try renderToken(ais, tree, start_last + 1, after_dots_space); // ellipsis2 ("..") | |
| 755 | var expr_widths = widths[0 .. widths.len - row_size]; | 833 | if (slice.ast.end == 0) { |
| 756 | var column_widths = widths[widths.len - row_size ..]; | 834 | return renderToken(ais, tree, start_last + 2, space); // rbracket |
| 757 | 835 | } | |
| 758 | // Find next row with trailing comment (if any) to end the current section | ||
| 759 | var section_end = sec_end: { | ||
| 760 | var this_line_first_expr: usize = 0; | ||
| 761 | var this_line_size = rowSize(tree, row_exprs, rtoken); | ||
| 762 | for (row_exprs) |expr, i| { | ||
| 763 | // Ignore comment on first line of this section | ||
| 764 | if (i == 0 or tree.tokensOnSameLine(row_exprs[0].firstToken(), expr.lastToken())) continue; | ||
| 765 | // Track start of line containing comment | ||
| 766 | if (!tree.tokensOnSameLine(row_exprs[this_line_first_expr].firstToken(), expr.lastToken())) { | ||
| 767 | this_line_first_expr = i; | ||
| 768 | this_line_size = rowSize(tree, row_exprs[this_line_first_expr..], rtoken); | ||
| 769 | } | ||
| 770 | |||
| 771 | const maybe_comma = expr.lastToken() + 1; | ||
| 772 | const maybe_comment = expr.lastToken() + 2; | ||
| 773 | if (maybe_comment < tree.token_ids.len) { | ||
| 774 | if (tree.token_ids[maybe_comma] == .Comma and | ||
| 775 | tree.token_ids[maybe_comment] == .LineComment and | ||
| 776 | tree.tokensOnSameLine(expr.lastToken(), maybe_comment)) | ||
| 777 | { | ||
| 778 | var comment_token_loc = tree.token_locs[maybe_comment]; | ||
| 779 | const comment_is_empty = mem.trimRight(u8, tree.tokenSliceLoc(comment_token_loc), " ").len == 2; | ||
| 780 | if (!comment_is_empty) { | ||
| 781 | // Found row ending in comment | ||
| 782 | break :sec_end i - this_line_size.? + 1; | ||
| 783 | } | ||
| 784 | } | ||
| 785 | } | ||
| 786 | } | ||
| 787 | break :sec_end row_exprs.len; | ||
| 788 | }; | ||
| 789 | expr_index += section_end; | ||
| 790 | |||
| 791 | const section_exprs = row_exprs[0..section_end]; | ||
| 792 | |||
| 793 | // Null stream for counting the printed length of each expression | ||
| 794 | var line_find_stream = std.io.findByteWriter('\n', std.io.null_writer); | ||
| 795 | var counting_stream = std.io.countingWriter(line_find_stream.writer()); | ||
| 796 | var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, counting_stream.writer()); | ||
| 797 | |||
| 798 | // Calculate size of columns in current section | ||
| 799 | var column_counter: usize = 0; | ||
| 800 | var single_line = true; | ||
| 801 | for (section_exprs) |expr, i| { | ||
| 802 | if (i + 1 < section_exprs.len) { | ||
| 803 | counting_stream.bytes_written = 0; | ||
| 804 | line_find_stream.byte_found = false; | ||
| 805 | try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None); | ||
| 806 | const width = @intCast(usize, counting_stream.bytes_written); | ||
| 807 | expr_widths[i] = width; | ||
| 808 | expr_newlines[i] = line_find_stream.byte_found; | ||
| 809 | |||
| 810 | if (!line_find_stream.byte_found) { | ||
| 811 | const column = column_counter % row_size; | ||
| 812 | column_widths[column] = std.math.max(column_widths[column], width); | ||
| 813 | |||
| 814 | const expr_last_token = expr.*.lastToken() + 1; | ||
| 815 | const next_expr = section_exprs[i + 1]; | ||
| 816 | const loc = tree.tokenLocation(tree.token_locs[expr_last_token].start, next_expr.*.firstToken()); | ||
| 817 | |||
| 818 | column_counter += 1; | ||
| 819 | |||
| 820 | if (loc.line != 0) single_line = false; | ||
| 821 | } else { | ||
| 822 | single_line = false; | ||
| 823 | column_counter = 0; | ||
| 824 | } | ||
| 825 | } else { | ||
| 826 | counting_stream.bytes_written = 0; | ||
| 827 | try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None); | ||
| 828 | const width = @intCast(usize, counting_stream.bytes_written); | ||
| 829 | expr_widths[i] = width; | ||
| 830 | expr_newlines[i] = line_find_stream.byte_found; | ||
| 831 | |||
| 832 | if (!line_find_stream.byte_found) { | ||
| 833 | const column = column_counter % row_size; | ||
| 834 | column_widths[column] = std.math.max(column_widths[column], width); | ||
| 835 | } | ||
| 836 | break; | ||
| 837 | } | ||
| 838 | } | ||
| 839 | 836 | ||
| 840 | // Render exprs in current section | 837 | const end_last = tree.lastToken(slice.ast.end); |
| 841 | column_counter = 0; | 838 | const after_end_space = if (slice.ast.sentinel != 0) Space.space else Space.none; |
| 842 | var last_col_index: usize = row_size - 1; | 839 | try renderExpression(gpa, ais, tree, slice.ast.end, after_end_space); |
| 843 | for (section_exprs) |expr, i| { | 840 | if (slice.ast.sentinel == 0) { |
| 844 | if (i + 1 < section_exprs.len) { | 841 | return renderToken(ais, tree, end_last + 1, space); // rbracket |
| 845 | const next_expr = section_exprs[i + 1]; | 842 | } |
| 846 | try renderExpression(allocator, ais, tree, expr, Space.None); | ||
| 847 | |||
| 848 | const comma = tree.nextToken(expr.*.lastToken()); | ||
| 849 | |||
| 850 | if (column_counter != last_col_index) { | ||
| 851 | if (!expr_newlines[i] and !expr_newlines[i + 1]) { | ||
| 852 | // Neither the current or next expression is multiline | ||
| 853 | try renderToken(tree, ais, comma, Space.Space); // , | ||
| 854 | assert(column_widths[column_counter % row_size] >= expr_widths[i]); | ||
| 855 | const padding = column_widths[column_counter % row_size] - expr_widths[i]; | ||
| 856 | try ais.writer().writeByteNTimes(' ', padding); | ||
| 857 | |||
| 858 | column_counter += 1; | ||
| 859 | continue; | ||
| 860 | } | ||
| 861 | } | ||
| 862 | if (single_line and row_size != 1) { | ||
| 863 | try renderToken(tree, ais, comma, Space.Space); // , | ||
| 864 | continue; | ||
| 865 | } | ||
| 866 | |||
| 867 | column_counter = 0; | ||
| 868 | try renderToken(tree, ais, comma, Space.Newline); // , | ||
| 869 | try renderExtraNewline(tree, ais, next_expr); | ||
| 870 | } else { | ||
| 871 | const maybe_comma = tree.nextToken(expr.*.lastToken()); | ||
| 872 | if (tree.token_ids[maybe_comma] == .Comma) { | ||
| 873 | try renderExpression(allocator, ais, tree, expr, Space.None); // , | ||
| 874 | try renderToken(tree, ais, maybe_comma, Space.Newline); // , | ||
| 875 | } else { | ||
| 876 | try renderExpression(allocator, ais, tree, expr, Space.Comma); // , | ||
| 877 | } | ||
| 878 | } | ||
| 879 | } | ||
| 880 | 843 | ||
| 881 | if (expr_index == exprs.len) { | 844 | try renderToken(ais, tree, end_last + 1, .none); // colon |
| 882 | break; | 845 | try renderExpression(gpa, ais, tree, slice.ast.sentinel, .none); |
| 883 | } | 846 | try renderToken(ais, tree, tree.lastToken(slice.ast.sentinel) + 1, space); // rbracket |
| 884 | } | 847 | } |
| 885 | } | ||
| 886 | 848 | ||
| 887 | return renderToken(tree, ais, rtoken, space); | 849 | fn renderAsmOutput( |
| 888 | } | 850 | gpa: *Allocator, |
| 851 | ais: *Ais, | ||
| 852 | tree: ast.Tree, | ||
| 853 | asm_output: ast.Node.Index, | ||
| 854 | space: Space, | ||
| 855 | ) Error!void { | ||
| 856 | const token_tags = tree.tokens.items(.tag); | ||
| 857 | const node_tags = tree.nodes.items(.tag); | ||
| 858 | const main_tokens = tree.nodes.items(.main_token); | ||
| 859 | const datas = tree.nodes.items(.data); | ||
| 860 | assert(node_tags[asm_output] == .asm_output); | ||
| 861 | const symbolic_name = main_tokens[asm_output]; | ||
| 862 | |||
| 863 | try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket | ||
| 864 | try renderToken(ais, tree, symbolic_name, .none); // ident | ||
| 865 | try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket | ||
| 866 | try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint" | ||
| 867 | try renderToken(ais, tree, symbolic_name + 3, .none); // lparen | ||
| 868 | |||
| 869 | if (token_tags[symbolic_name + 4] == .arrow) { | ||
| 870 | try renderToken(ais, tree, symbolic_name + 4, .space); // -> | ||
| 871 | try renderExpression(gpa, ais, tree, datas[asm_output].lhs, Space.none); | ||
| 872 | return renderToken(ais, tree, datas[asm_output].rhs, space); // rparen | ||
| 873 | } else { | ||
| 874 | try renderToken(ais, tree, symbolic_name + 4, .none); // ident | ||
| 875 | return renderToken(ais, tree, symbolic_name + 5, space); // rparen | ||
| 876 | } | ||
| 877 | } | ||
| 889 | 878 | ||
| 890 | // Single line | 879 | fn renderAsmInput( |
| 891 | try renderToken(tree, ais, lbrace, Space.Space); | 880 | gpa: *Allocator, |
| 892 | for (exprs) |expr, i| { | 881 | ais: *Ais, |
| 893 | if (i + 1 < exprs.len) { | 882 | tree: ast.Tree, |
| 894 | const next_expr = exprs[i + 1]; | 883 | asm_input: ast.Node.Index, |
| 895 | try renderExpression(allocator, ais, tree, expr, Space.None); | 884 | space: Space, |
| 896 | const comma = tree.nextToken(expr.*.lastToken()); | 885 | ) Error!void { |
| 897 | try renderToken(tree, ais, comma, Space.Space); // , | 886 | const node_tags = tree.nodes.items(.tag); |
| 898 | } else { | 887 | const main_tokens = tree.nodes.items(.main_token); |
| 899 | try renderExpression(allocator, ais, tree, expr, Space.Space); | 888 | const datas = tree.nodes.items(.data); |
| 900 | } | 889 | assert(node_tags[asm_input] == .asm_input); |
| 901 | } | 890 | const symbolic_name = main_tokens[asm_input]; |
| 891 | |||
| 892 | try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket | ||
| 893 | try renderToken(ais, tree, symbolic_name, .none); // ident | ||
| 894 | try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket | ||
| 895 | try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint" | ||
| 896 | try renderToken(ais, tree, symbolic_name + 3, .none); // lparen | ||
| 897 | try renderExpression(gpa, ais, tree, datas[asm_input].lhs, Space.none); | ||
| 898 | return renderToken(ais, tree, datas[asm_input].rhs, space); // rparen | ||
| 899 | } | ||
| 902 | 900 | ||
| 903 | return renderToken(tree, ais, rtoken, space); | 901 | fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: ast.Tree, var_decl: ast.full.VarDecl) Error!void { |
| 904 | }, | 902 | if (var_decl.visib_token) |visib_token| { |
| 903 | try renderToken(ais, tree, visib_token, Space.space); // pub | ||
| 904 | } | ||
| 905 | 905 | ||
| 906 | .StructInitializer, .StructInitializerDot => { | 906 | if (var_decl.extern_export_token) |extern_export_token| { |
| 907 | var rtoken: ast.TokenIndex = undefined; | 907 | try renderToken(ais, tree, extern_export_token, Space.space); // extern |
| 908 | var field_inits: []*ast.Node = undefined; | ||
| 909 | const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.tag) { | ||
| 910 | .StructInitializerDot => blk: { | ||
| 911 | const casted = @fieldParentPtr(ast.Node.StructInitializerDot, "base", base); | ||
| 912 | rtoken = casted.rtoken; | ||
| 913 | field_inits = casted.list(); | ||
| 914 | break :blk .{ .dot = casted.dot }; | ||
| 915 | }, | ||
| 916 | .StructInitializer => blk: { | ||
| 917 | const casted = @fieldParentPtr(ast.Node.StructInitializer, "base", base); | ||
| 918 | rtoken = casted.rtoken; | ||
| 919 | field_inits = casted.list(); | ||
| 920 | break :blk .{ .node = casted.lhs }; | ||
| 921 | }, | ||
| 922 | else => unreachable, | ||
| 923 | }; | ||
| 924 | 908 | ||
| 925 | const lbrace = switch (lhs) { | 909 | if (var_decl.lib_name) |lib_name| { |
| 926 | .dot => |dot| tree.nextToken(dot), | 910 | try renderToken(ais, tree, lib_name, Space.space); // "lib" |
| 927 | .node => |node| tree.nextToken(node.lastToken()), | 911 | } |
| 928 | }; | 912 | } |
| 929 | 913 | ||
| 930 | if (field_inits.len == 0) { | 914 | if (var_decl.threadlocal_token) |thread_local_token| { |
| 931 | switch (lhs) { | 915 | try renderToken(ais, tree, thread_local_token, Space.space); // threadlocal |
| 932 | .dot => |dot| try renderToken(tree, ais, dot, Space.None), | 916 | } |
| 933 | .node => |node| try renderExpression(allocator, ais, tree, node, Space.None), | ||
| 934 | } | ||
| 935 | 917 | ||
| 936 | { | 918 | if (var_decl.comptime_token) |comptime_token| { |
| 937 | ais.pushIndentNextLine(); | 919 | try renderToken(ais, tree, comptime_token, Space.space); // comptime |
| 938 | defer ais.popIndent(); | 920 | } |
| 939 | try renderToken(tree, ais, lbrace, Space.None); | ||
| 940 | } | ||
| 941 | 921 | ||
| 942 | return renderToken(tree, ais, rtoken, space); | 922 | try renderToken(ais, tree, var_decl.ast.mut_token, .space); // var |
| 943 | } | ||
| 944 | 923 | ||
| 945 | const src_has_trailing_comma = blk: { | 924 | const name_space = if (var_decl.ast.type_node == 0 and |
| 946 | const maybe_comma = tree.prevToken(rtoken); | 925 | (var_decl.ast.align_node != 0 or |
| 947 | break :blk tree.token_ids[maybe_comma] == .Comma; | 926 | var_decl.ast.section_node != 0 or |
| 948 | }; | 927 | var_decl.ast.init_node != 0)) |
| 928 | Space.space | ||
| 929 | else | ||
| 930 | Space.none; | ||
| 931 | try renderToken(ais, tree, var_decl.ast.mut_token + 1, name_space); // name | ||
| 949 | 932 | ||
| 950 | const src_same_line = blk: { | 933 | if (var_decl.ast.type_node != 0) { |
| 951 | const loc = tree.tokenLocation(tree.token_locs[lbrace].end, rtoken); | 934 | try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // : |
| 952 | break :blk loc.line == 0; | 935 | if (var_decl.ast.align_node != 0 or var_decl.ast.section_node != 0 or |
| 953 | }; | 936 | var_decl.ast.init_node != 0) |
| 937 | { | ||
| 938 | try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .space); | ||
| 939 | } else { | ||
| 940 | try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .none); | ||
| 941 | const semicolon = tree.lastToken(var_decl.ast.type_node) + 1; | ||
| 942 | return renderToken(ais, tree, semicolon, Space.newline); // ; | ||
| 943 | } | ||
| 944 | } | ||
| 954 | 945 | ||
| 955 | const expr_outputs_one_line = blk: { | 946 | if (var_decl.ast.align_node != 0) { |
| 956 | // render field expressions until a LF is found | 947 | const lparen = tree.firstToken(var_decl.ast.align_node) - 1; |
| 957 | for (field_inits) |field_init| { | 948 | const align_kw = lparen - 1; |
| 958 | var find_stream = std.io.findByteWriter('\n', std.io.null_writer); | 949 | const rparen = tree.lastToken(var_decl.ast.align_node) + 1; |
| 959 | var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, find_stream.writer()); | 950 | try renderToken(ais, tree, align_kw, Space.none); // align |
| 951 | try renderToken(ais, tree, lparen, Space.none); // ( | ||
| 952 | try renderExpression(gpa, ais, tree, var_decl.ast.align_node, Space.none); | ||
| 953 | if (var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0) { | ||
| 954 | try renderToken(ais, tree, rparen, .space); // ) | ||
| 955 | } else { | ||
| 956 | try renderToken(ais, tree, rparen, .none); // ) | ||
| 957 | return renderToken(ais, tree, rparen + 1, Space.newline); // ; | ||
| 958 | } | ||
| 959 | } | ||
| 960 | 960 | ||
| 961 | try renderExpression(allocator, &auto_indenting_stream, tree, field_init, Space.None); | 961 | if (var_decl.ast.section_node != 0) { |
| 962 | if (find_stream.byte_found) break :blk false; | 962 | const lparen = tree.firstToken(var_decl.ast.section_node) - 1; |
| 963 | } | 963 | const section_kw = lparen - 1; |
| 964 | break :blk true; | 964 | const rparen = tree.lastToken(var_decl.ast.section_node) + 1; |
| 965 | }; | 965 | try renderToken(ais, tree, section_kw, Space.none); // linksection |
| 966 | try renderToken(ais, tree, lparen, Space.none); // ( | ||
| 967 | try renderExpression(gpa, ais, tree, var_decl.ast.section_node, Space.none); | ||
| 968 | if (var_decl.ast.init_node != 0) { | ||
| 969 | try renderToken(ais, tree, rparen, .space); // ) | ||
| 970 | } else { | ||
| 971 | try renderToken(ais, tree, rparen, .none); // ) | ||
| 972 | return renderToken(ais, tree, rparen + 1, Space.newline); // ; | ||
| 973 | } | ||
| 974 | } | ||
| 966 | 975 | ||
| 967 | if (field_inits.len == 1) blk: { | 976 | assert(var_decl.ast.init_node != 0); |
| 968 | if (field_inits[0].cast(ast.Node.FieldInitializer)) |field_init| { | 977 | const eq_token = tree.firstToken(var_decl.ast.init_node) - 1; |
| 969 | switch (field_init.expr.tag) { | 978 | const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline; |
| 970 | .StructInitializer, | 979 | { |
| 971 | .StructInitializerDot, | 980 | ais.pushIndent(); |
| 972 | => break :blk, | 981 | try renderToken(ais, tree, eq_token, eq_space); // = |
| 973 | else => {}, | 982 | ais.popIndent(); |
| 974 | } | 983 | } |
| 975 | } | 984 | ais.pushIndentOneShot(); |
| 985 | try renderExpression(gpa, ais, tree, var_decl.ast.init_node, .semicolon); | ||
| 986 | } | ||
| 976 | 987 | ||
| 977 | // if the expression outputs to multiline, make this struct multiline | 988 | fn renderIf(gpa: *Allocator, ais: *Ais, tree: ast.Tree, if_node: ast.full.If, space: Space) Error!void { |
| 978 | if (!expr_outputs_one_line or src_has_trailing_comma) { | 989 | return renderWhile(gpa, ais, tree, .{ |
| 979 | break :blk; | 990 | .ast = .{ |
| 980 | } | 991 | .while_token = if_node.ast.if_token, |
| 992 | .cond_expr = if_node.ast.cond_expr, | ||
| 993 | .cont_expr = 0, | ||
| 994 | .then_expr = if_node.ast.then_expr, | ||
| 995 | .else_expr = if_node.ast.else_expr, | ||
| 996 | }, | ||
| 997 | .inline_token = null, | ||
| 998 | .label_token = null, | ||
| 999 | .payload_token = if_node.payload_token, | ||
| 1000 | .else_token = if_node.else_token, | ||
| 1001 | .error_token = if_node.error_token, | ||
| 1002 | }, space); | ||
| 1003 | } | ||
| 981 | 1004 | ||
| 982 | switch (lhs) { | 1005 | /// Note that this function is additionally used to render if and for expressions, with |
| 983 | .dot => |dot| try renderToken(tree, ais, dot, Space.None), | 1006 | /// respective values set to null. |
| 984 | .node => |node| try renderExpression(allocator, ais, tree, node, Space.None), | 1007 | fn renderWhile(gpa: *Allocator, ais: *Ais, tree: ast.Tree, while_node: ast.full.While, space: Space) Error!void { |
| 985 | } | 1008 | const node_tags = tree.nodes.items(.tag); |
| 986 | try renderToken(tree, ais, lbrace, Space.Space); | 1009 | const token_tags = tree.tokens.items(.tag); |
| 987 | try renderExpression(allocator, ais, tree, field_inits[0], Space.Space); | ||
| 988 | return renderToken(tree, ais, rtoken, space); | ||
| 989 | } | ||
| 990 | 1010 | ||
| 991 | if (!src_has_trailing_comma and src_same_line and expr_outputs_one_line) { | 1011 | if (while_node.label_token) |label| { |
| 992 | // render all on one line, no trailing comma | 1012 | try renderToken(ais, tree, label, .none); // label |
| 993 | switch (lhs) { | 1013 | try renderToken(ais, tree, label + 1, .space); // : |
| 994 | .dot => |dot| try renderToken(tree, ais, dot, Space.None), | 1014 | } |
| 995 | .node => |node| try renderExpression(allocator, ais, tree, node, Space.None), | ||
| 996 | } | ||
| 997 | try renderToken(tree, ais, lbrace, Space.Space); | ||
| 998 | 1015 | ||
| 999 | for (field_inits) |field_init, i| { | 1016 | if (while_node.inline_token) |inline_token| { |
| 1000 | if (i + 1 < field_inits.len) { | 1017 | try renderToken(ais, tree, inline_token, .space); // inline |
| 1001 | try renderExpression(allocator, ais, tree, field_init, Space.None); | 1018 | } |
| 1002 | 1019 | ||
| 1003 | const comma = tree.nextToken(field_init.lastToken()); | 1020 | try renderToken(ais, tree, while_node.ast.while_token, .space); // if |
| 1004 | try renderToken(tree, ais, comma, Space.Space); | 1021 | try renderToken(ais, tree, while_node.ast.while_token + 1, .none); // lparen |
| 1005 | } else { | 1022 | try renderExpression(gpa, ais, tree, while_node.ast.cond_expr, .none); // condition |
| 1006 | try renderExpression(allocator, ais, tree, field_init, Space.Space); | 1023 | |
| 1007 | } | 1024 | const then_tag = node_tags[while_node.ast.then_expr]; |
| 1025 | if (nodeIsBlock(then_tag) and !nodeIsIf(then_tag)) { | ||
| 1026 | if (while_node.payload_token) |payload_token| { | ||
| 1027 | try renderToken(ais, tree, payload_token - 2, .space); // rparen | ||
| 1028 | try renderToken(ais, tree, payload_token - 1, .none); // | | ||
| 1029 | const ident = blk: { | ||
| 1030 | if (token_tags[payload_token] == .asterisk) { | ||
| 1031 | try renderToken(ais, tree, payload_token, .none); // * | ||
| 1032 | break :blk payload_token + 1; | ||
| 1033 | } else { | ||
| 1034 | break :blk payload_token; | ||
| 1008 | } | 1035 | } |
| 1009 | |||
| 1010 | return renderToken(tree, ais, rtoken, space); | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | { | ||
| 1014 | switch (lhs) { | ||
| 1015 | .dot => |dot| try renderToken(tree, ais, dot, Space.None), | ||
| 1016 | .node => |node| try renderExpression(allocator, ais, tree, node, Space.None), | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | ais.pushIndentNextLine(); | ||
| 1020 | defer ais.popIndent(); | ||
| 1021 | |||
| 1022 | try renderToken(tree, ais, lbrace, Space.Newline); | ||
| 1023 | |||
| 1024 | for (field_inits) |field_init, i| { | ||
| 1025 | if (i + 1 < field_inits.len) { | ||
| 1026 | const next_field_init = field_inits[i + 1]; | ||
| 1027 | try renderExpression(allocator, ais, tree, field_init, Space.None); | ||
| 1028 | |||
| 1029 | const comma = tree.nextToken(field_init.lastToken()); | ||
| 1030 | try renderToken(tree, ais, comma, Space.Newline); | ||
| 1031 | |||
| 1032 | try renderExtraNewline(tree, ais, next_field_init); | ||
| 1033 | } else { | ||
| 1034 | try renderExpression(allocator, ais, tree, field_init, Space.Comma); | ||
| 1035 | } | ||
| 1036 | } | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | return renderToken(tree, ais, rtoken, space); | ||
| 1040 | }, | ||
| 1041 | |||
| 1042 | .Call => { | ||
| 1043 | const call = @fieldParentPtr(ast.Node.Call, "base", base); | ||
| 1044 | if (call.async_token) |async_token| { | ||
| 1045 | try renderToken(tree, ais, async_token, Space.Space); | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | try renderExpression(allocator, ais, tree, call.lhs, Space.None); | ||
| 1049 | |||
| 1050 | const lparen = tree.nextToken(call.lhs.lastToken()); | ||
| 1051 | |||
| 1052 | if (call.params_len == 0) { | ||
| 1053 | try renderToken(tree, ais, lparen, Space.None); | ||
| 1054 | return renderToken(tree, ais, call.rtoken, space); | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | const src_has_trailing_comma = blk: { | ||
| 1058 | const maybe_comma = tree.prevToken(call.rtoken); | ||
| 1059 | break :blk tree.token_ids[maybe_comma] == .Comma; | ||
| 1060 | }; | 1036 | }; |
| 1061 | 1037 | try renderToken(ais, tree, ident, .none); // identifier | |
| 1062 | if (src_has_trailing_comma) { | 1038 | const pipe = blk: { |
| 1063 | { | 1039 | if (token_tags[ident + 1] == .comma) { |
| 1064 | ais.pushIndent(); | 1040 | try renderToken(ais, tree, ident + 1, .space); // , |
| 1065 | defer ais.popIndent(); | 1041 | try renderToken(ais, tree, ident + 2, .none); // index |
| 1066 | 1042 | break :blk ident + 3; | |
| 1067 | try renderToken(tree, ais, lparen, Space.Newline); // ( | 1043 | } else { |
| 1068 | const params = call.params(); | 1044 | break :blk ident + 1; |
| 1069 | for (params) |param_node, i| { | ||
| 1070 | if (i + 1 < params.len) { | ||
| 1071 | const next_node = params[i + 1]; | ||
| 1072 | try renderExpression(allocator, ais, tree, param_node, Space.None); | ||
| 1073 | |||
| 1074 | // Unindent the comma for multiline string literals | ||
| 1075 | const maybe_multiline_string = param_node.firstToken(); | ||
| 1076 | const is_multiline_string = tree.token_ids[maybe_multiline_string] == .MultilineStringLiteralLine; | ||
| 1077 | if (is_multiline_string) ais.popIndent(); | ||
| 1078 | defer if (is_multiline_string) ais.pushIndent(); | ||
| 1079 | |||
| 1080 | const comma = tree.nextToken(param_node.lastToken()); | ||
| 1081 | try renderToken(tree, ais, comma, Space.Newline); // , | ||
| 1082 | try renderExtraNewline(tree, ais, next_node); | ||
| 1083 | } else { | ||
| 1084 | try renderExpression(allocator, ais, tree, param_node, Space.Comma); | ||
| 1085 | } | ||
| 1086 | } | ||
| 1087 | } | ||
| 1088 | return renderToken(tree, ais, call.rtoken, space); | ||
| 1089 | } | ||
| 1090 | |||
| 1091 | try renderToken(tree, ais, lparen, Space.None); // ( | ||
| 1092 | |||
| 1093 | const params = call.params(); | ||
| 1094 | for (params) |param_node, i| { | ||
| 1095 | const maybe_comment = param_node.firstToken() - 1; | ||
| 1096 | const maybe_multiline_string = param_node.firstToken(); | ||
| 1097 | if (tree.token_ids[maybe_multiline_string] == .MultilineStringLiteralLine or tree.token_ids[maybe_comment] == .LineComment) { | ||
| 1098 | ais.pushIndentOneShot(); | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | try renderExpression(allocator, ais, tree, param_node, Space.None); | ||
| 1102 | |||
| 1103 | if (i + 1 < params.len) { | ||
| 1104 | const comma = tree.nextToken(param_node.lastToken()); | ||
| 1105 | try renderToken(tree, ais, comma, Space.Space); | ||
| 1106 | } | ||
| 1107 | } | ||
| 1108 | return renderToken(tree, ais, call.rtoken, space); // ) | ||
| 1109 | }, | ||
| 1110 | |||
| 1111 | .ArrayAccess => { | ||
| 1112 | const suffix_op = base.castTag(.ArrayAccess).?; | ||
| 1113 | |||
| 1114 | const lbracket = tree.nextToken(suffix_op.lhs.lastToken()); | ||
| 1115 | const rbracket = tree.nextToken(suffix_op.index_expr.lastToken()); | ||
| 1116 | |||
| 1117 | try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None); | ||
| 1118 | try renderToken(tree, ais, lbracket, Space.None); // [ | ||
| 1119 | |||
| 1120 | const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment; | ||
| 1121 | const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment; | ||
| 1122 | { | ||
| 1123 | const new_space = if (ends_with_comment) Space.Newline else Space.None; | ||
| 1124 | |||
| 1125 | ais.pushIndent(); | ||
| 1126 | defer ais.popIndent(); | ||
| 1127 | try renderExpression(allocator, ais, tree, suffix_op.index_expr, new_space); | ||
| 1128 | } | ||
| 1129 | if (starts_with_comment) try ais.maybeInsertNewline(); | ||
| 1130 | return renderToken(tree, ais, rbracket, space); // ] | ||
| 1131 | }, | ||
| 1132 | |||
| 1133 | .Slice => { | ||
| 1134 | const suffix_op = base.castTag(.Slice).?; | ||
| 1135 | try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None); | ||
| 1136 | |||
| 1137 | const lbracket = tree.prevToken(suffix_op.start.firstToken()); | ||
| 1138 | const dotdot = tree.nextToken(suffix_op.start.lastToken()); | ||
| 1139 | |||
| 1140 | const after_start_space_bool = nodeCausesSliceOpSpace(suffix_op.start) or | ||
| 1141 | (if (suffix_op.end) |end| nodeCausesSliceOpSpace(end) else false); | ||
| 1142 | const after_start_space = if (after_start_space_bool) Space.Space else Space.None; | ||
| 1143 | const after_op_space = if (suffix_op.end != null) after_start_space else Space.None; | ||
| 1144 | |||
| 1145 | try renderToken(tree, ais, lbracket, Space.None); // [ | ||
| 1146 | try renderExpression(allocator, ais, tree, suffix_op.start, after_start_space); | ||
| 1147 | try renderToken(tree, ais, dotdot, after_op_space); // .. | ||
| 1148 | if (suffix_op.end) |end| { | ||
| 1149 | const after_end_space = if (suffix_op.sentinel != null) Space.Space else Space.None; | ||
| 1150 | try renderExpression(allocator, ais, tree, end, after_end_space); | ||
| 1151 | } | ||
| 1152 | if (suffix_op.sentinel) |sentinel| { | ||
| 1153 | const colon = tree.prevToken(sentinel.firstToken()); | ||
| 1154 | try renderToken(tree, ais, colon, Space.None); // : | ||
| 1155 | try renderExpression(allocator, ais, tree, sentinel, Space.None); | ||
| 1156 | } | ||
| 1157 | return renderToken(tree, ais, suffix_op.rtoken, space); // ] | ||
| 1158 | }, | ||
| 1159 | |||
| 1160 | .Deref => { | ||
| 1161 | const suffix_op = base.castTag(.Deref).?; | ||
| 1162 | |||
| 1163 | try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None); | ||
| 1164 | return renderToken(tree, ais, suffix_op.rtoken, space); // .* | ||
| 1165 | }, | ||
| 1166 | .UnwrapOptional => { | ||
| 1167 | const suffix_op = base.castTag(.UnwrapOptional).?; | ||
| 1168 | |||
| 1169 | try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None); | ||
| 1170 | try renderToken(tree, ais, tree.prevToken(suffix_op.rtoken), Space.None); // . | ||
| 1171 | return renderToken(tree, ais, suffix_op.rtoken, space); // ? | ||
| 1172 | }, | ||
| 1173 | |||
| 1174 | .Break => { | ||
| 1175 | const flow_expr = base.castTag(.Break).?; | ||
| 1176 | const maybe_rhs = flow_expr.getRHS(); | ||
| 1177 | const maybe_label = flow_expr.getLabel(); | ||
| 1178 | |||
| 1179 | if (maybe_label == null and maybe_rhs == null) { | ||
| 1180 | return renderToken(tree, ais, flow_expr.ltoken, space); // break | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | try renderToken(tree, ais, flow_expr.ltoken, Space.Space); // break | ||
| 1184 | if (maybe_label) |label| { | ||
| 1185 | const colon = tree.nextToken(flow_expr.ltoken); | ||
| 1186 | try renderToken(tree, ais, colon, Space.None); // : | ||
| 1187 | |||
| 1188 | if (maybe_rhs == null) { | ||
| 1189 | return renderToken(tree, ais, label, space); // label | ||
| 1190 | } | ||
| 1191 | try renderToken(tree, ais, label, Space.Space); // label | ||
| 1192 | } | ||
| 1193 | return renderExpression(allocator, ais, tree, maybe_rhs.?, space); | ||
| 1194 | }, | ||
| 1195 | |||
| 1196 | .Continue => { | ||
| 1197 | const flow_expr = base.castTag(.Continue).?; | ||
| 1198 | if (flow_expr.getLabel()) |label| { | ||
| 1199 | try renderToken(tree, ais, flow_expr.ltoken, Space.Space); // continue | ||
| 1200 | const colon = tree.nextToken(flow_expr.ltoken); | ||
| 1201 | try renderToken(tree, ais, colon, Space.None); // : | ||
| 1202 | return renderToken(tree, ais, label, space); // label | ||
| 1203 | } else { | ||
| 1204 | return renderToken(tree, ais, flow_expr.ltoken, space); // continue | ||
| 1205 | } | ||
| 1206 | }, | ||
| 1207 | |||
| 1208 | .Return => { | ||
| 1209 | const flow_expr = base.castTag(.Return).?; | ||
| 1210 | if (flow_expr.getRHS()) |rhs| { | ||
| 1211 | try renderToken(tree, ais, flow_expr.ltoken, Space.Space); | ||
| 1212 | return renderExpression(allocator, ais, tree, rhs, space); | ||
| 1213 | } else { | ||
| 1214 | return renderToken(tree, ais, flow_expr.ltoken, space); | ||
| 1215 | } | ||
| 1216 | }, | ||
| 1217 | |||
| 1218 | .Payload => { | ||
| 1219 | const payload = @fieldParentPtr(ast.Node.Payload, "base", base); | ||
| 1220 | |||
| 1221 | try renderToken(tree, ais, payload.lpipe, Space.None); | ||
| 1222 | try renderExpression(allocator, ais, tree, payload.error_symbol, Space.None); | ||
| 1223 | return renderToken(tree, ais, payload.rpipe, space); | ||
| 1224 | }, | ||
| 1225 | |||
| 1226 | .PointerPayload => { | ||
| 1227 | const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base); | ||
| 1228 | |||
| 1229 | try renderToken(tree, ais, payload.lpipe, Space.None); | ||
| 1230 | if (payload.ptr_token) |ptr_token| { | ||
| 1231 | try renderToken(tree, ais, ptr_token, Space.None); | ||
| 1232 | } | ||
| 1233 | try renderExpression(allocator, ais, tree, payload.value_symbol, Space.None); | ||
| 1234 | return renderToken(tree, ais, payload.rpipe, space); | ||
| 1235 | }, | ||
| 1236 | |||
| 1237 | .PointerIndexPayload => { | ||
| 1238 | const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base); | ||
| 1239 | |||
| 1240 | try renderToken(tree, ais, payload.lpipe, Space.None); | ||
| 1241 | if (payload.ptr_token) |ptr_token| { | ||
| 1242 | try renderToken(tree, ais, ptr_token, Space.None); | ||
| 1243 | } | ||
| 1244 | try renderExpression(allocator, ais, tree, payload.value_symbol, Space.None); | ||
| 1245 | |||
| 1246 | if (payload.index_symbol) |index_symbol| { | ||
| 1247 | const comma = tree.nextToken(payload.value_symbol.lastToken()); | ||
| 1248 | |||
| 1249 | try renderToken(tree, ais, comma, Space.Space); | ||
| 1250 | try renderExpression(allocator, ais, tree, index_symbol, Space.None); | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | return renderToken(tree, ais, payload.rpipe, space); | ||
| 1254 | }, | ||
| 1255 | |||
| 1256 | .GroupedExpression => { | ||
| 1257 | const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); | ||
| 1258 | |||
| 1259 | try renderToken(tree, ais, grouped_expr.lparen, Space.None); | ||
| 1260 | { | ||
| 1261 | ais.pushIndentOneShot(); | ||
| 1262 | try renderExpression(allocator, ais, tree, grouped_expr.expr, Space.None); | ||
| 1263 | } | ||
| 1264 | return renderToken(tree, ais, grouped_expr.rparen, space); | ||
| 1265 | }, | ||
| 1266 | |||
| 1267 | .FieldInitializer => { | ||
| 1268 | const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base); | ||
| 1269 | |||
| 1270 | try renderToken(tree, ais, field_init.period_token, Space.None); // . | ||
| 1271 | try renderToken(tree, ais, field_init.name_token, Space.Space); // name | ||
| 1272 | try renderToken(tree, ais, tree.nextToken(field_init.name_token), Space.Space); // = | ||
| 1273 | return renderExpression(allocator, ais, tree, field_init.expr, space); | ||
| 1274 | }, | ||
| 1275 | |||
| 1276 | .ContainerDecl => { | ||
| 1277 | const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base); | ||
| 1278 | |||
| 1279 | if (container_decl.layout_token) |layout_token| { | ||
| 1280 | try renderToken(tree, ais, layout_token, Space.Space); | ||
| 1281 | } | ||
| 1282 | |||
| 1283 | switch (container_decl.init_arg_expr) { | ||
| 1284 | .None => { | ||
| 1285 | try renderToken(tree, ais, container_decl.kind_token, Space.Space); // union | ||
| 1286 | }, | ||
| 1287 | .Enum => |enum_tag_type| { | ||
| 1288 | try renderToken(tree, ais, container_decl.kind_token, Space.None); // union | ||
| 1289 | |||
| 1290 | const lparen = tree.nextToken(container_decl.kind_token); | ||
| 1291 | const enum_token = tree.nextToken(lparen); | ||
| 1292 | |||
| 1293 | try renderToken(tree, ais, lparen, Space.None); // ( | ||
| 1294 | try renderToken(tree, ais, enum_token, Space.None); // enum | ||
| 1295 | |||
| 1296 | if (enum_tag_type) |expr| { | ||
| 1297 | try renderToken(tree, ais, tree.nextToken(enum_token), Space.None); // ( | ||
| 1298 | try renderExpression(allocator, ais, tree, expr, Space.None); | ||
| 1299 | |||
| 1300 | const rparen = tree.nextToken(expr.lastToken()); | ||
| 1301 | try renderToken(tree, ais, rparen, Space.None); // ) | ||
| 1302 | try renderToken(tree, ais, tree.nextToken(rparen), Space.Space); // ) | ||
| 1303 | } else { | ||
| 1304 | try renderToken(tree, ais, tree.nextToken(enum_token), Space.Space); // ) | ||
| 1305 | } | ||
| 1306 | }, | ||
| 1307 | .Type => |type_expr| { | ||
| 1308 | try renderToken(tree, ais, container_decl.kind_token, Space.None); // union | ||
| 1309 | |||
| 1310 | const lparen = tree.nextToken(container_decl.kind_token); | ||
| 1311 | const rparen = tree.nextToken(type_expr.lastToken()); | ||
| 1312 | |||
| 1313 | try renderToken(tree, ais, lparen, Space.None); // ( | ||
| 1314 | try renderExpression(allocator, ais, tree, type_expr, Space.None); | ||
| 1315 | try renderToken(tree, ais, rparen, Space.Space); // ) | ||
| 1316 | }, | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | if (container_decl.fields_and_decls_len == 0) { | ||
| 1320 | { | ||
| 1321 | ais.pushIndentNextLine(); | ||
| 1322 | defer ais.popIndent(); | ||
| 1323 | try renderToken(tree, ais, container_decl.lbrace_token, Space.None); // { | ||
| 1324 | } | 1045 | } |
| 1325 | return renderToken(tree, ais, container_decl.rbrace_token, space); // } | ||
| 1326 | } | ||
| 1327 | |||
| 1328 | const src_has_trailing_comma = blk: { | ||
| 1329 | var maybe_comma = tree.prevToken(container_decl.lastToken()); | ||
| 1330 | // Doc comments for a field may also appear after the comma, eg. | ||
| 1331 | // field_name: T, // comment attached to field_name | ||
| 1332 | if (tree.token_ids[maybe_comma] == .DocComment) | ||
| 1333 | maybe_comma = tree.prevToken(maybe_comma); | ||
| 1334 | break :blk tree.token_ids[maybe_comma] == .Comma; | ||
| 1335 | }; | 1046 | }; |
| 1047 | const brace_space = if (while_node.ast.cont_expr == 0 and ais.isLineOverIndented()) | ||
| 1048 | Space.newline | ||
| 1049 | else | ||
| 1050 | Space.space; | ||
| 1051 | try renderToken(ais, tree, pipe, brace_space); // | | ||
| 1052 | } else { | ||
| 1053 | const rparen = tree.lastToken(while_node.ast.cond_expr) + 1; | ||
| 1054 | const brace_space = if (while_node.ast.cont_expr == 0 and ais.isLineOverIndented()) | ||
| 1055 | Space.newline | ||
| 1056 | else | ||
| 1057 | Space.space; | ||
| 1058 | try renderToken(ais, tree, rparen, brace_space); // rparen | ||
| 1059 | } | ||
| 1060 | if (while_node.ast.cont_expr != 0) { | ||
| 1061 | const rparen = tree.lastToken(while_node.ast.cont_expr) + 1; | ||
| 1062 | const lparen = tree.firstToken(while_node.ast.cont_expr) - 1; | ||
| 1063 | try renderToken(ais, tree, lparen - 1, .space); // : | ||
| 1064 | try renderToken(ais, tree, lparen, .none); // lparen | ||
| 1065 | try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none); | ||
| 1066 | const brace_space: Space = if (ais.isLineOverIndented()) .newline else .space; | ||
| 1067 | try renderToken(ais, tree, rparen, brace_space); // rparen | ||
| 1068 | } | ||
| 1069 | if (while_node.ast.else_expr != 0) { | ||
| 1070 | try renderExpression(gpa, ais, tree, while_node.ast.then_expr, Space.space); | ||
| 1071 | try renderToken(ais, tree, while_node.else_token, .space); // else | ||
| 1072 | if (while_node.error_token) |error_token| { | ||
| 1073 | try renderToken(ais, tree, error_token - 1, .none); // | | ||
| 1074 | try renderToken(ais, tree, error_token, .none); // identifier | ||
| 1075 | try renderToken(ais, tree, error_token + 1, .space); // | | ||
| 1076 | } | ||
| 1077 | return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space); | ||
| 1078 | } else { | ||
| 1079 | return renderExpression(gpa, ais, tree, while_node.ast.then_expr, space); | ||
| 1080 | } | ||
| 1081 | } | ||
| 1336 | 1082 | ||
| 1337 | const fields_and_decls = container_decl.fieldsAndDecls(); | 1083 | const rparen = tree.lastToken(while_node.ast.cond_expr) + 1; |
| 1338 | 1084 | const last_then_token = tree.lastToken(while_node.ast.then_expr); | |
| 1339 | // Check if the first declaration and the { are on the same line | 1085 | const src_has_newline = !tree.tokensOnSameLine(rparen, last_then_token); |
| 1340 | const src_has_newline = !tree.tokensOnSameLine( | 1086 | |
| 1341 | container_decl.lbrace_token, | 1087 | if (src_has_newline) { |
| 1342 | fields_and_decls[0].firstToken(), | 1088 | if (while_node.payload_token) |payload_token| { |
| 1343 | ); | 1089 | try renderToken(ais, tree, payload_token - 2, .space); // rparen |
| 1344 | 1090 | try renderToken(ais, tree, payload_token - 1, .none); // | | |
| 1345 | // We can only print all the elements in-line if all the | 1091 | const ident = blk: { |
| 1346 | // declarations inside are fields | 1092 | if (token_tags[payload_token] == .asterisk) { |
| 1347 | const src_has_only_fields = blk: { | 1093 | try renderToken(ais, tree, payload_token, .none); // * |
| 1348 | for (fields_and_decls) |decl| { | 1094 | break :blk payload_token + 1; |
| 1349 | if (decl.tag != .ContainerField) break :blk false; | 1095 | } else { |
| 1096 | break :blk payload_token; | ||
| 1350 | } | 1097 | } |
| 1351 | break :blk true; | ||
| 1352 | }; | 1098 | }; |
| 1353 | 1099 | try renderToken(ais, tree, ident, .none); // identifier | |
| 1354 | if (src_has_trailing_comma or !src_has_only_fields) { | 1100 | const pipe = blk: { |
| 1355 | // One declaration per line | 1101 | if (token_tags[ident + 1] == .comma) { |
| 1356 | ais.pushIndentNextLine(); | 1102 | try renderToken(ais, tree, ident + 1, .space); // , |
| 1357 | defer ais.popIndent(); | 1103 | try renderToken(ais, tree, ident + 2, .none); // index |
| 1358 | try renderToken(tree, ais, container_decl.lbrace_token, .Newline); // { | 1104 | break :blk ident + 3; |
| 1359 | |||
| 1360 | for (fields_and_decls) |decl, i| { | ||
| 1361 | try renderContainerDecl(allocator, ais, tree, decl, .Newline); | ||
| 1362 | |||
| 1363 | if (i + 1 < fields_and_decls.len) { | ||
| 1364 | try renderExtraNewline(tree, ais, fields_and_decls[i + 1]); | ||
| 1365 | } | ||
| 1366 | } | ||
| 1367 | } else if (src_has_newline) { | ||
| 1368 | // All the declarations on the same line, but place the items on | ||
| 1369 | // their own line | ||
| 1370 | try renderToken(tree, ais, container_decl.lbrace_token, .Newline); // { | ||
| 1371 | |||
| 1372 | ais.pushIndent(); | ||
| 1373 | defer ais.popIndent(); | ||
| 1374 | |||
| 1375 | for (fields_and_decls) |decl, i| { | ||
| 1376 | const space_after_decl: Space = if (i + 1 >= fields_and_decls.len) .Newline else .Space; | ||
| 1377 | try renderContainerDecl(allocator, ais, tree, decl, space_after_decl); | ||
| 1378 | } | ||
| 1379 | } else { | ||
| 1380 | // All the declarations on the same line | ||
| 1381 | try renderToken(tree, ais, container_decl.lbrace_token, .Space); // { | ||
| 1382 | |||
| 1383 | for (fields_and_decls) |decl| { | ||
| 1384 | try renderContainerDecl(allocator, ais, tree, decl, .Space); | ||
| 1385 | } | ||
| 1386 | } | ||
| 1387 | |||
| 1388 | return renderToken(tree, ais, container_decl.rbrace_token, space); // } | ||
| 1389 | }, | ||
| 1390 | |||
| 1391 | .ErrorSetDecl => { | ||
| 1392 | const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base); | ||
| 1393 | |||
| 1394 | const lbrace = tree.nextToken(err_set_decl.error_token); | ||
| 1395 | |||
| 1396 | if (err_set_decl.decls_len == 0) { | ||
| 1397 | try renderToken(tree, ais, err_set_decl.error_token, Space.None); | ||
| 1398 | try renderToken(tree, ais, lbrace, Space.None); | ||
| 1399 | return renderToken(tree, ais, err_set_decl.rbrace_token, space); | ||
| 1400 | } | ||
| 1401 | |||
| 1402 | if (err_set_decl.decls_len == 1) blk: { | ||
| 1403 | const node = err_set_decl.decls()[0]; | ||
| 1404 | |||
| 1405 | // if there are any doc comments or same line comments | ||
| 1406 | // don't try to put it all on one line | ||
| 1407 | if (node.cast(ast.Node.ErrorTag)) |tag| { | ||
| 1408 | if (tag.doc_comments != null) break :blk; | ||
| 1409 | } else { | 1105 | } else { |
| 1410 | break :blk; | 1106 | break :blk ident + 1; |
| 1411 | } | 1107 | } |
| 1412 | |||
| 1413 | try renderToken(tree, ais, err_set_decl.error_token, Space.None); // error | ||
| 1414 | try renderToken(tree, ais, lbrace, Space.None); // { | ||
| 1415 | try renderExpression(allocator, ais, tree, node, Space.None); | ||
| 1416 | return renderToken(tree, ais, err_set_decl.rbrace_token, space); // } | ||
| 1417 | } | ||
| 1418 | |||
| 1419 | try renderToken(tree, ais, err_set_decl.error_token, Space.None); // error | ||
| 1420 | |||
| 1421 | const src_has_trailing_comma = blk: { | ||
| 1422 | const maybe_comma = tree.prevToken(err_set_decl.rbrace_token); | ||
| 1423 | break :blk tree.token_ids[maybe_comma] == .Comma; | ||
| 1424 | }; | 1108 | }; |
| 1425 | 1109 | const after_space: Space = if (while_node.ast.cont_expr != 0) .space else .newline; | |
| 1426 | if (src_has_trailing_comma) { | 1110 | try renderToken(ais, tree, pipe, after_space); // | |
| 1427 | { | 1111 | } else { |
| 1428 | ais.pushIndent(); | 1112 | ais.pushIndent(); |
| 1429 | defer ais.popIndent(); | 1113 | const after_space: Space = if (while_node.ast.cont_expr != 0) .space else .newline; |
| 1430 | 1114 | try renderToken(ais, tree, rparen, after_space); // rparen | |
| 1431 | try renderToken(tree, ais, lbrace, Space.Newline); // { | 1115 | ais.popIndent(); |
| 1432 | const decls = err_set_decl.decls(); | 1116 | } |
| 1433 | for (decls) |node, i| { | 1117 | if (while_node.ast.cont_expr != 0) { |
| 1434 | if (i + 1 < decls.len) { | 1118 | const cont_rparen = tree.lastToken(while_node.ast.cont_expr) + 1; |
| 1435 | try renderExpression(allocator, ais, tree, node, Space.None); | 1119 | const cont_lparen = tree.firstToken(while_node.ast.cont_expr) - 1; |
| 1436 | try renderToken(tree, ais, tree.nextToken(node.lastToken()), Space.Newline); // , | 1120 | try renderToken(ais, tree, cont_lparen - 1, .space); // : |
| 1437 | 1121 | try renderToken(ais, tree, cont_lparen, .none); // lparen | |
| 1438 | try renderExtraNewline(tree, ais, decls[i + 1]); | 1122 | try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none); |
| 1439 | } else { | 1123 | try renderToken(ais, tree, cont_rparen, .newline); // rparen |
| 1440 | try renderExpression(allocator, ais, tree, node, Space.Comma); | 1124 | } |
| 1441 | } | 1125 | if (while_node.ast.else_expr != 0) { |
| 1442 | } | 1126 | ais.pushIndent(); |
| 1127 | try renderExpression(gpa, ais, tree, while_node.ast.then_expr, Space.newline); | ||
| 1128 | ais.popIndent(); | ||
| 1129 | const else_is_block = nodeIsBlock(node_tags[while_node.ast.else_expr]); | ||
| 1130 | if (else_is_block) { | ||
| 1131 | try renderToken(ais, tree, while_node.else_token, .space); // else | ||
| 1132 | if (while_node.error_token) |error_token| { | ||
| 1133 | try renderToken(ais, tree, error_token - 1, .none); // | | ||
| 1134 | try renderToken(ais, tree, error_token, .none); // identifier | ||
| 1135 | try renderToken(ais, tree, error_token + 1, .space); // | | ||
| 1443 | } | 1136 | } |
| 1444 | 1137 | return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space); | |
| 1445 | return renderToken(tree, ais, err_set_decl.rbrace_token, space); // } | ||
| 1446 | } else { | 1138 | } else { |
| 1447 | try renderToken(tree, ais, lbrace, Space.Space); // { | 1139 | if (while_node.error_token) |error_token| { |
| 1448 | 1140 | try renderToken(ais, tree, while_node.else_token, .space); // else | |
| 1449 | const decls = err_set_decl.decls(); | 1141 | try renderToken(ais, tree, error_token - 1, .none); // | |
| 1450 | for (decls) |node, i| { | 1142 | try renderToken(ais, tree, error_token, .none); // identifier |
| 1451 | if (i + 1 < decls.len) { | 1143 | try renderToken(ais, tree, error_token + 1, .space); // | |
| 1452 | try renderExpression(allocator, ais, tree, node, Space.None); | 1144 | } else { |
| 1453 | 1145 | try renderToken(ais, tree, while_node.else_token, .newline); // else | |
| 1454 | const comma_token = tree.nextToken(node.lastToken()); | ||
| 1455 | assert(tree.token_ids[comma_token] == .Comma); | ||
| 1456 | try renderToken(tree, ais, comma_token, Space.Space); // , | ||
| 1457 | try renderExtraNewline(tree, ais, decls[i + 1]); | ||
| 1458 | } else { | ||
| 1459 | try renderExpression(allocator, ais, tree, node, Space.Space); | ||
| 1460 | } | ||
| 1461 | } | ||
| 1462 | |||
| 1463 | return renderToken(tree, ais, err_set_decl.rbrace_token, space); // } | ||
| 1464 | } | ||
| 1465 | }, | ||
| 1466 | |||
| 1467 | .ErrorTag => { | ||
| 1468 | const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", base); | ||
| 1469 | |||
| 1470 | try renderDocComments(tree, ais, tag, tag.doc_comments); | ||
| 1471 | return renderToken(tree, ais, tag.name_token, space); // name | ||
| 1472 | }, | ||
| 1473 | |||
| 1474 | .MultilineStringLiteral => { | ||
| 1475 | const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); | ||
| 1476 | |||
| 1477 | { | ||
| 1478 | const locked_indents = ais.lockOneShotIndent(); | ||
| 1479 | defer { | ||
| 1480 | var i: u8 = 0; | ||
| 1481 | while (i < locked_indents) : (i += 1) ais.popIndent(); | ||
| 1482 | } | ||
| 1483 | try ais.maybeInsertNewline(); | ||
| 1484 | |||
| 1485 | for (multiline_str_literal.lines()) |t| try renderToken(tree, ais, t, Space.None); | ||
| 1486 | } | ||
| 1487 | }, | ||
| 1488 | |||
| 1489 | .BuiltinCall => { | ||
| 1490 | const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); | ||
| 1491 | |||
| 1492 | // TODO remove after 0.7.0 release | ||
| 1493 | if (mem.eql(u8, tree.tokenSlice(builtin_call.builtin_token), "@OpaqueType")) | ||
| 1494 | return ais.writer().writeAll("opaque {}"); | ||
| 1495 | |||
| 1496 | // TODO remove after 0.7.0 release | ||
| 1497 | { | ||
| 1498 | const params = builtin_call.paramsConst(); | ||
| 1499 | if (mem.eql(u8, tree.tokenSlice(builtin_call.builtin_token), "@Type") and | ||
| 1500 | params.len == 1) | ||
| 1501 | { | ||
| 1502 | if (params[0].castTag(.EnumLiteral)) |enum_literal| | ||
| 1503 | if (mem.eql(u8, tree.tokenSlice(enum_literal.name), "Opaque")) | ||
| 1504 | return ais.writer().writeAll("opaque {}"); | ||
| 1505 | } | 1146 | } |
| 1147 | try renderExpressionIndented(gpa, ais, tree, while_node.ast.else_expr, space); | ||
| 1148 | return; | ||
| 1506 | } | 1149 | } |
| 1150 | } else { | ||
| 1151 | try renderExpressionIndented(gpa, ais, tree, while_node.ast.then_expr, space); | ||
| 1152 | return; | ||
| 1153 | } | ||
| 1154 | } | ||
| 1507 | 1155 | ||
| 1508 | try renderToken(tree, ais, builtin_call.builtin_token, Space.None); // @name | 1156 | // Render everything on a single line. |
| 1509 | |||
| 1510 | const src_params_trailing_comma = blk: { | ||
| 1511 | if (builtin_call.params_len == 0) break :blk false; | ||
| 1512 | const last_node = builtin_call.params()[builtin_call.params_len - 1]; | ||
| 1513 | const maybe_comma = tree.nextToken(last_node.lastToken()); | ||
| 1514 | break :blk tree.token_ids[maybe_comma] == .Comma; | ||
| 1515 | }; | ||
| 1516 | |||
| 1517 | const lparen = tree.nextToken(builtin_call.builtin_token); | ||
| 1518 | |||
| 1519 | if (!src_params_trailing_comma) { | ||
| 1520 | try renderToken(tree, ais, lparen, Space.None); // ( | ||
| 1521 | |||
| 1522 | // render all on one line, no trailing comma | ||
| 1523 | const params = builtin_call.params(); | ||
| 1524 | for (params) |param_node, i| { | ||
| 1525 | const maybe_comment = param_node.firstToken() - 1; | ||
| 1526 | if (param_node.*.tag == .MultilineStringLiteral or tree.token_ids[maybe_comment] == .LineComment) { | ||
| 1527 | ais.pushIndentOneShot(); | ||
| 1528 | } | ||
| 1529 | try renderExpression(allocator, ais, tree, param_node, Space.None); | ||
| 1530 | 1157 | ||
| 1531 | if (i + 1 < params.len) { | 1158 | if (while_node.payload_token) |payload_token| { |
| 1532 | const comma_token = tree.nextToken(param_node.lastToken()); | 1159 | assert(payload_token - 2 == rparen); |
| 1533 | try renderToken(tree, ais, comma_token, Space.Space); // , | 1160 | try renderToken(ais, tree, payload_token - 2, .space); // ) |
| 1534 | } | 1161 | try renderToken(ais, tree, payload_token - 1, .none); // | |
| 1535 | } | 1162 | const ident = blk: { |
| 1163 | if (token_tags[payload_token] == .asterisk) { | ||
| 1164 | try renderToken(ais, tree, payload_token, .none); // * | ||
| 1165 | break :blk payload_token + 1; | ||
| 1536 | } else { | 1166 | } else { |
| 1537 | // one param per line | 1167 | break :blk payload_token; |
| 1538 | ais.pushIndent(); | ||
| 1539 | defer ais.popIndent(); | ||
| 1540 | try renderToken(tree, ais, lparen, Space.Newline); // ( | ||
| 1541 | |||
| 1542 | for (builtin_call.params()) |param_node| { | ||
| 1543 | try renderExpression(allocator, ais, tree, param_node, Space.Comma); | ||
| 1544 | } | ||
| 1545 | } | 1168 | } |
| 1546 | 1169 | }; | |
| 1547 | return renderToken(tree, ais, builtin_call.rparen_token, space); // ) | 1170 | try renderToken(ais, tree, ident, .none); // identifier |
| 1548 | }, | 1171 | const pipe = blk: { |
| 1549 | 1172 | if (token_tags[ident + 1] == .comma) { | |
| 1550 | .FnProto => { | 1173 | try renderToken(ais, tree, ident + 1, .space); // , |
| 1551 | const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base); | 1174 | try renderToken(ais, tree, ident + 2, .none); // index |
| 1552 | 1175 | break :blk ident + 3; | |
| 1553 | if (fn_proto.getVisibToken()) |visib_token_index| { | ||
| 1554 | const visib_token = tree.token_ids[visib_token_index]; | ||
| 1555 | assert(visib_token == .Keyword_pub or visib_token == .Keyword_export); | ||
| 1556 | |||
| 1557 | try renderToken(tree, ais, visib_token_index, Space.Space); // pub | ||
| 1558 | } | ||
| 1559 | |||
| 1560 | if (fn_proto.getExternExportInlineToken()) |extern_export_inline_token| { | ||
| 1561 | if (fn_proto.getIsExternPrototype() == null and fn_proto.getIsInline() == null) | ||
| 1562 | try renderToken(tree, ais, extern_export_inline_token, Space.Space); // extern/export/inline | ||
| 1563 | } | ||
| 1564 | |||
| 1565 | if (fn_proto.getLibName()) |lib_name| { | ||
| 1566 | try renderExpression(allocator, ais, tree, lib_name, Space.Space); | ||
| 1567 | } | ||
| 1568 | |||
| 1569 | const lparen = if (fn_proto.getNameToken()) |name_token| blk: { | ||
| 1570 | try renderToken(tree, ais, fn_proto.fn_token, Space.Space); // fn | ||
| 1571 | try renderToken(tree, ais, name_token, Space.None); // name | ||
| 1572 | break :blk tree.nextToken(name_token); | ||
| 1573 | } else blk: { | ||
| 1574 | try renderToken(tree, ais, fn_proto.fn_token, Space.Space); // fn | ||
| 1575 | break :blk tree.nextToken(fn_proto.fn_token); | ||
| 1576 | }; | ||
| 1577 | assert(tree.token_ids[lparen] == .LParen); | ||
| 1578 | |||
| 1579 | const rparen = tree.prevToken( | ||
| 1580 | // the first token for the annotation expressions is the left | ||
| 1581 | // parenthesis, hence the need for two prevToken | ||
| 1582 | if (fn_proto.getAlignExpr()) |align_expr| | ||
| 1583 | tree.prevToken(tree.prevToken(align_expr.firstToken())) | ||
| 1584 | else if (fn_proto.getSectionExpr()) |section_expr| | ||
| 1585 | tree.prevToken(tree.prevToken(section_expr.firstToken())) | ||
| 1586 | else if (fn_proto.getCallconvExpr()) |callconv_expr| | ||
| 1587 | tree.prevToken(tree.prevToken(callconv_expr.firstToken())) | ||
| 1588 | else switch (fn_proto.return_type) { | ||
| 1589 | .Explicit => |node| node.firstToken(), | ||
| 1590 | .InferErrorSet => |node| tree.prevToken(node.firstToken()), | ||
| 1591 | .Invalid => unreachable, | ||
| 1592 | }, | ||
| 1593 | ); | ||
| 1594 | assert(tree.token_ids[rparen] == .RParen); | ||
| 1595 | |||
| 1596 | const src_params_trailing_comma = blk: { | ||
| 1597 | const maybe_comma = tree.token_ids[rparen - 1]; | ||
| 1598 | break :blk maybe_comma == .Comma or maybe_comma == .LineComment; | ||
| 1599 | }; | ||
| 1600 | |||
| 1601 | if (!src_params_trailing_comma) { | ||
| 1602 | try renderToken(tree, ais, lparen, Space.None); // ( | ||
| 1603 | |||
| 1604 | // render all on one line, no trailing comma | ||
| 1605 | for (fn_proto.params()) |param_decl, i| { | ||
| 1606 | try renderParamDecl(allocator, ais, tree, param_decl, Space.None); | ||
| 1607 | |||
| 1608 | if (i + 1 < fn_proto.params_len or fn_proto.getVarArgsToken() != null) { | ||
| 1609 | const comma = tree.nextToken(param_decl.lastToken()); | ||
| 1610 | try renderToken(tree, ais, comma, Space.Space); // , | ||
| 1611 | } | ||
| 1612 | } | ||
| 1613 | if (fn_proto.getVarArgsToken()) |var_args_token| { | ||
| 1614 | try renderToken(tree, ais, var_args_token, Space.None); | ||
| 1615 | } | ||
| 1616 | } else { | 1176 | } else { |
| 1617 | // one param per line | 1177 | break :blk ident + 1; |
| 1618 | ais.pushIndent(); | ||
| 1619 | defer ais.popIndent(); | ||
| 1620 | try renderToken(tree, ais, lparen, Space.Newline); // ( | ||
| 1621 | |||
| 1622 | for (fn_proto.params()) |param_decl| { | ||
| 1623 | try renderParamDecl(allocator, ais, tree, param_decl, Space.Comma); | ||
| 1624 | } | ||
| 1625 | if (fn_proto.getVarArgsToken()) |var_args_token| { | ||
| 1626 | try renderToken(tree, ais, var_args_token, Space.Comma); | ||
| 1627 | } | ||
| 1628 | } | 1178 | } |
| 1179 | }; | ||
| 1180 | try renderToken(ais, tree, pipe, .space); // | | ||
| 1181 | } else { | ||
| 1182 | try renderToken(ais, tree, rparen, .space); // ) | ||
| 1183 | } | ||
| 1629 | 1184 | ||
| 1630 | try renderToken(tree, ais, rparen, Space.Space); // ) | 1185 | if (while_node.ast.cont_expr != 0) { |
| 1631 | 1186 | const cont_rparen = tree.lastToken(while_node.ast.cont_expr) + 1; | |
| 1632 | if (fn_proto.getAlignExpr()) |align_expr| { | 1187 | const cont_lparen = tree.firstToken(while_node.ast.cont_expr) - 1; |
| 1633 | const align_rparen = tree.nextToken(align_expr.lastToken()); | 1188 | try renderToken(ais, tree, cont_lparen - 1, .space); // : |
| 1634 | const align_lparen = tree.prevToken(align_expr.firstToken()); | 1189 | try renderToken(ais, tree, cont_lparen, .none); // lparen |
| 1635 | const align_kw = tree.prevToken(align_lparen); | 1190 | try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none); |
| 1636 | 1191 | try renderToken(ais, tree, cont_rparen, .space); // rparen | |
| 1637 | try renderToken(tree, ais, align_kw, Space.None); // align | 1192 | } |
| 1638 | try renderToken(tree, ais, align_lparen, Space.None); // ( | ||
| 1639 | try renderExpression(allocator, ais, tree, align_expr, Space.None); | ||
| 1640 | try renderToken(tree, ais, align_rparen, Space.Space); // ) | ||
| 1641 | } | ||
| 1642 | |||
| 1643 | if (fn_proto.getSectionExpr()) |section_expr| { | ||
| 1644 | const section_rparen = tree.nextToken(section_expr.lastToken()); | ||
| 1645 | const section_lparen = tree.prevToken(section_expr.firstToken()); | ||
| 1646 | const section_kw = tree.prevToken(section_lparen); | ||
| 1647 | |||
| 1648 | try renderToken(tree, ais, section_kw, Space.None); // section | ||
| 1649 | try renderToken(tree, ais, section_lparen, Space.None); // ( | ||
| 1650 | try renderExpression(allocator, ais, tree, section_expr, Space.None); | ||
| 1651 | try renderToken(tree, ais, section_rparen, Space.Space); // ) | ||
| 1652 | } | ||
| 1653 | 1193 | ||
| 1654 | if (fn_proto.getCallconvExpr()) |callconv_expr| { | 1194 | if (while_node.ast.else_expr != 0) { |
| 1655 | const callconv_rparen = tree.nextToken(callconv_expr.lastToken()); | 1195 | try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .space); |
| 1656 | const callconv_lparen = tree.prevToken(callconv_expr.firstToken()); | 1196 | try renderToken(ais, tree, while_node.else_token, .space); // else |
| 1657 | const callconv_kw = tree.prevToken(callconv_lparen); | ||
| 1658 | |||
| 1659 | try renderToken(tree, ais, callconv_kw, Space.None); // callconv | ||
| 1660 | try renderToken(tree, ais, callconv_lparen, Space.None); // ( | ||
| 1661 | try renderExpression(allocator, ais, tree, callconv_expr, Space.None); | ||
| 1662 | try renderToken(tree, ais, callconv_rparen, Space.Space); // ) | ||
| 1663 | } else if (fn_proto.getIsExternPrototype() != null) { | ||
| 1664 | try ais.writer().writeAll("callconv(.C) "); | ||
| 1665 | } else if (fn_proto.getIsAsync() != null) { | ||
| 1666 | try ais.writer().writeAll("callconv(.Async) "); | ||
| 1667 | } else if (fn_proto.getIsInline() != null) { | ||
| 1668 | try ais.writer().writeAll("callconv(.Inline) "); | ||
| 1669 | } | ||
| 1670 | 1197 | ||
| 1671 | switch (fn_proto.return_type) { | 1198 | if (while_node.error_token) |error_token| { |
| 1672 | .Explicit => |node| { | 1199 | try renderToken(ais, tree, error_token - 1, .none); // | |
| 1673 | return renderExpression(allocator, ais, tree, node, space); | 1200 | try renderToken(ais, tree, error_token, .none); // identifier |
| 1674 | }, | 1201 | try renderToken(ais, tree, error_token + 1, .space); // | |
| 1675 | .InferErrorSet => |node| { | 1202 | } |
| 1676 | try renderToken(tree, ais, tree.prevToken(node.firstToken()), Space.None); // ! | ||
| 1677 | return renderExpression(allocator, ais, tree, node, space); | ||
| 1678 | }, | ||
| 1679 | .Invalid => unreachable, | ||
| 1680 | } | ||
| 1681 | }, | ||
| 1682 | 1203 | ||
| 1683 | .AnyFrameType => { | 1204 | return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space); |
| 1684 | const anyframe_type = @fieldParentPtr(ast.Node.AnyFrameType, "base", base); | 1205 | } else { |
| 1206 | return renderExpression(gpa, ais, tree, while_node.ast.then_expr, space); | ||
| 1207 | } | ||
| 1208 | } | ||
| 1685 | 1209 | ||
| 1686 | if (anyframe_type.result) |result| { | 1210 | fn renderContainerField( |
| 1687 | try renderToken(tree, ais, anyframe_type.anyframe_token, Space.None); // anyframe | 1211 | gpa: *Allocator, |
| 1688 | try renderToken(tree, ais, result.arrow_token, Space.None); // -> | 1212 | ais: *Ais, |
| 1689 | return renderExpression(allocator, ais, tree, result.return_type, space); | 1213 | tree: ast.Tree, |
| 1690 | } else { | 1214 | field: ast.full.ContainerField, |
| 1691 | return renderToken(tree, ais, anyframe_type.anyframe_token, space); // anyframe | 1215 | space: Space, |
| 1692 | } | 1216 | ) Error!void { |
| 1693 | }, | 1217 | const main_tokens = tree.nodes.items(.main_token); |
| 1218 | if (field.comptime_token) |t| { | ||
| 1219 | try renderToken(ais, tree, t, .space); // comptime | ||
| 1220 | } | ||
| 1221 | if (field.ast.type_expr == 0 and field.ast.value_expr == 0) { | ||
| 1222 | return renderTokenComma(ais, tree, field.ast.name_token, space); // name | ||
| 1223 | } | ||
| 1224 | if (field.ast.type_expr != 0 and field.ast.value_expr == 0) { | ||
| 1225 | try renderToken(ais, tree, field.ast.name_token, .none); // name | ||
| 1226 | try renderToken(ais, tree, field.ast.name_token + 1, .space); // : | ||
| 1227 | |||
| 1228 | if (field.ast.align_expr != 0) { | ||
| 1229 | try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type | ||
| 1230 | const align_token = tree.firstToken(field.ast.align_expr) - 2; | ||
| 1231 | try renderToken(ais, tree, align_token, .none); // align | ||
| 1232 | try renderToken(ais, tree, align_token + 1, .none); // ( | ||
| 1233 | try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment | ||
| 1234 | const rparen = tree.lastToken(field.ast.align_expr) + 1; | ||
| 1235 | return renderTokenComma(ais, tree, rparen, space); // ) | ||
| 1236 | } else { | ||
| 1237 | return renderExpressionComma(gpa, ais, tree, field.ast.type_expr, space); // type | ||
| 1238 | } | ||
| 1239 | } | ||
| 1240 | if (field.ast.type_expr == 0 and field.ast.value_expr != 0) { | ||
| 1241 | try renderToken(ais, tree, field.ast.name_token, .space); // name | ||
| 1242 | try renderToken(ais, tree, field.ast.name_token + 1, .space); // = | ||
| 1243 | return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value | ||
| 1244 | } | ||
| 1694 | 1245 | ||
| 1695 | .DocComment => unreachable, // doc comments are attached to nodes | 1246 | try renderToken(ais, tree, field.ast.name_token, .none); // name |
| 1247 | try renderToken(ais, tree, field.ast.name_token + 1, .space); // : | ||
| 1248 | try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type | ||
| 1249 | |||
| 1250 | if (field.ast.align_expr != 0) { | ||
| 1251 | const lparen_token = tree.firstToken(field.ast.align_expr) - 1; | ||
| 1252 | const align_kw = lparen_token - 1; | ||
| 1253 | const rparen_token = tree.lastToken(field.ast.align_expr) + 1; | ||
| 1254 | try renderToken(ais, tree, align_kw, .none); // align | ||
| 1255 | try renderToken(ais, tree, lparen_token, .none); // ( | ||
| 1256 | try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment | ||
| 1257 | try renderToken(ais, tree, rparen_token, .space); // ) | ||
| 1258 | } | ||
| 1259 | const eq_token = tree.firstToken(field.ast.value_expr) - 1; | ||
| 1260 | try renderToken(ais, tree, eq_token, .space); // = | ||
| 1261 | return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value | ||
| 1262 | } | ||
| 1696 | 1263 | ||
| 1697 | .Switch => { | 1264 | fn renderBuiltinCall( |
| 1698 | const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); | 1265 | gpa: *Allocator, |
| 1266 | ais: *Ais, | ||
| 1267 | tree: ast.Tree, | ||
| 1268 | builtin_token: ast.TokenIndex, | ||
| 1269 | params: []const ast.Node.Index, | ||
| 1270 | space: Space, | ||
| 1271 | ) Error!void { | ||
| 1272 | const token_tags = tree.tokens.items(.tag); | ||
| 1699 | 1273 | ||
| 1700 | try renderToken(tree, ais, switch_node.switch_token, Space.Space); // switch | 1274 | try renderToken(ais, tree, builtin_token, .none); // @name |
| 1701 | try renderToken(tree, ais, tree.nextToken(switch_node.switch_token), Space.None); // ( | ||
| 1702 | 1275 | ||
| 1703 | const rparen = tree.nextToken(switch_node.expr.lastToken()); | 1276 | if (params.len == 0) { |
| 1704 | const lbrace = tree.nextToken(rparen); | 1277 | try renderToken(ais, tree, builtin_token + 1, .none); // ( |
| 1278 | return renderToken(ais, tree, builtin_token + 2, space); // ) | ||
| 1279 | } | ||
| 1705 | 1280 | ||
| 1706 | if (switch_node.cases_len == 0) { | 1281 | const last_param = params[params.len - 1]; |
| 1707 | try renderExpression(allocator, ais, tree, switch_node.expr, Space.None); | 1282 | const after_last_param_token = tree.lastToken(last_param) + 1; |
| 1708 | try renderToken(tree, ais, rparen, Space.Space); // ) | ||
| 1709 | try renderToken(tree, ais, lbrace, Space.None); // { | ||
| 1710 | return renderToken(tree, ais, switch_node.rbrace, space); // } | ||
| 1711 | } | ||
| 1712 | 1283 | ||
| 1713 | try renderExpression(allocator, ais, tree, switch_node.expr, Space.None); | 1284 | if (token_tags[after_last_param_token] != .comma) { |
| 1714 | try renderToken(tree, ais, rparen, Space.Space); // ) | 1285 | // Render all on one line, no trailing comma. |
| 1286 | try renderToken(ais, tree, builtin_token + 1, .none); // ( | ||
| 1715 | 1287 | ||
| 1288 | for (params) |param_node, i| { | ||
| 1289 | const first_param_token = tree.firstToken(param_node); | ||
| 1290 | if (token_tags[first_param_token] == .multiline_string_literal_line or | ||
| 1291 | hasSameLineComment(tree, first_param_token - 1)) | ||
| 1716 | { | 1292 | { |
| 1717 | ais.pushIndentNextLine(); | 1293 | ais.pushIndentOneShot(); |
| 1718 | defer ais.popIndent(); | ||
| 1719 | try renderToken(tree, ais, lbrace, Space.Newline); // { | ||
| 1720 | |||
| 1721 | const cases = switch_node.cases(); | ||
| 1722 | for (cases) |node, i| { | ||
| 1723 | try renderExpression(allocator, ais, tree, node, Space.Comma); | ||
| 1724 | |||
| 1725 | if (i + 1 < cases.len) { | ||
| 1726 | try renderExtraNewline(tree, ais, cases[i + 1]); | ||
| 1727 | } | ||
| 1728 | } | ||
| 1729 | } | 1294 | } |
| 1295 | try renderExpression(gpa, ais, tree, param_node, .none); | ||
| 1730 | 1296 | ||
| 1731 | return renderToken(tree, ais, switch_node.rbrace, space); // } | 1297 | if (i + 1 < params.len) { |
| 1732 | }, | 1298 | const comma_token = tree.lastToken(param_node) + 1; |
| 1733 | 1299 | try renderToken(ais, tree, comma_token, .space); // , | |
| 1734 | .SwitchCase => { | ||
| 1735 | const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); | ||
| 1736 | |||
| 1737 | assert(switch_case.items_len != 0); | ||
| 1738 | const src_has_trailing_comma = blk: { | ||
| 1739 | const last_node = switch_case.items()[switch_case.items_len - 1]; | ||
| 1740 | const maybe_comma = tree.nextToken(last_node.lastToken()); | ||
| 1741 | break :blk tree.token_ids[maybe_comma] == .Comma; | ||
| 1742 | }; | ||
| 1743 | |||
| 1744 | if (switch_case.items_len == 1 or !src_has_trailing_comma) { | ||
| 1745 | const items = switch_case.items(); | ||
| 1746 | for (items) |node, i| { | ||
| 1747 | if (i + 1 < items.len) { | ||
| 1748 | try renderExpression(allocator, ais, tree, node, Space.None); | ||
| 1749 | |||
| 1750 | const comma_token = tree.nextToken(node.lastToken()); | ||
| 1751 | try renderToken(tree, ais, comma_token, Space.Space); // , | ||
| 1752 | try renderExtraNewline(tree, ais, items[i + 1]); | ||
| 1753 | } else { | ||
| 1754 | try renderExpression(allocator, ais, tree, node, Space.Space); | ||
| 1755 | } | ||
| 1756 | } | ||
| 1757 | } else { | ||
| 1758 | const items = switch_case.items(); | ||
| 1759 | for (items) |node, i| { | ||
| 1760 | if (i + 1 < items.len) { | ||
| 1761 | try renderExpression(allocator, ais, tree, node, Space.None); | ||
| 1762 | |||
| 1763 | const comma_token = tree.nextToken(node.lastToken()); | ||
| 1764 | try renderToken(tree, ais, comma_token, Space.Newline); // , | ||
| 1765 | try renderExtraNewline(tree, ais, items[i + 1]); | ||
| 1766 | } else { | ||
| 1767 | try renderExpression(allocator, ais, tree, node, Space.Comma); | ||
| 1768 | } | ||
| 1769 | } | ||
| 1770 | } | 1300 | } |
| 1301 | } | ||
| 1302 | return renderToken(ais, tree, after_last_param_token, space); // ) | ||
| 1303 | } else { | ||
| 1304 | // Render one param per line. | ||
| 1305 | ais.pushIndent(); | ||
| 1306 | try renderToken(ais, tree, builtin_token + 1, Space.newline); // ( | ||
| 1307 | |||
| 1308 | for (params) |param_node| { | ||
| 1309 | try renderExpression(gpa, ais, tree, param_node, .comma); | ||
| 1310 | } | ||
| 1311 | ais.popIndent(); | ||
| 1771 | 1312 | ||
| 1772 | try renderToken(tree, ais, switch_case.arrow_token, Space.Space); // => | 1313 | return renderToken(ais, tree, after_last_param_token + 1, space); // ) |
| 1773 | 1314 | } | |
| 1774 | if (switch_case.payload) |payload| { | 1315 | } |
| 1775 | try renderExpression(allocator, ais, tree, payload, Space.Space); | ||
| 1776 | } | ||
| 1777 | |||
| 1778 | return renderExpression(allocator, ais, tree, switch_case.expr, space); | ||
| 1779 | }, | ||
| 1780 | .SwitchElse => { | ||
| 1781 | const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); | ||
| 1782 | return renderToken(tree, ais, switch_else.token, space); | ||
| 1783 | }, | ||
| 1784 | .Else => { | ||
| 1785 | const else_node = @fieldParentPtr(ast.Node.Else, "base", base); | ||
| 1786 | |||
| 1787 | const body_is_block = nodeIsBlock(else_node.body); | ||
| 1788 | const same_line = body_is_block or tree.tokensOnSameLine(else_node.else_token, else_node.body.lastToken()); | ||
| 1789 | |||
| 1790 | const after_else_space = if (same_line or else_node.payload != null) Space.Space else Space.Newline; | ||
| 1791 | try renderToken(tree, ais, else_node.else_token, after_else_space); | ||
| 1792 | |||
| 1793 | if (else_node.payload) |payload| { | ||
| 1794 | const payload_space = if (same_line) Space.Space else Space.Newline; | ||
| 1795 | try renderExpression(allocator, ais, tree, payload, payload_space); | ||
| 1796 | } | ||
| 1797 | 1316 | ||
| 1798 | if (same_line) { | 1317 | fn renderFnProto(gpa: *Allocator, ais: *Ais, tree: ast.Tree, fn_proto: ast.full.FnProto, space: Space) Error!void { |
| 1799 | return renderExpression(allocator, ais, tree, else_node.body, space); | 1318 | const token_tags = tree.tokens.items(.tag); |
| 1800 | } else { | 1319 | const token_starts = tree.tokens.items(.start); |
| 1801 | ais.pushIndent(); | 1320 | |
| 1802 | defer ais.popIndent(); | 1321 | const is_inline = fn_proto.ast.fn_token > 0 and |
| 1803 | return renderExpression(allocator, ais, tree, else_node.body, space); | 1322 | token_tags[fn_proto.ast.fn_token - 1] == .keyword_inline; |
| 1323 | |||
| 1324 | const after_fn_token = fn_proto.ast.fn_token + 1; | ||
| 1325 | const lparen = if (token_tags[after_fn_token] == .identifier) blk: { | ||
| 1326 | try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn | ||
| 1327 | try renderToken(ais, tree, after_fn_token, .none); // name | ||
| 1328 | break :blk after_fn_token + 1; | ||
| 1329 | } else blk: { | ||
| 1330 | try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn | ||
| 1331 | break :blk fn_proto.ast.fn_token + 1; | ||
| 1332 | }; | ||
| 1333 | assert(token_tags[lparen] == .l_paren); | ||
| 1334 | |||
| 1335 | const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; | ||
| 1336 | const rparen = blk: { | ||
| 1337 | // These may appear in any order, so we have to check the token_starts array | ||
| 1338 | // to find out which is first. | ||
| 1339 | var rparen = if (token_tags[maybe_bang] == .bang) maybe_bang - 1 else maybe_bang; | ||
| 1340 | var smallest_start = token_starts[maybe_bang]; | ||
| 1341 | if (fn_proto.ast.align_expr != 0) { | ||
| 1342 | const tok = tree.firstToken(fn_proto.ast.align_expr) - 3; | ||
| 1343 | const start = token_starts[tok]; | ||
| 1344 | if (start < smallest_start) { | ||
| 1345 | rparen = tok; | ||
| 1346 | smallest_start = start; | ||
| 1804 | } | 1347 | } |
| 1805 | }, | 1348 | } |
| 1806 | 1349 | if (fn_proto.ast.section_expr != 0) { | |
| 1807 | .While => { | 1350 | const tok = tree.firstToken(fn_proto.ast.section_expr) - 3; |
| 1808 | const while_node = @fieldParentPtr(ast.Node.While, "base", base); | 1351 | const start = token_starts[tok]; |
| 1809 | 1352 | if (start < smallest_start) { | |
| 1810 | if (while_node.label) |label| { | 1353 | rparen = tok; |
| 1811 | try renderToken(tree, ais, label, Space.None); // label | 1354 | smallest_start = start; |
| 1812 | try renderToken(tree, ais, tree.nextToken(label), Space.Space); // : | ||
| 1813 | } | 1355 | } |
| 1814 | 1356 | } | |
| 1815 | if (while_node.inline_token) |inline_token| { | 1357 | if (fn_proto.ast.callconv_expr != 0) { |
| 1816 | try renderToken(tree, ais, inline_token, Space.Space); // inline | 1358 | const tok = tree.firstToken(fn_proto.ast.callconv_expr) - 3; |
| 1359 | const start = token_starts[tok]; | ||
| 1360 | if (start < smallest_start) { | ||
| 1361 | rparen = tok; | ||
| 1362 | smallest_start = start; | ||
| 1817 | } | 1363 | } |
| 1818 | 1364 | } | |
| 1819 | try renderToken(tree, ais, while_node.while_token, Space.Space); // while | 1365 | break :blk rparen; |
| 1820 | try renderToken(tree, ais, tree.nextToken(while_node.while_token), Space.None); // ( | 1366 | }; |
| 1821 | try renderExpression(allocator, ais, tree, while_node.condition, Space.None); | 1367 | assert(token_tags[rparen] == .r_paren); |
| 1822 | 1368 | ||
| 1823 | const cond_rparen = tree.nextToken(while_node.condition.lastToken()); | 1369 | // The params list is a sparse set that does *not* include anytype or ... parameters. |
| 1824 | 1370 | ||
| 1825 | const body_is_block = nodeIsBlock(while_node.body); | 1371 | const trailing_comma = token_tags[rparen - 1] == .comma; |
| 1826 | 1372 | if (!trailing_comma and !hasComment(tree, lparen, rparen)) { | |
| 1827 | var block_start_space: Space = undefined; | 1373 | // Render all on one line, no trailing comma. |
| 1828 | var after_body_space: Space = undefined; | 1374 | try renderToken(ais, tree, lparen, .none); // ( |
| 1829 | 1375 | ||
| 1830 | if (body_is_block) { | 1376 | var param_i: usize = 0; |
| 1831 | block_start_space = Space.BlockStart; | 1377 | var last_param_token = lparen; |
| 1832 | after_body_space = if (while_node.@"else" == null) space else Space.SpaceOrOutdent; | 1378 | while (true) { |
| 1833 | } else if (tree.tokensOnSameLine(cond_rparen, while_node.body.lastToken())) { | 1379 | last_param_token += 1; |
| 1834 | block_start_space = Space.Space; | 1380 | switch (token_tags[last_param_token]) { |
| 1835 | after_body_space = if (while_node.@"else" == null) space else Space.Space; | 1381 | .doc_comment => { |
| 1836 | } else { | 1382 | try renderToken(ais, tree, last_param_token, .newline); |
| 1837 | block_start_space = Space.Newline; | 1383 | continue; |
| 1838 | after_body_space = if (while_node.@"else" == null) space else Space.Newline; | 1384 | }, |
| 1385 | .ellipsis3 => { | ||
| 1386 | try renderToken(ais, tree, last_param_token, .none); // ... | ||
| 1387 | break; | ||
| 1388 | }, | ||
| 1389 | .keyword_noalias, .keyword_comptime => { | ||
| 1390 | try renderToken(ais, tree, last_param_token, .space); | ||
| 1391 | last_param_token += 1; | ||
| 1392 | }, | ||
| 1393 | .identifier => {}, | ||
| 1394 | .keyword_anytype => { | ||
| 1395 | try renderToken(ais, tree, last_param_token, .none); // anytype | ||
| 1396 | continue; | ||
| 1397 | }, | ||
| 1398 | .r_paren => break, | ||
| 1399 | .comma => { | ||
| 1400 | try renderToken(ais, tree, last_param_token, .space); // , | ||
| 1401 | continue; | ||
| 1402 | }, | ||
| 1403 | else => {}, // Parameter type without a name. | ||
| 1839 | } | 1404 | } |
| 1840 | 1405 | if (token_tags[last_param_token] == .identifier and | |
| 1406 | token_tags[last_param_token + 1] == .colon) | ||
| 1841 | { | 1407 | { |
| 1842 | const rparen_space = if (while_node.payload != null or while_node.continue_expr != null) Space.Space else block_start_space; | 1408 | try renderToken(ais, tree, last_param_token, .none); // name |
| 1843 | try renderToken(tree, ais, cond_rparen, rparen_space); // ) | 1409 | last_param_token += 1; |
| 1844 | } | 1410 | try renderToken(ais, tree, last_param_token, .space); // : |
| 1845 | 1411 | last_param_token += 1; | |
| 1846 | if (while_node.payload) |payload| { | 1412 | } |
| 1847 | const payload_space = if (while_node.continue_expr != null) Space.Space else block_start_space; | 1413 | if (token_tags[last_param_token] == .keyword_anytype) { |
| 1848 | try renderExpression(allocator, ais, tree, payload, payload_space); | 1414 | try renderToken(ais, tree, last_param_token, .none); // anytype |
| 1415 | continue; | ||
| 1416 | } | ||
| 1417 | const param = fn_proto.ast.params[param_i]; | ||
| 1418 | param_i += 1; | ||
| 1419 | try renderExpression(gpa, ais, tree, param, .none); | ||
| 1420 | last_param_token = tree.lastToken(param); | ||
| 1421 | } | ||
| 1422 | } else { | ||
| 1423 | // One param per line. | ||
| 1424 | ais.pushIndent(); | ||
| 1425 | try renderToken(ais, tree, lparen, .newline); // ( | ||
| 1426 | |||
| 1427 | var param_i: usize = 0; | ||
| 1428 | var last_param_token = lparen; | ||
| 1429 | while (true) { | ||
| 1430 | last_param_token += 1; | ||
| 1431 | switch (token_tags[last_param_token]) { | ||
| 1432 | .doc_comment => { | ||
| 1433 | try renderToken(ais, tree, last_param_token, .newline); | ||
| 1434 | continue; | ||
| 1435 | }, | ||
| 1436 | .ellipsis3 => { | ||
| 1437 | try renderToken(ais, tree, last_param_token, .comma); // ... | ||
| 1438 | break; | ||
| 1439 | }, | ||
| 1440 | .keyword_noalias, .keyword_comptime => { | ||
| 1441 | try renderToken(ais, tree, last_param_token, .space); | ||
| 1442 | last_param_token += 1; | ||
| 1443 | }, | ||
| 1444 | .identifier => {}, | ||
| 1445 | .keyword_anytype => { | ||
| 1446 | try renderToken(ais, tree, last_param_token, .comma); // anytype | ||
| 1447 | if (token_tags[last_param_token + 1] == .comma) | ||
| 1448 | last_param_token += 1; | ||
| 1449 | continue; | ||
| 1450 | }, | ||
| 1451 | .r_paren => break, | ||
| 1452 | else => unreachable, | ||
| 1849 | } | 1453 | } |
| 1454 | if (token_tags[last_param_token] == .identifier) { | ||
| 1455 | try renderToken(ais, tree, last_param_token, .none); // name | ||
| 1456 | last_param_token += 1; | ||
| 1457 | try renderToken(ais, tree, last_param_token, .space); // : | ||
| 1458 | last_param_token += 1; | ||
| 1459 | } | ||
| 1460 | if (token_tags[last_param_token] == .keyword_anytype) { | ||
| 1461 | try renderToken(ais, tree, last_param_token, .comma); // anytype | ||
| 1462 | if (token_tags[last_param_token + 1] == .comma) | ||
| 1463 | last_param_token += 1; | ||
| 1464 | continue; | ||
| 1465 | } | ||
| 1466 | const param = fn_proto.ast.params[param_i]; | ||
| 1467 | param_i += 1; | ||
| 1468 | try renderExpression(gpa, ais, tree, param, .comma); | ||
| 1469 | last_param_token = tree.lastToken(param); | ||
| 1470 | if (token_tags[last_param_token + 1] == .comma) last_param_token += 1; | ||
| 1471 | } | ||
| 1472 | ais.popIndent(); | ||
| 1473 | } | ||
| 1850 | 1474 | ||
| 1851 | if (while_node.continue_expr) |continue_expr| { | 1475 | try renderToken(ais, tree, rparen, .space); // ) |
| 1852 | const rparen = tree.nextToken(continue_expr.lastToken()); | ||
| 1853 | const lparen = tree.prevToken(continue_expr.firstToken()); | ||
| 1854 | const colon = tree.prevToken(lparen); | ||
| 1855 | |||
| 1856 | try renderToken(tree, ais, colon, Space.Space); // : | ||
| 1857 | try renderToken(tree, ais, lparen, Space.None); // ( | ||
| 1858 | 1476 | ||
| 1859 | try renderExpression(allocator, ais, tree, continue_expr, Space.None); | 1477 | if (fn_proto.ast.align_expr != 0) { |
| 1478 | const align_lparen = tree.firstToken(fn_proto.ast.align_expr) - 1; | ||
| 1479 | const align_rparen = tree.lastToken(fn_proto.ast.align_expr) + 1; | ||
| 1860 | 1480 | ||
| 1861 | try renderToken(tree, ais, rparen, block_start_space); // ) | 1481 | try renderToken(ais, tree, align_lparen - 1, .none); // align |
| 1862 | } | 1482 | try renderToken(ais, tree, align_lparen, .none); // ( |
| 1863 | 1483 | try renderExpression(gpa, ais, tree, fn_proto.ast.align_expr, .none); | |
| 1864 | { | 1484 | try renderToken(ais, tree, align_rparen, .space); // ) |
| 1865 | if (!body_is_block) ais.pushIndent(); | 1485 | } |
| 1866 | defer if (!body_is_block) ais.popIndent(); | ||
| 1867 | try renderExpression(allocator, ais, tree, while_node.body, after_body_space); | ||
| 1868 | } | ||
| 1869 | 1486 | ||
| 1870 | if (while_node.@"else") |@"else"| { | 1487 | if (fn_proto.ast.section_expr != 0) { |
| 1871 | return renderExpression(allocator, ais, tree, &@"else".base, space); | 1488 | const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1; |
| 1872 | } | 1489 | const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1; |
| 1873 | }, | ||
| 1874 | 1490 | ||
| 1875 | .For => { | 1491 | try renderToken(ais, tree, section_lparen - 1, .none); // section |
| 1876 | const for_node = @fieldParentPtr(ast.Node.For, "base", base); | 1492 | try renderToken(ais, tree, section_lparen, .none); // ( |
| 1493 | try renderExpression(gpa, ais, tree, fn_proto.ast.section_expr, .none); | ||
| 1494 | try renderToken(ais, tree, section_rparen, .space); // ) | ||
| 1495 | } | ||
| 1877 | 1496 | ||
| 1878 | if (for_node.label) |label| { | 1497 | if (fn_proto.ast.callconv_expr != 0) { |
| 1879 | try renderToken(tree, ais, label, Space.None); // label | 1498 | const callconv_lparen = tree.firstToken(fn_proto.ast.callconv_expr) - 1; |
| 1880 | try renderToken(tree, ais, tree.nextToken(label), Space.Space); // : | 1499 | const callconv_rparen = tree.lastToken(fn_proto.ast.callconv_expr) + 1; |
| 1881 | } | ||
| 1882 | 1500 | ||
| 1883 | if (for_node.inline_token) |inline_token| { | 1501 | try renderToken(ais, tree, callconv_lparen - 1, .none); // callconv |
| 1884 | try renderToken(tree, ais, inline_token, Space.Space); // inline | 1502 | try renderToken(ais, tree, callconv_lparen, .none); // ( |
| 1885 | } | 1503 | try renderExpression(gpa, ais, tree, fn_proto.ast.callconv_expr, .none); |
| 1886 | 1504 | try renderToken(ais, tree, callconv_rparen, .space); // ) | |
| 1887 | try renderToken(tree, ais, for_node.for_token, Space.Space); // for | 1505 | } else if (is_inline) { |
| 1888 | try renderToken(tree, ais, tree.nextToken(for_node.for_token), Space.None); // ( | 1506 | try ais.writer().writeAll("callconv(.Inline) "); |
| 1889 | try renderExpression(allocator, ais, tree, for_node.array_expr, Space.None); | 1507 | } |
| 1890 | 1508 | ||
| 1891 | const rparen = tree.nextToken(for_node.array_expr.lastToken()); | 1509 | if (token_tags[maybe_bang] == .bang) { |
| 1510 | try renderToken(ais, tree, maybe_bang, .none); // ! | ||
| 1511 | } | ||
| 1512 | return renderExpression(gpa, ais, tree, fn_proto.ast.return_type, space); | ||
| 1513 | } | ||
| 1892 | 1514 | ||
| 1893 | const body_is_block = for_node.body.tag.isBlock(); | 1515 | fn renderSwitchCase( |
| 1894 | const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken()); | 1516 | gpa: *Allocator, |
| 1895 | const body_on_same_line = body_is_block or src_one_line_to_body; | 1517 | ais: *Ais, |
| 1518 | tree: ast.Tree, | ||
| 1519 | switch_case: ast.full.SwitchCase, | ||
| 1520 | space: Space, | ||
| 1521 | ) Error!void { | ||
| 1522 | const token_tags = tree.tokens.items(.tag); | ||
| 1523 | const trailing_comma = token_tags[switch_case.ast.arrow_token - 1] == .comma; | ||
| 1524 | |||
| 1525 | // Render everything before the arrow | ||
| 1526 | if (switch_case.ast.values.len == 0) { | ||
| 1527 | try renderToken(ais, tree, switch_case.ast.arrow_token - 1, .space); // else keyword | ||
| 1528 | } else if (switch_case.ast.values.len == 1) { | ||
| 1529 | // render on one line and drop the trailing comma if any | ||
| 1530 | try renderExpression(gpa, ais, tree, switch_case.ast.values[0], .space); | ||
| 1531 | } else if (trailing_comma or | ||
| 1532 | hasComment(tree, tree.firstToken(switch_case.ast.values[0]), switch_case.ast.arrow_token)) | ||
| 1533 | { | ||
| 1534 | // Render each value on a new line | ||
| 1535 | try renderExpressions(gpa, ais, tree, switch_case.ast.values, .comma); | ||
| 1536 | } else { | ||
| 1537 | // Render on one line | ||
| 1538 | for (switch_case.ast.values) |value_expr| { | ||
| 1539 | try renderExpression(gpa, ais, tree, value_expr, .comma_space); | ||
| 1540 | } | ||
| 1541 | } | ||
| 1896 | 1542 | ||
| 1897 | try renderToken(tree, ais, rparen, Space.Space); // ) | 1543 | // Render the arrow and everything after it |
| 1544 | try renderToken(ais, tree, switch_case.ast.arrow_token, .space); | ||
| 1898 | 1545 | ||
| 1899 | const space_after_payload = if (body_on_same_line) Space.Space else Space.Newline; | 1546 | if (switch_case.payload_token) |payload_token| { |
| 1900 | try renderExpression(allocator, ais, tree, for_node.payload, space_after_payload); // |x| | 1547 | try renderToken(ais, tree, payload_token - 1, .none); // pipe |
| 1548 | if (token_tags[payload_token] == .asterisk) { | ||
| 1549 | try renderToken(ais, tree, payload_token, .none); // asterisk | ||
| 1550 | try renderToken(ais, tree, payload_token + 1, .none); // identifier | ||
| 1551 | try renderToken(ais, tree, payload_token + 2, .space); // pipe | ||
| 1552 | } else { | ||
| 1553 | try renderToken(ais, tree, payload_token, .none); // identifier | ||
| 1554 | try renderToken(ais, tree, payload_token + 1, .space); // pipe | ||
| 1555 | } | ||
| 1556 | } | ||
| 1901 | 1557 | ||
| 1902 | const space_after_body = blk: { | 1558 | try renderExpression(gpa, ais, tree, switch_case.ast.target_expr, space); |
| 1903 | if (for_node.@"else") |@"else"| { | 1559 | } |
| 1904 | const src_one_line_to_else = tree.tokensOnSameLine(rparen, @"else".firstToken()); | ||
| 1905 | if (body_is_block or src_one_line_to_else) { | ||
| 1906 | break :blk Space.Space; | ||
| 1907 | } else { | ||
| 1908 | break :blk Space.Newline; | ||
| 1909 | } | ||
| 1910 | } else { | ||
| 1911 | break :blk space; | ||
| 1912 | } | ||
| 1913 | }; | ||
| 1914 | 1560 | ||
| 1915 | { | 1561 | fn renderBlock( |
| 1916 | if (!body_on_same_line) ais.pushIndent(); | 1562 | gpa: *Allocator, |
| 1917 | defer if (!body_on_same_line) ais.popIndent(); | 1563 | ais: *Ais, |
| 1918 | try renderExpression(allocator, ais, tree, for_node.body, space_after_body); // { body } | 1564 | tree: ast.Tree, |
| 1919 | } | 1565 | block_node: ast.Node.Index, |
| 1566 | statements: []const ast.Node.Index, | ||
| 1567 | space: Space, | ||
| 1568 | ) Error!void { | ||
| 1569 | const token_tags = tree.tokens.items(.tag); | ||
| 1570 | const node_tags = tree.nodes.items(.tag); | ||
| 1571 | const nodes_data = tree.nodes.items(.data); | ||
| 1572 | const lbrace = tree.nodes.items(.main_token)[block_node]; | ||
| 1573 | |||
| 1574 | if (token_tags[lbrace - 1] == .colon and | ||
| 1575 | token_tags[lbrace - 2] == .identifier) | ||
| 1576 | { | ||
| 1577 | try renderToken(ais, tree, lbrace - 2, .none); | ||
| 1578 | try renderToken(ais, tree, lbrace - 1, .space); | ||
| 1579 | } | ||
| 1920 | 1580 | ||
| 1921 | if (for_node.@"else") |@"else"| { | 1581 | ais.pushIndentNextLine(); |
| 1922 | return renderExpression(allocator, ais, tree, &@"else".base, space); // else | 1582 | if (statements.len == 0) { |
| 1583 | try renderToken(ais, tree, lbrace, .none); | ||
| 1584 | } else { | ||
| 1585 | try renderToken(ais, tree, lbrace, .newline); | ||
| 1586 | for (statements) |stmt, i| { | ||
| 1587 | if (i != 0) try renderExtraNewline(ais, tree, stmt); | ||
| 1588 | switch (node_tags[stmt]) { | ||
| 1589 | .global_var_decl => try renderVarDecl(gpa, ais, tree, tree.globalVarDecl(stmt)), | ||
| 1590 | .local_var_decl => try renderVarDecl(gpa, ais, tree, tree.localVarDecl(stmt)), | ||
| 1591 | .simple_var_decl => try renderVarDecl(gpa, ais, tree, tree.simpleVarDecl(stmt)), | ||
| 1592 | .aligned_var_decl => try renderVarDecl(gpa, ais, tree, tree.alignedVarDecl(stmt)), | ||
| 1593 | else => try renderExpression(gpa, ais, tree, stmt, .semicolon), | ||
| 1923 | } | 1594 | } |
| 1924 | }, | 1595 | } |
| 1925 | 1596 | } | |
| 1926 | .If => { | 1597 | ais.popIndent(); |
| 1927 | const if_node = @fieldParentPtr(ast.Node.If, "base", base); | ||
| 1928 | 1598 | ||
| 1929 | const lparen = tree.nextToken(if_node.if_token); | 1599 | try renderToken(ais, tree, tree.lastToken(block_node), space); // rbrace |
| 1930 | const rparen = tree.nextToken(if_node.condition.lastToken()); | 1600 | } |
| 1931 | 1601 | ||
| 1932 | try renderToken(tree, ais, if_node.if_token, Space.Space); // if | 1602 | fn renderStructInit( |
| 1933 | try renderToken(tree, ais, lparen, Space.None); // ( | 1603 | gpa: *Allocator, |
| 1604 | ais: *Ais, | ||
| 1605 | tree: ast.Tree, | ||
| 1606 | struct_node: ast.Node.Index, | ||
| 1607 | struct_init: ast.full.StructInit, | ||
| 1608 | space: Space, | ||
| 1609 | ) Error!void { | ||
| 1610 | const token_tags = tree.tokens.items(.tag); | ||
| 1611 | if (struct_init.ast.type_expr == 0) { | ||
| 1612 | try renderToken(ais, tree, struct_init.ast.lbrace - 1, .none); // . | ||
| 1613 | } else { | ||
| 1614 | try renderExpression(gpa, ais, tree, struct_init.ast.type_expr, .none); // T | ||
| 1615 | } | ||
| 1616 | if (struct_init.ast.fields.len == 0) { | ||
| 1617 | ais.pushIndentNextLine(); | ||
| 1618 | try renderToken(ais, tree, struct_init.ast.lbrace, .none); // lbrace | ||
| 1619 | ais.popIndent(); | ||
| 1620 | return renderToken(ais, tree, struct_init.ast.lbrace + 1, space); // rbrace | ||
| 1621 | } | ||
| 1934 | 1622 | ||
| 1935 | try renderExpression(allocator, ais, tree, if_node.condition, Space.None); // condition | 1623 | const rbrace = tree.lastToken(struct_node); |
| 1624 | const trailing_comma = token_tags[rbrace - 1] == .comma; | ||
| 1625 | if (trailing_comma or hasComment(tree, struct_init.ast.lbrace, rbrace)) { | ||
| 1626 | // Render one field init per line. | ||
| 1627 | ais.pushIndentNextLine(); | ||
| 1628 | try renderToken(ais, tree, struct_init.ast.lbrace, .newline); | ||
| 1629 | |||
| 1630 | try renderToken(ais, tree, struct_init.ast.lbrace + 1, .none); // . | ||
| 1631 | try renderToken(ais, tree, struct_init.ast.lbrace + 2, .space); // name | ||
| 1632 | try renderToken(ais, tree, struct_init.ast.lbrace + 3, .space); // = | ||
| 1633 | try renderExpression(gpa, ais, tree, struct_init.ast.fields[0], .comma); | ||
| 1634 | |||
| 1635 | for (struct_init.ast.fields[1..]) |field_init| { | ||
| 1636 | const init_token = tree.firstToken(field_init); | ||
| 1637 | try renderExtraNewlineToken(ais, tree, init_token - 3); | ||
| 1638 | try renderToken(ais, tree, init_token - 3, .none); // . | ||
| 1639 | try renderToken(ais, tree, init_token - 2, .space); // name | ||
| 1640 | try renderToken(ais, tree, init_token - 1, .space); // = | ||
| 1641 | try renderExpression(gpa, ais, tree, field_init, .comma); | ||
| 1642 | } | ||
| 1936 | 1643 | ||
| 1937 | const body_is_if_block = if_node.body.tag == .If; | 1644 | ais.popIndent(); |
| 1938 | const body_is_block = nodeIsBlock(if_node.body); | 1645 | } else { |
| 1646 | // Render all on one line, no trailing comma. | ||
| 1647 | try renderToken(ais, tree, struct_init.ast.lbrace, .space); | ||
| 1648 | |||
| 1649 | for (struct_init.ast.fields) |field_init| { | ||
| 1650 | const init_token = tree.firstToken(field_init); | ||
| 1651 | try renderToken(ais, tree, init_token - 3, .none); // . | ||
| 1652 | try renderToken(ais, tree, init_token - 2, .space); // name | ||
| 1653 | try renderToken(ais, tree, init_token - 1, .space); // = | ||
| 1654 | try renderExpression(gpa, ais, tree, field_init, .comma_space); | ||
| 1655 | } | ||
| 1656 | } | ||
| 1939 | 1657 | ||
| 1940 | if (body_is_if_block) { | 1658 | return renderToken(ais, tree, rbrace, space); |
| 1941 | try renderExtraNewline(tree, ais, if_node.body); | 1659 | } |
| 1942 | } else if (body_is_block) { | ||
| 1943 | const after_rparen_space = if (if_node.payload == null) Space.BlockStart else Space.Space; | ||
| 1944 | try renderToken(tree, ais, rparen, after_rparen_space); // ) | ||
| 1945 | 1660 | ||
| 1946 | if (if_node.payload) |payload| { | 1661 | // TODO: handle comments between elements |
| 1947 | try renderExpression(allocator, ais, tree, payload, Space.BlockStart); // |x| | 1662 | fn renderArrayInit( |
| 1948 | } | 1663 | gpa: *Allocator, |
| 1664 | ais: *Ais, | ||
| 1665 | tree: ast.Tree, | ||
| 1666 | array_init: ast.full.ArrayInit, | ||
| 1667 | space: Space, | ||
| 1668 | ) Error!void { | ||
| 1669 | const token_tags = tree.tokens.items(.tag); | ||
| 1670 | const token_starts = tree.tokens.items(.start); | ||
| 1671 | |||
| 1672 | if (array_init.ast.type_expr == 0) { | ||
| 1673 | try renderToken(ais, tree, array_init.ast.lbrace - 1, .none); // . | ||
| 1674 | } else { | ||
| 1675 | try renderExpression(gpa, ais, tree, array_init.ast.type_expr, .none); // T | ||
| 1676 | } | ||
| 1949 | 1677 | ||
| 1950 | if (if_node.@"else") |@"else"| { | 1678 | if (array_init.ast.elements.len == 0) { |
| 1951 | try renderExpression(allocator, ais, tree, if_node.body, Space.SpaceOrOutdent); | 1679 | ais.pushIndentNextLine(); |
| 1952 | return renderExpression(allocator, ais, tree, &@"else".base, space); | 1680 | try renderToken(ais, tree, array_init.ast.lbrace, .none); // lbrace |
| 1953 | } else { | 1681 | ais.popIndent(); |
| 1954 | return renderExpression(allocator, ais, tree, if_node.body, space); | 1682 | return renderToken(ais, tree, array_init.ast.lbrace + 1, space); // rbrace |
| 1955 | } | 1683 | } |
| 1956 | } | ||
| 1957 | 1684 | ||
| 1958 | const src_has_newline = !tree.tokensOnSameLine(rparen, if_node.body.lastToken()); | 1685 | const last_elem = array_init.ast.elements[array_init.ast.elements.len - 1]; |
| 1686 | const last_elem_token = tree.lastToken(last_elem); | ||
| 1687 | const trailing_comma = token_tags[last_elem_token + 1] == .comma; | ||
| 1688 | const rbrace = if (trailing_comma) last_elem_token + 2 else last_elem_token + 1; | ||
| 1689 | assert(token_tags[rbrace] == .r_brace); | ||
| 1690 | |||
| 1691 | if (array_init.ast.elements.len == 1) { | ||
| 1692 | const only_elem = array_init.ast.elements[0]; | ||
| 1693 | const first_token = tree.firstToken(only_elem); | ||
| 1694 | if (token_tags[first_token] != .multiline_string_literal_line and | ||
| 1695 | !anythingBetween(tree, last_elem_token, rbrace)) | ||
| 1696 | { | ||
| 1697 | try renderToken(ais, tree, array_init.ast.lbrace, .none); | ||
| 1698 | try renderExpression(gpa, ais, tree, only_elem, .none); | ||
| 1699 | return renderToken(ais, tree, rbrace, space); | ||
| 1700 | } | ||
| 1701 | } | ||
| 1959 | 1702 | ||
| 1960 | if (src_has_newline) { | 1703 | const contains_newlines = !tree.tokensOnSameLine(array_init.ast.lbrace, rbrace); |
| 1961 | const after_rparen_space = if (if_node.payload == null) Space.Newline else Space.Space; | ||
| 1962 | 1704 | ||
| 1963 | { | 1705 | if (!trailing_comma and !contains_newlines) { |
| 1964 | ais.pushIndent(); | 1706 | // Render all on one line, no trailing comma. |
| 1965 | defer ais.popIndent(); | 1707 | if (array_init.ast.elements.len == 1) { |
| 1966 | try renderToken(tree, ais, rparen, after_rparen_space); // ) | 1708 | // If there is only one element, we don't use spaces |
| 1967 | } | 1709 | try renderToken(ais, tree, array_init.ast.lbrace, .none); |
| 1710 | try renderExpression(gpa, ais, tree, array_init.ast.elements[0], .none); | ||
| 1711 | } else { | ||
| 1712 | try renderToken(ais, tree, array_init.ast.lbrace, .space); | ||
| 1713 | for (array_init.ast.elements) |elem| { | ||
| 1714 | try renderExpression(gpa, ais, tree, elem, .comma_space); | ||
| 1715 | } | ||
| 1716 | } | ||
| 1717 | return renderToken(ais, tree, last_elem_token + 1, space); // rbrace | ||
| 1718 | } | ||
| 1968 | 1719 | ||
| 1969 | if (if_node.payload) |payload| { | 1720 | ais.pushIndentNextLine(); |
| 1970 | try renderExpression(allocator, ais, tree, payload, Space.Newline); | 1721 | try renderToken(ais, tree, array_init.ast.lbrace, .newline); |
| 1722 | |||
| 1723 | var expr_index: usize = 0; | ||
| 1724 | while (rowSize(tree, array_init.ast.elements[expr_index..], rbrace)) |row_size| { | ||
| 1725 | const row_exprs = array_init.ast.elements[expr_index..]; | ||
| 1726 | // A place to store the width of each expression and its column's maximum | ||
| 1727 | const widths = try gpa.alloc(usize, row_exprs.len + row_size); | ||
| 1728 | defer gpa.free(widths); | ||
| 1729 | mem.set(usize, widths, 0); | ||
| 1730 | |||
| 1731 | const expr_newlines = try gpa.alloc(bool, row_exprs.len); | ||
| 1732 | defer gpa.free(expr_newlines); | ||
| 1733 | mem.set(bool, expr_newlines, false); | ||
| 1734 | |||
| 1735 | const expr_widths = widths[0..row_exprs.len]; | ||
| 1736 | const column_widths = widths[row_exprs.len..]; | ||
| 1737 | |||
| 1738 | // Find next row with trailing comment (if any) to end the current section. | ||
| 1739 | const section_end = sec_end: { | ||
| 1740 | var this_line_first_expr: usize = 0; | ||
| 1741 | var this_line_size = rowSize(tree, row_exprs, rbrace); | ||
| 1742 | for (row_exprs) |expr, i| { | ||
| 1743 | // Ignore comment on first line of this section. | ||
| 1744 | if (i == 0) continue; | ||
| 1745 | const expr_last_token = tree.lastToken(expr); | ||
| 1746 | if (tree.tokensOnSameLine(tree.firstToken(row_exprs[0]), expr_last_token)) | ||
| 1747 | continue; | ||
| 1748 | // Track start of line containing comment. | ||
| 1749 | if (!tree.tokensOnSameLine(tree.firstToken(row_exprs[this_line_first_expr]), expr_last_token)) { | ||
| 1750 | this_line_first_expr = i; | ||
| 1751 | this_line_size = rowSize(tree, row_exprs[this_line_first_expr..], rbrace); | ||
| 1971 | } | 1752 | } |
| 1972 | 1753 | ||
| 1973 | if (if_node.@"else") |@"else"| { | 1754 | const maybe_comma = expr_last_token + 1; |
| 1974 | const else_is_block = nodeIsBlock(@"else".body); | 1755 | if (token_tags[maybe_comma] == .comma) { |
| 1975 | 1756 | if (hasSameLineComment(tree, maybe_comma)) | |
| 1976 | { | 1757 | break :sec_end i - this_line_size.? + 1; |
| 1977 | ais.pushIndent(); | ||
| 1978 | defer ais.popIndent(); | ||
| 1979 | try renderExpression(allocator, ais, tree, if_node.body, Space.Newline); | ||
| 1980 | } | ||
| 1981 | |||
| 1982 | if (else_is_block) { | ||
| 1983 | try renderToken(tree, ais, @"else".else_token, Space.Space); // else | ||
| 1984 | |||
| 1985 | if (@"else".payload) |payload| { | ||
| 1986 | try renderExpression(allocator, ais, tree, payload, Space.Space); | ||
| 1987 | } | ||
| 1988 | |||
| 1989 | return renderExpression(allocator, ais, tree, @"else".body, space); | ||
| 1990 | } else { | ||
| 1991 | const after_else_space = if (@"else".payload == null) Space.Newline else Space.Space; | ||
| 1992 | try renderToken(tree, ais, @"else".else_token, after_else_space); // else | ||
| 1993 | |||
| 1994 | if (@"else".payload) |payload| { | ||
| 1995 | try renderExpression(allocator, ais, tree, payload, Space.Newline); | ||
| 1996 | } | ||
| 1997 | |||
| 1998 | ais.pushIndent(); | ||
| 1999 | defer ais.popIndent(); | ||
| 2000 | return renderExpression(allocator, ais, tree, @"else".body, space); | ||
| 2001 | } | ||
| 2002 | } else { | ||
| 2003 | ais.pushIndent(); | ||
| 2004 | defer ais.popIndent(); | ||
| 2005 | return renderExpression(allocator, ais, tree, if_node.body, space); | ||
| 2006 | } | 1758 | } |
| 2007 | } | 1759 | } |
| 1760 | break :sec_end row_exprs.len; | ||
| 1761 | }; | ||
| 1762 | expr_index += section_end; | ||
| 2008 | 1763 | ||
| 2009 | // Single line if statement | 1764 | const section_exprs = row_exprs[0..section_end]; |
| 2010 | 1765 | ||
| 2011 | try renderToken(tree, ais, rparen, Space.Space); // ) | 1766 | var sub_expr_buffer = std.ArrayList(u8).init(gpa); |
| 1767 | defer sub_expr_buffer.deinit(); | ||
| 2012 | 1768 | ||
| 2013 | if (if_node.payload) |payload| { | 1769 | var auto_indenting_stream = Ais{ |
| 2014 | try renderExpression(allocator, ais, tree, payload, Space.Space); | 1770 | .indent_delta = indent_delta, |
| 2015 | } | 1771 | .underlying_writer = sub_expr_buffer.writer(), |
| 1772 | }; | ||
| 2016 | 1773 | ||
| 2017 | if (if_node.@"else") |@"else"| { | 1774 | // Calculate size of columns in current section |
| 2018 | try renderExpression(allocator, ais, tree, if_node.body, Space.Space); | 1775 | var column_counter: usize = 0; |
| 2019 | try renderToken(tree, ais, @"else".else_token, Space.Space); | 1776 | var single_line = true; |
| 1777 | var contains_newline = false; | ||
| 1778 | for (section_exprs) |expr, i| { | ||
| 1779 | sub_expr_buffer.shrinkRetainingCapacity(0); | ||
| 1780 | if (i + 1 < section_exprs.len) { | ||
| 1781 | try renderExpression(gpa, &auto_indenting_stream, tree, expr, .none); | ||
| 1782 | const width = sub_expr_buffer.items.len; | ||
| 1783 | const this_contains_newline = mem.indexOfScalar(u8, sub_expr_buffer.items, '\n') != null; | ||
| 1784 | contains_newline = contains_newline or this_contains_newline; | ||
| 1785 | expr_widths[i] = width; | ||
| 1786 | expr_newlines[i] = this_contains_newline; | ||
| 1787 | |||
| 1788 | if (!this_contains_newline) { | ||
| 1789 | const column = column_counter % row_size; | ||
| 1790 | column_widths[column] = std.math.max(column_widths[column], width); | ||
| 1791 | |||
| 1792 | const expr_last_token = tree.lastToken(expr) + 1; | ||
| 1793 | const next_expr = section_exprs[i + 1]; | ||
| 1794 | column_counter += 1; | ||
| 1795 | if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(next_expr))) single_line = false; | ||
| 1796 | } else { | ||
| 1797 | single_line = false; | ||
| 1798 | column_counter = 0; | ||
| 1799 | } | ||
| 1800 | } else { | ||
| 1801 | try renderExpression(gpa, &auto_indenting_stream, tree, expr, .none); | ||
| 1802 | const width = sub_expr_buffer.items.len; | ||
| 1803 | contains_newline = contains_newline or mem.indexOfScalar(u8, sub_expr_buffer.items, '\n') != null; | ||
| 1804 | expr_widths[i] = width; | ||
| 1805 | expr_newlines[i] = contains_newline; | ||
| 1806 | |||
| 1807 | if (!contains_newline) { | ||
| 1808 | const column = column_counter % row_size; | ||
| 1809 | column_widths[column] = std.math.max(column_widths[column], width); | ||
| 1810 | } | ||
| 1811 | break; | ||
| 1812 | } | ||
| 1813 | } | ||
| 2020 | 1814 | ||
| 2021 | if (@"else".payload) |payload| { | 1815 | // Render exprs in current section. |
| 2022 | try renderExpression(allocator, ais, tree, payload, Space.Space); | 1816 | column_counter = 0; |
| 1817 | var last_col_index: usize = row_size - 1; | ||
| 1818 | for (section_exprs) |expr, i| { | ||
| 1819 | if (i + 1 < section_exprs.len) { | ||
| 1820 | const next_expr = section_exprs[i + 1]; | ||
| 1821 | try renderExpression(gpa, ais, tree, expr, .none); | ||
| 1822 | |||
| 1823 | const comma = tree.lastToken(expr) + 1; | ||
| 1824 | |||
| 1825 | if (column_counter != last_col_index) { | ||
| 1826 | if (!expr_newlines[i] and !expr_newlines[i + 1]) { | ||
| 1827 | // Neither the current or next expression is multiline | ||
| 1828 | try renderToken(ais, tree, comma, .space); // , | ||
| 1829 | assert(column_widths[column_counter % row_size] >= expr_widths[i]); | ||
| 1830 | const padding = column_widths[column_counter % row_size] - expr_widths[i]; | ||
| 1831 | try ais.writer().writeByteNTimes(' ', padding); | ||
| 1832 | |||
| 1833 | column_counter += 1; | ||
| 1834 | continue; | ||
| 1835 | } | ||
| 1836 | } | ||
| 1837 | if (single_line and row_size != 1) { | ||
| 1838 | try renderToken(ais, tree, comma, .space); // , | ||
| 1839 | continue; | ||
| 2023 | } | 1840 | } |
| 2024 | 1841 | ||
| 2025 | return renderExpression(allocator, ais, tree, @"else".body, space); | 1842 | column_counter = 0; |
| 1843 | try renderToken(ais, tree, comma, .newline); // , | ||
| 1844 | try renderExtraNewline(ais, tree, next_expr); | ||
| 2026 | } else { | 1845 | } else { |
| 2027 | return renderExpression(allocator, ais, tree, if_node.body, space); | 1846 | try renderExpression(gpa, ais, tree, expr, .comma); // , |
| 2028 | } | 1847 | } |
| 2029 | }, | 1848 | } |
| 2030 | 1849 | ||
| 2031 | .Asm => { | 1850 | if (expr_index == array_init.ast.elements.len) |
| 2032 | const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); | 1851 | break; |
| 1852 | } | ||
| 2033 | 1853 | ||
| 2034 | try renderToken(tree, ais, asm_node.asm_token, Space.Space); // asm | 1854 | ais.popIndent(); |
| 1855 | return renderToken(ais, tree, rbrace, space); // rbrace | ||
| 1856 | } | ||
| 2035 | 1857 | ||
| 2036 | if (asm_node.volatile_token) |volatile_token| { | 1858 | fn renderContainerDecl( |
| 2037 | try renderToken(tree, ais, volatile_token, Space.Space); // volatile | 1859 | gpa: *Allocator, |
| 2038 | try renderToken(tree, ais, tree.nextToken(volatile_token), Space.None); // ( | 1860 | ais: *Ais, |
| 2039 | } else { | 1861 | tree: ast.Tree, |
| 2040 | try renderToken(tree, ais, tree.nextToken(asm_node.asm_token), Space.None); // ( | 1862 | container_decl_node: ast.Node.Index, |
| 2041 | } | 1863 | container_decl: ast.full.ContainerDecl, |
| 1864 | space: Space, | ||
| 1865 | ) Error!void { | ||
| 1866 | const token_tags = tree.tokens.items(.tag); | ||
| 1867 | const node_tags = tree.nodes.items(.tag); | ||
| 2042 | 1868 | ||
| 2043 | asmblk: { | 1869 | if (container_decl.layout_token) |layout_token| { |
| 2044 | ais.pushIndent(); | 1870 | try renderToken(ais, tree, layout_token, .space); |
| 2045 | defer ais.popIndent(); | 1871 | } |
| 2046 | 1872 | ||
| 2047 | if (asm_node.outputs.len == 0 and asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) { | 1873 | var lbrace: ast.TokenIndex = undefined; |
| 2048 | try renderExpression(allocator, ais, tree, asm_node.template, Space.None); | 1874 | if (container_decl.ast.enum_token) |enum_token| { |
| 2049 | break :asmblk; | 1875 | try renderToken(ais, tree, container_decl.ast.main_token, .none); // union |
| 2050 | } | 1876 | try renderToken(ais, tree, enum_token - 1, .none); // lparen |
| 1877 | try renderToken(ais, tree, enum_token, .none); // enum | ||
| 1878 | if (container_decl.ast.arg != 0) { | ||
| 1879 | try renderToken(ais, tree, enum_token + 1, .none); // lparen | ||
| 1880 | try renderExpression(gpa, ais, tree, container_decl.ast.arg, .none); | ||
| 1881 | const rparen = tree.lastToken(container_decl.ast.arg) + 1; | ||
| 1882 | try renderToken(ais, tree, rparen, .none); // rparen | ||
| 1883 | try renderToken(ais, tree, rparen + 1, .space); // rparen | ||
| 1884 | lbrace = rparen + 2; | ||
| 1885 | } else { | ||
| 1886 | try renderToken(ais, tree, enum_token + 1, .space); // rparen | ||
| 1887 | lbrace = enum_token + 2; | ||
| 1888 | } | ||
| 1889 | } else if (container_decl.ast.arg != 0) { | ||
| 1890 | try renderToken(ais, tree, container_decl.ast.main_token, .none); // union | ||
| 1891 | try renderToken(ais, tree, container_decl.ast.main_token + 1, .none); // lparen | ||
| 1892 | try renderExpression(gpa, ais, tree, container_decl.ast.arg, .none); | ||
| 1893 | const rparen = tree.lastToken(container_decl.ast.arg) + 1; | ||
| 1894 | try renderToken(ais, tree, rparen, .space); // rparen | ||
| 1895 | lbrace = rparen + 1; | ||
| 1896 | } else { | ||
| 1897 | try renderToken(ais, tree, container_decl.ast.main_token, .space); // union | ||
| 1898 | lbrace = container_decl.ast.main_token + 1; | ||
| 1899 | } | ||
| 2051 | 1900 | ||
| 2052 | try renderExpression(allocator, ais, tree, asm_node.template, Space.Newline); | 1901 | const rbrace = tree.lastToken(container_decl_node); |
| 1902 | if (container_decl.ast.members.len == 0) { | ||
| 1903 | ais.pushIndentNextLine(); | ||
| 1904 | if (token_tags[lbrace + 1] == .container_doc_comment) { | ||
| 1905 | try renderToken(ais, tree, lbrace, .newline); // lbrace | ||
| 1906 | try renderContainerDocComments(ais, tree, lbrace + 1); | ||
| 1907 | } else { | ||
| 1908 | try renderToken(ais, tree, lbrace, .none); // lbrace | ||
| 1909 | } | ||
| 1910 | ais.popIndent(); | ||
| 1911 | return renderToken(ais, tree, rbrace, space); // rbrace | ||
| 1912 | } | ||
| 2053 | 1913 | ||
| 2054 | ais.setIndentDelta(asm_indent_delta); | 1914 | const src_has_trailing_comma = token_tags[rbrace - 1] == .comma; |
| 2055 | defer ais.setIndentDelta(indent_delta); | 1915 | if (!src_has_trailing_comma) one_line: { |
| 1916 | // We can only print all the members in-line if all the members are fields. | ||
| 1917 | for (container_decl.ast.members) |member| { | ||
| 1918 | if (!node_tags[member].isContainerField()) break :one_line; | ||
| 1919 | } | ||
| 1920 | // All the declarations on the same line. | ||
| 1921 | try renderToken(ais, tree, lbrace, .space); // lbrace | ||
| 1922 | for (container_decl.ast.members) |member| { | ||
| 1923 | try renderMember(gpa, ais, tree, member, .space); | ||
| 1924 | } | ||
| 1925 | return renderToken(ais, tree, rbrace, space); // rbrace | ||
| 1926 | } | ||
| 2056 | 1927 | ||
| 2057 | const colon1 = tree.nextToken(asm_node.template.lastToken()); | 1928 | // One member per line. |
| 1929 | ais.pushIndentNextLine(); | ||
| 1930 | try renderToken(ais, tree, lbrace, .newline); // lbrace | ||
| 1931 | if (token_tags[lbrace + 1] == .container_doc_comment) { | ||
| 1932 | try renderContainerDocComments(ais, tree, lbrace + 1); | ||
| 1933 | } | ||
| 1934 | try renderMembers(gpa, ais, tree, container_decl.ast.members); | ||
| 1935 | ais.popIndent(); | ||
| 2058 | 1936 | ||
| 2059 | const colon2 = if (asm_node.outputs.len == 0) blk: { | 1937 | return renderToken(ais, tree, rbrace, space); // rbrace |
| 2060 | try renderToken(tree, ais, colon1, Space.Newline); // : | 1938 | } |
| 2061 | 1939 | ||
| 2062 | break :blk tree.nextToken(colon1); | 1940 | fn renderAsm( |
| 2063 | } else blk: { | 1941 | gpa: *Allocator, |
| 2064 | try renderToken(tree, ais, colon1, Space.Space); // : | 1942 | ais: *Ais, |
| 1943 | tree: ast.Tree, | ||
| 1944 | asm_node: ast.full.Asm, | ||
| 1945 | space: Space, | ||
| 1946 | ) Error!void { | ||
| 1947 | const token_tags = tree.tokens.items(.tag); | ||
| 2065 | 1948 | ||
| 2066 | ais.pushIndent(); | 1949 | try renderToken(ais, tree, asm_node.ast.asm_token, .space); // asm |
| 2067 | defer ais.popIndent(); | ||
| 2068 | 1950 | ||
| 2069 | for (asm_node.outputs) |*asm_output, i| { | 1951 | if (asm_node.volatile_token) |volatile_token| { |
| 2070 | if (i + 1 < asm_node.outputs.len) { | 1952 | try renderToken(ais, tree, volatile_token, .space); // volatile |
| 2071 | const next_asm_output = asm_node.outputs[i + 1]; | 1953 | try renderToken(ais, tree, volatile_token + 1, .none); // lparen |
| 2072 | try renderAsmOutput(allocator, ais, tree, asm_output, Space.None); | 1954 | } else { |
| 1955 | try renderToken(ais, tree, asm_node.ast.asm_token + 1, .none); // lparen | ||
| 1956 | } | ||
| 2073 | 1957 | ||
| 2074 | const comma = tree.prevToken(next_asm_output.firstToken()); | 1958 | if (asm_node.ast.items.len == 0) { |
| 2075 | try renderToken(tree, ais, comma, Space.Newline); // , | 1959 | ais.pushIndent(); |
| 2076 | try renderExtraNewlineToken(tree, ais, next_asm_output.firstToken()); | 1960 | if (asm_node.first_clobber) |first_clobber| { |
| 2077 | } else if (asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) { | 1961 | // asm ("foo" ::: "a", "b") |
| 2078 | try renderAsmOutput(allocator, ais, tree, asm_output, Space.Newline); | 1962 | // asm ("foo" ::: "a", "b",) |
| 2079 | break :asmblk; | 1963 | try renderExpression(gpa, ais, tree, asm_node.ast.template, .space); |
| 1964 | // Render the three colons. | ||
| 1965 | try renderToken(ais, tree, first_clobber - 3, .none); | ||
| 1966 | try renderToken(ais, tree, first_clobber - 2, .none); | ||
| 1967 | try renderToken(ais, tree, first_clobber - 1, .space); | ||
| 1968 | |||
| 1969 | var tok_i = first_clobber; | ||
| 1970 | while (true) : (tok_i += 1) { | ||
| 1971 | try renderToken(ais, tree, tok_i, .none); | ||
| 1972 | tok_i += 1; | ||
| 1973 | switch (token_tags[tok_i]) { | ||
| 1974 | .r_paren => { | ||
| 1975 | ais.popIndent(); | ||
| 1976 | return renderToken(ais, tree, tok_i, space); | ||
| 1977 | }, | ||
| 1978 | .comma => { | ||
| 1979 | if (token_tags[tok_i + 1] == .r_paren) { | ||
| 1980 | ais.popIndent(); | ||
| 1981 | return renderToken(ais, tree, tok_i + 1, space); | ||
| 2080 | } else { | 1982 | } else { |
| 2081 | try renderAsmOutput(allocator, ais, tree, asm_output, Space.Newline); | 1983 | try renderToken(ais, tree, tok_i, .space); |
| 2082 | const comma_or_colon = tree.nextToken(asm_output.lastToken()); | ||
| 2083 | break :blk switch (tree.token_ids[comma_or_colon]) { | ||
| 2084 | .Comma => tree.nextToken(comma_or_colon), | ||
| 2085 | else => comma_or_colon, | ||
| 2086 | }; | ||
| 2087 | } | 1984 | } |
| 2088 | } | 1985 | }, |
| 2089 | unreachable; | 1986 | else => unreachable, |
| 2090 | }; | 1987 | } |
| 1988 | } | ||
| 1989 | } else { | ||
| 1990 | // asm ("foo") | ||
| 1991 | try renderExpression(gpa, ais, tree, asm_node.ast.template, .none); | ||
| 1992 | ais.popIndent(); | ||
| 1993 | return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen | ||
| 1994 | } | ||
| 1995 | } | ||
| 2091 | 1996 | ||
| 2092 | const colon3 = if (asm_node.inputs.len == 0) blk: { | 1997 | ais.pushIndent(); |
| 2093 | try renderToken(tree, ais, colon2, Space.Newline); // : | 1998 | try renderExpression(gpa, ais, tree, asm_node.ast.template, .newline); |
| 2094 | break :blk tree.nextToken(colon2); | 1999 | ais.setIndentDelta(asm_indent_delta); |
| 2095 | } else blk: { | 2000 | const colon1 = tree.lastToken(asm_node.ast.template) + 1; |
| 2096 | try renderToken(tree, ais, colon2, Space.Space); // : | 2001 | |
| 2097 | ais.pushIndent(); | 2002 | const colon2 = if (asm_node.outputs.len == 0) colon2: { |
| 2098 | defer ais.popIndent(); | 2003 | try renderToken(ais, tree, colon1, .newline); // : |
| 2099 | for (asm_node.inputs) |*asm_input, i| { | 2004 | break :colon2 colon1 + 1; |
| 2100 | if (i + 1 < asm_node.inputs.len) { | 2005 | } else colon2: { |
| 2101 | const next_asm_input = &asm_node.inputs[i + 1]; | 2006 | try renderToken(ais, tree, colon1, .space); // : |
| 2102 | try renderAsmInput(allocator, ais, tree, asm_input, Space.None); | 2007 | |
| 2103 | 2008 | ais.pushIndent(); | |
| 2104 | const comma = tree.prevToken(next_asm_input.firstToken()); | 2009 | for (asm_node.outputs) |asm_output, i| { |
| 2105 | try renderToken(tree, ais, comma, Space.Newline); // , | 2010 | if (i + 1 < asm_node.outputs.len) { |
| 2106 | try renderExtraNewlineToken(tree, ais, next_asm_input.firstToken()); | 2011 | const next_asm_output = asm_node.outputs[i + 1]; |
| 2107 | } else if (asm_node.clobbers.len == 0) { | 2012 | try renderAsmOutput(gpa, ais, tree, asm_output, .none); |
| 2108 | try renderAsmInput(allocator, ais, tree, asm_input, Space.Newline); | 2013 | |
| 2109 | break :asmblk; | 2014 | const comma = tree.firstToken(next_asm_output) - 1; |
| 2110 | } else { | 2015 | try renderToken(ais, tree, comma, .newline); // , |
| 2111 | try renderAsmInput(allocator, ais, tree, asm_input, Space.Newline); | 2016 | try renderExtraNewlineToken(ais, tree, tree.firstToken(next_asm_output)); |
| 2112 | const comma_or_colon = tree.nextToken(asm_input.lastToken()); | 2017 | } else if (asm_node.inputs.len == 0 and asm_node.first_clobber == null) { |
| 2113 | break :blk switch (tree.token_ids[comma_or_colon]) { | 2018 | try renderAsmOutput(gpa, ais, tree, asm_output, .newline); |
| 2114 | .Comma => tree.nextToken(comma_or_colon), | 2019 | ais.popIndent(); |
| 2115 | else => comma_or_colon, | 2020 | ais.setIndentDelta(indent_delta); |
| 2116 | }; | 2021 | ais.popIndent(); |
| 2117 | } | 2022 | return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen |
| 2118 | } | 2023 | } else { |
| 2119 | unreachable; | 2024 | try renderAsmOutput(gpa, ais, tree, asm_output, .newline); |
| 2025 | const comma_or_colon = tree.lastToken(asm_output) + 1; | ||
| 2026 | ais.popIndent(); | ||
| 2027 | break :colon2 switch (token_tags[comma_or_colon]) { | ||
| 2028 | .comma => comma_or_colon + 1, | ||
| 2029 | else => comma_or_colon, | ||
| 2120 | }; | 2030 | }; |
| 2121 | |||
| 2122 | try renderToken(tree, ais, colon3, Space.Space); // : | ||
| 2123 | ais.pushIndent(); | ||
| 2124 | defer ais.popIndent(); | ||
| 2125 | for (asm_node.clobbers) |clobber_node, i| { | ||
| 2126 | if (i + 1 >= asm_node.clobbers.len) { | ||
| 2127 | try renderExpression(allocator, ais, tree, clobber_node, Space.Newline); | ||
| 2128 | break :asmblk; | ||
| 2129 | } else { | ||
| 2130 | try renderExpression(allocator, ais, tree, clobber_node, Space.None); | ||
| 2131 | const comma = tree.nextToken(clobber_node.lastToken()); | ||
| 2132 | try renderToken(tree, ais, comma, Space.Space); // , | ||
| 2133 | } | ||
| 2134 | } | ||
| 2135 | } | 2031 | } |
| 2032 | } else unreachable; | ||
| 2033 | }; | ||
| 2136 | 2034 | ||
| 2137 | return renderToken(tree, ais, asm_node.rparen, space); | 2035 | const colon3 = if (asm_node.inputs.len == 0) colon3: { |
| 2138 | }, | 2036 | try renderToken(ais, tree, colon2, .newline); // : |
| 2037 | break :colon3 colon2 + 1; | ||
| 2038 | } else colon3: { | ||
| 2039 | try renderToken(ais, tree, colon2, .space); // : | ||
| 2040 | ais.pushIndent(); | ||
| 2041 | for (asm_node.inputs) |asm_input, i| { | ||
| 2042 | if (i + 1 < asm_node.inputs.len) { | ||
| 2043 | const next_asm_input = asm_node.inputs[i + 1]; | ||
| 2044 | try renderAsmInput(gpa, ais, tree, asm_input, .none); | ||
| 2045 | |||
| 2046 | const first_token = tree.firstToken(next_asm_input); | ||
| 2047 | try renderToken(ais, tree, first_token - 1, .newline); // , | ||
| 2048 | try renderExtraNewlineToken(ais, tree, first_token); | ||
| 2049 | } else if (asm_node.first_clobber == null) { | ||
| 2050 | try renderAsmInput(gpa, ais, tree, asm_input, .newline); | ||
| 2051 | ais.popIndent(); | ||
| 2052 | ais.setIndentDelta(indent_delta); | ||
| 2053 | ais.popIndent(); | ||
| 2054 | return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen | ||
| 2055 | } else { | ||
| 2056 | try renderAsmInput(gpa, ais, tree, asm_input, .newline); | ||
| 2057 | const comma_or_colon = tree.lastToken(asm_input) + 1; | ||
| 2058 | ais.popIndent(); | ||
| 2059 | break :colon3 switch (token_tags[comma_or_colon]) { | ||
| 2060 | .comma => comma_or_colon + 1, | ||
| 2061 | else => comma_or_colon, | ||
| 2062 | }; | ||
| 2063 | } | ||
| 2064 | } | ||
| 2065 | unreachable; | ||
| 2066 | }; | ||
| 2139 | 2067 | ||
| 2140 | .EnumLiteral => { | 2068 | try renderToken(ais, tree, colon3, .space); // : |
| 2141 | const enum_literal = @fieldParentPtr(ast.Node.EnumLiteral, "base", base); | 2069 | const first_clobber = asm_node.first_clobber.?; |
| 2070 | var tok_i = first_clobber; | ||
| 2071 | while (true) { | ||
| 2072 | switch (token_tags[tok_i + 1]) { | ||
| 2073 | .r_paren => { | ||
| 2074 | ais.setIndentDelta(indent_delta); | ||
| 2075 | ais.popIndent(); | ||
| 2076 | try renderToken(ais, tree, tok_i, .newline); | ||
| 2077 | return renderToken(ais, tree, tok_i + 1, space); | ||
| 2078 | }, | ||
| 2079 | .comma => { | ||
| 2080 | try renderToken(ais, tree, tok_i, .none); | ||
| 2081 | try renderToken(ais, tree, tok_i + 1, .space); | ||
| 2082 | tok_i += 2; | ||
| 2083 | }, | ||
| 2084 | else => unreachable, | ||
| 2085 | } | ||
| 2086 | } else unreachable; // TODO shouldn't need this on while(true) | ||
| 2087 | } | ||
| 2142 | 2088 | ||
| 2143 | try renderToken(tree, ais, enum_literal.dot, Space.None); // . | 2089 | fn renderCall( |
| 2144 | return renderToken(tree, ais, enum_literal.name, space); // name | 2090 | gpa: *Allocator, |
| 2145 | }, | 2091 | ais: *Ais, |
| 2092 | tree: ast.Tree, | ||
| 2093 | call: ast.full.Call, | ||
| 2094 | space: Space, | ||
| 2095 | ) Error!void { | ||
| 2096 | const token_tags = tree.tokens.items(.tag); | ||
| 2097 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2146 | 2098 | ||
| 2147 | .ContainerField, | 2099 | if (call.async_token) |async_token| { |
| 2148 | .Root, | 2100 | try renderToken(ais, tree, async_token, .space); |
| 2149 | .VarDecl, | 2101 | } |
| 2150 | .Use, | 2102 | try renderExpression(gpa, ais, tree, call.ast.fn_expr, .none); |
| 2151 | .TestDecl, | 2103 | |
| 2152 | => unreachable, | 2104 | const lparen = call.ast.lparen; |
| 2105 | const params = call.ast.params; | ||
| 2106 | if (params.len == 0) { | ||
| 2107 | ais.pushIndentNextLine(); | ||
| 2108 | try renderToken(ais, tree, lparen, .none); | ||
| 2109 | ais.popIndent(); | ||
| 2110 | return renderToken(ais, tree, lparen + 1, space); // ) | ||
| 2153 | } | 2111 | } |
| 2154 | } | ||
| 2155 | 2112 | ||
| 2156 | fn renderArrayType( | 2113 | const last_param = params[params.len - 1]; |
| 2157 | allocator: *mem.Allocator, | 2114 | const after_last_param_tok = tree.lastToken(last_param) + 1; |
| 2158 | ais: anytype, | 2115 | if (token_tags[after_last_param_tok] == .comma) { |
| 2159 | tree: *ast.Tree, | 2116 | ais.pushIndentNextLine(); |
| 2160 | lbracket: ast.TokenIndex, | 2117 | try renderToken(ais, tree, lparen, .newline); // ( |
| 2161 | rhs: *ast.Node, | 2118 | for (params) |param_node, i| { |
| 2162 | len_expr: *ast.Node, | 2119 | if (i + 1 < params.len) { |
| 2163 | opt_sentinel: ?*ast.Node, | 2120 | try renderExpression(gpa, ais, tree, param_node, .none); |
| 2164 | space: Space, | ||
| 2165 | ) (@TypeOf(ais.*).Error || Error)!void { | ||
| 2166 | const rbracket = tree.nextToken(if (opt_sentinel) |sentinel| | ||
| 2167 | sentinel.lastToken() | ||
| 2168 | else | ||
| 2169 | len_expr.lastToken()); | ||
| 2170 | 2121 | ||
| 2171 | const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment; | 2122 | // Unindent the comma for multiline string literals. |
| 2172 | const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment; | 2123 | const is_multiline_string = |
| 2173 | const new_space = if (ends_with_comment) Space.Newline else Space.None; | 2124 | token_tags[tree.firstToken(param_node)] == .multiline_string_literal_line; |
| 2174 | { | 2125 | if (is_multiline_string) ais.popIndent(); |
| 2175 | const do_indent = (starts_with_comment or ends_with_comment); | ||
| 2176 | if (do_indent) ais.pushIndent(); | ||
| 2177 | defer if (do_indent) ais.popIndent(); | ||
| 2178 | 2126 | ||
| 2179 | try renderToken(tree, ais, lbracket, Space.None); // [ | 2127 | const comma = tree.lastToken(param_node) + 1; |
| 2180 | try renderExpression(allocator, ais, tree, len_expr, new_space); | 2128 | try renderToken(ais, tree, comma, .newline); // , |
| 2181 | 2129 | ||
| 2182 | if (starts_with_comment) { | 2130 | if (is_multiline_string) ais.pushIndent(); |
| 2183 | try ais.maybeInsertNewline(); | 2131 | |
| 2132 | try renderExtraNewline(ais, tree, params[i + 1]); | ||
| 2133 | } else { | ||
| 2134 | try renderExpression(gpa, ais, tree, param_node, .comma); | ||
| 2135 | } | ||
| 2184 | } | 2136 | } |
| 2185 | if (opt_sentinel) |sentinel| { | 2137 | ais.popIndent(); |
| 2186 | const colon_token = tree.prevToken(sentinel.firstToken()); | 2138 | return renderToken(ais, tree, after_last_param_tok + 1, space); // ) |
| 2187 | try renderToken(tree, ais, colon_token, Space.None); // : | 2139 | } |
| 2188 | try renderExpression(allocator, ais, tree, sentinel, Space.None); | 2140 | |
| 2141 | try renderToken(ais, tree, lparen, .none); // ( | ||
| 2142 | |||
| 2143 | for (params) |param_node, i| { | ||
| 2144 | const first_param_token = tree.firstToken(param_node); | ||
| 2145 | if (token_tags[first_param_token] == .multiline_string_literal_line or | ||
| 2146 | hasSameLineComment(tree, first_param_token - 1)) | ||
| 2147 | { | ||
| 2148 | ais.pushIndentOneShot(); | ||
| 2189 | } | 2149 | } |
| 2190 | if (starts_with_comment) { | 2150 | try renderExpression(gpa, ais, tree, param_node, .none); |
| 2191 | try ais.maybeInsertNewline(); | 2151 | |
| 2152 | if (i + 1 < params.len) { | ||
| 2153 | const comma = tree.lastToken(param_node) + 1; | ||
| 2154 | const next_multiline_string = | ||
| 2155 | token_tags[tree.firstToken(params[i + 1])] == .multiline_string_literal_line; | ||
| 2156 | const comma_space: Space = if (next_multiline_string) .none else .space; | ||
| 2157 | try renderToken(ais, tree, comma, comma_space); | ||
| 2192 | } | 2158 | } |
| 2193 | } | 2159 | } |
| 2194 | try renderToken(tree, ais, rbracket, Space.None); // ] | ||
| 2195 | 2160 | ||
| 2196 | return renderExpression(allocator, ais, tree, rhs, space); | 2161 | return renderToken(ais, tree, after_last_param_tok, space); // ) |
| 2197 | } | 2162 | } |
| 2198 | 2163 | ||
| 2199 | fn renderAsmOutput( | 2164 | /// Renders the given expression indented, popping the indent before rendering |
| 2200 | allocator: *mem.Allocator, | 2165 | /// any following line comments |
| 2201 | ais: anytype, | 2166 | fn renderExpressionIndented(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void { |
| 2202 | tree: *ast.Tree, | 2167 | const token_starts = tree.tokens.items(.start); |
| 2203 | asm_output: *const ast.Node.Asm.Output, | 2168 | const token_tags = tree.tokens.items(.tag); |
| 2204 | space: Space, | 2169 | |
| 2205 | ) (@TypeOf(ais.*).Error || Error)!void { | 2170 | ais.pushIndent(); |
| 2206 | try ais.writer().writeAll("["); | 2171 | |
| 2207 | try renderExpression(allocator, ais, tree, asm_output.symbolic_name, Space.None); | 2172 | var last_token = tree.lastToken(node); |
| 2208 | try ais.writer().writeAll("] "); | 2173 | const punctuation = switch (space) { |
| 2209 | try renderExpression(allocator, ais, tree, asm_output.constraint, Space.None); | 2174 | .none, .space, .newline, .skip => false, |
| 2210 | try ais.writer().writeAll(" ("); | 2175 | .comma => true, |
| 2211 | 2176 | .comma_space => token_tags[last_token + 1] == .comma, | |
| 2212 | switch (asm_output.kind) { | 2177 | .semicolon => token_tags[last_token + 1] == .semicolon, |
| 2213 | .Variable => |variable_name| { | 2178 | }; |
| 2214 | try renderExpression(allocator, ais, tree, &variable_name.base, Space.None); | 2179 | |
| 2180 | try renderExpression(gpa, ais, tree, node, if (punctuation) .none else .skip); | ||
| 2181 | |||
| 2182 | switch (space) { | ||
| 2183 | .none, .space, .newline, .skip => {}, | ||
| 2184 | .comma => { | ||
| 2185 | if (token_tags[last_token + 1] == .comma) { | ||
| 2186 | try renderToken(ais, tree, last_token + 1, .skip); | ||
| 2187 | last_token += 1; | ||
| 2188 | } else { | ||
| 2189 | try ais.writer().writeByte(','); | ||
| 2190 | } | ||
| 2191 | }, | ||
| 2192 | .comma_space => if (token_tags[last_token + 1] == .comma) { | ||
| 2193 | try renderToken(ais, tree, last_token + 1, .skip); | ||
| 2194 | last_token += 1; | ||
| 2215 | }, | 2195 | }, |
| 2216 | .Return => |return_type| { | 2196 | .semicolon => if (token_tags[last_token + 1] == .semicolon) { |
| 2217 | try ais.writer().writeAll("-> "); | 2197 | try renderToken(ais, tree, last_token + 1, .skip); |
| 2218 | try renderExpression(allocator, ais, tree, return_type, Space.None); | 2198 | last_token += 1; |
| 2219 | }, | 2199 | }, |
| 2220 | } | 2200 | } |
| 2221 | 2201 | ||
| 2222 | return renderToken(tree, ais, asm_output.lastToken(), space); // ) | 2202 | ais.popIndent(); |
| 2223 | } | ||
| 2224 | 2203 | ||
| 2225 | fn renderAsmInput( | 2204 | if (space == .skip) return; |
| 2226 | allocator: *mem.Allocator, | ||
| 2227 | ais: anytype, | ||
| 2228 | tree: *ast.Tree, | ||
| 2229 | asm_input: *const ast.Node.Asm.Input, | ||
| 2230 | space: Space, | ||
| 2231 | ) (@TypeOf(ais.*).Error || Error)!void { | ||
| 2232 | try ais.writer().writeAll("["); | ||
| 2233 | try renderExpression(allocator, ais, tree, asm_input.symbolic_name, Space.None); | ||
| 2234 | try ais.writer().writeAll("] "); | ||
| 2235 | try renderExpression(allocator, ais, tree, asm_input.constraint, Space.None); | ||
| 2236 | try ais.writer().writeAll(" ("); | ||
| 2237 | try renderExpression(allocator, ais, tree, asm_input.expr, Space.None); | ||
| 2238 | return renderToken(tree, ais, asm_input.lastToken(), space); // ) | ||
| 2239 | } | ||
| 2240 | 2205 | ||
| 2241 | fn renderVarDecl( | 2206 | const comment_start = token_starts[last_token] + tokenSliceForRender(tree, last_token).len; |
| 2242 | allocator: *mem.Allocator, | 2207 | const comment = try renderComments(ais, tree, comment_start, token_starts[last_token + 1]); |
| 2243 | ais: anytype, | ||
| 2244 | tree: *ast.Tree, | ||
| 2245 | var_decl: *ast.Node.VarDecl, | ||
| 2246 | ) (@TypeOf(ais.*).Error || Error)!void { | ||
| 2247 | if (var_decl.getVisibToken()) |visib_token| { | ||
| 2248 | try renderToken(tree, ais, visib_token, Space.Space); // pub | ||
| 2249 | } | ||
| 2250 | 2208 | ||
| 2251 | if (var_decl.getExternExportToken()) |extern_export_token| { | 2209 | if (!comment) switch (space) { |
| 2252 | try renderToken(tree, ais, extern_export_token, Space.Space); // extern | 2210 | .none => {}, |
| 2211 | .space, | ||
| 2212 | .comma_space, | ||
| 2213 | => try ais.writer().writeByte(' '), | ||
| 2214 | .newline, | ||
| 2215 | .comma, | ||
| 2216 | .semicolon, | ||
| 2217 | => try ais.insertNewline(), | ||
| 2218 | .skip => unreachable, | ||
| 2219 | }; | ||
| 2220 | } | ||
| 2253 | 2221 | ||
| 2254 | if (var_decl.getLibName()) |lib_name| { | 2222 | /// Render an expression, and the comma that follows it, if it is present in the source. |
| 2255 | try renderExpression(allocator, ais, tree, lib_name, Space.Space); // "lib" | 2223 | fn renderExpressionComma(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void { |
| 2256 | } | 2224 | const token_tags = tree.tokens.items(.tag); |
| 2225 | const maybe_comma = tree.lastToken(node) + 1; | ||
| 2226 | if (token_tags[maybe_comma] == .comma) { | ||
| 2227 | try renderExpression(gpa, ais, tree, node, .none); | ||
| 2228 | return renderToken(ais, tree, maybe_comma, space); | ||
| 2229 | } else { | ||
| 2230 | return renderExpression(gpa, ais, tree, node, space); | ||
| 2257 | } | 2231 | } |
| 2232 | } | ||
| 2258 | 2233 | ||
| 2259 | if (var_decl.getComptimeToken()) |comptime_token| { | 2234 | fn renderTokenComma(ais: *Ais, tree: ast.Tree, token: ast.TokenIndex, space: Space) Error!void { |
| 2260 | try renderToken(tree, ais, comptime_token, Space.Space); // comptime | 2235 | const token_tags = tree.tokens.items(.tag); |
| 2236 | const maybe_comma = token + 1; | ||
| 2237 | if (token_tags[maybe_comma] == .comma) { | ||
| 2238 | try renderToken(ais, tree, token, .none); | ||
| 2239 | return renderToken(ais, tree, maybe_comma, space); | ||
| 2240 | } else { | ||
| 2241 | return renderToken(ais, tree, token, space); | ||
| 2261 | } | 2242 | } |
| 2243 | } | ||
| 2262 | 2244 | ||
| 2263 | if (var_decl.getThreadLocalToken()) |thread_local_token| { | 2245 | const Space = enum { |
| 2264 | try renderToken(tree, ais, thread_local_token, Space.Space); // threadlocal | 2246 | /// Output the token lexeme only. |
| 2265 | } | 2247 | none, |
| 2266 | try renderToken(tree, ais, var_decl.mut_token, Space.Space); // var | 2248 | /// Output the token lexeme followed by a single space. |
| 2249 | space, | ||
| 2250 | /// Output the token lexeme followed by a newline. | ||
| 2251 | newline, | ||
| 2252 | /// If the next token is a comma, render it as well. If not, insert one. | ||
| 2253 | /// In either case, a newline will be inserted afterwards. | ||
| 2254 | comma, | ||
| 2255 | /// Additionally consume the next token if it is a comma. | ||
| 2256 | /// In either case, a space will be inserted afterwards. | ||
| 2257 | comma_space, | ||
| 2258 | /// Additionally consume the next token if it is a semicolon. | ||
| 2259 | /// In either case, a newline will be inserted afterwards. | ||
| 2260 | semicolon, | ||
| 2261 | /// Skip rendering whitespace and comments. If this is used, the caller | ||
| 2262 | /// *must* handle handle whitespace and comments manually. | ||
| 2263 | skip, | ||
| 2264 | }; | ||
| 2267 | 2265 | ||
| 2268 | const name_space = if (var_decl.getTypeNode() == null and | 2266 | fn renderToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex, space: Space) Error!void { |
| 2269 | (var_decl.getAlignNode() != null or | 2267 | const token_tags = tree.tokens.items(.tag); |
| 2270 | var_decl.getSectionNode() != null or | 2268 | const token_starts = tree.tokens.items(.start); |
| 2271 | var_decl.getInitNode() != null)) | ||
| 2272 | Space.Space | ||
| 2273 | else | ||
| 2274 | Space.None; | ||
| 2275 | try renderToken(tree, ais, var_decl.name_token, name_space); | ||
| 2276 | |||
| 2277 | if (var_decl.getTypeNode()) |type_node| { | ||
| 2278 | try renderToken(tree, ais, tree.nextToken(var_decl.name_token), Space.Space); | ||
| 2279 | const s = if (var_decl.getAlignNode() != null or | ||
| 2280 | var_decl.getSectionNode() != null or | ||
| 2281 | var_decl.getInitNode() != null) Space.Space else Space.None; | ||
| 2282 | try renderExpression(allocator, ais, tree, type_node, s); | ||
| 2283 | } | ||
| 2284 | |||
| 2285 | if (var_decl.getAlignNode()) |align_node| { | ||
| 2286 | const lparen = tree.prevToken(align_node.firstToken()); | ||
| 2287 | const align_kw = tree.prevToken(lparen); | ||
| 2288 | const rparen = tree.nextToken(align_node.lastToken()); | ||
| 2289 | try renderToken(tree, ais, align_kw, Space.None); // align | ||
| 2290 | try renderToken(tree, ais, lparen, Space.None); // ( | ||
| 2291 | try renderExpression(allocator, ais, tree, align_node, Space.None); | ||
| 2292 | const s = if (var_decl.getSectionNode() != null or var_decl.getInitNode() != null) Space.Space else Space.None; | ||
| 2293 | try renderToken(tree, ais, rparen, s); // ) | ||
| 2294 | } | ||
| 2295 | |||
| 2296 | if (var_decl.getSectionNode()) |section_node| { | ||
| 2297 | const lparen = tree.prevToken(section_node.firstToken()); | ||
| 2298 | const section_kw = tree.prevToken(lparen); | ||
| 2299 | const rparen = tree.nextToken(section_node.lastToken()); | ||
| 2300 | try renderToken(tree, ais, section_kw, Space.None); // linksection | ||
| 2301 | try renderToken(tree, ais, lparen, Space.None); // ( | ||
| 2302 | try renderExpression(allocator, ais, tree, section_node, Space.None); | ||
| 2303 | const s = if (var_decl.getInitNode() != null) Space.Space else Space.None; | ||
| 2304 | try renderToken(tree, ais, rparen, s); // ) | ||
| 2305 | } | ||
| 2306 | |||
| 2307 | if (var_decl.getInitNode()) |init_node| { | ||
| 2308 | const eq_token = var_decl.getEqToken().?; | ||
| 2309 | const eq_space = blk: { | ||
| 2310 | const loc = tree.tokenLocation(tree.token_locs[eq_token].end, tree.nextToken(eq_token)); | ||
| 2311 | break :blk if (loc.line == 0) Space.Space else Space.Newline; | ||
| 2312 | }; | ||
| 2313 | 2269 | ||
| 2314 | { | 2270 | const token_start = token_starts[token_index]; |
| 2315 | ais.pushIndent(); | 2271 | const lexeme = tokenSliceForRender(tree, token_index); |
| 2316 | defer ais.popIndent(); | ||
| 2317 | try renderToken(tree, ais, eq_token, eq_space); // = | ||
| 2318 | } | ||
| 2319 | ais.pushIndentOneShot(); | ||
| 2320 | try renderExpression(allocator, ais, tree, init_node, Space.None); | ||
| 2321 | } | ||
| 2322 | 2272 | ||
| 2323 | try renderToken(tree, ais, var_decl.semicolon_token, Space.Newline); | 2273 | try ais.writer().writeAll(lexeme); |
| 2324 | } | ||
| 2325 | 2274 | ||
| 2326 | fn renderParamDecl( | 2275 | if (space == .skip) return; |
| 2327 | allocator: *mem.Allocator, | ||
| 2328 | ais: anytype, | ||
| 2329 | tree: *ast.Tree, | ||
| 2330 | param_decl: ast.Node.FnProto.ParamDecl, | ||
| 2331 | space: Space, | ||
| 2332 | ) (@TypeOf(ais.*).Error || Error)!void { | ||
| 2333 | try renderDocComments(tree, ais, param_decl, param_decl.doc_comments); | ||
| 2334 | 2276 | ||
| 2335 | if (param_decl.comptime_token) |comptime_token| { | 2277 | if (space == .comma and token_tags[token_index + 1] != .comma) { |
| 2336 | try renderToken(tree, ais, comptime_token, Space.Space); | 2278 | try ais.writer().writeByte(','); |
| 2337 | } | ||
| 2338 | if (param_decl.noalias_token) |noalias_token| { | ||
| 2339 | try renderToken(tree, ais, noalias_token, Space.Space); | ||
| 2340 | } | ||
| 2341 | if (param_decl.name_token) |name_token| { | ||
| 2342 | try renderToken(tree, ais, name_token, Space.None); | ||
| 2343 | try renderToken(tree, ais, tree.nextToken(name_token), Space.Space); // : | ||
| 2344 | } | ||
| 2345 | switch (param_decl.param_type) { | ||
| 2346 | .any_type, .type_expr => |node| try renderExpression(allocator, ais, tree, node, space), | ||
| 2347 | } | 2279 | } |
| 2348 | } | ||
| 2349 | 2280 | ||
| 2350 | fn renderStatement( | 2281 | const comment = try renderComments(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]); |
| 2351 | allocator: *mem.Allocator, | 2282 | switch (space) { |
| 2352 | ais: anytype, | 2283 | .none => {}, |
| 2353 | tree: *ast.Tree, | 2284 | .space => if (!comment) try ais.writer().writeByte(' '), |
| 2354 | base: *ast.Node, | 2285 | .newline => if (!comment) try ais.insertNewline(), |
| 2355 | ) (@TypeOf(ais.*).Error || Error)!void { | 2286 | |
| 2356 | switch (base.tag) { | 2287 | .comma => if (token_tags[token_index + 1] == .comma) { |
| 2357 | .VarDecl => { | 2288 | try renderToken(ais, tree, token_index + 1, .newline); |
| 2358 | const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); | 2289 | } else if (!comment) { |
| 2359 | try renderVarDecl(allocator, ais, tree, var_decl); | 2290 | try ais.insertNewline(); |
| 2360 | }, | 2291 | }, |
| 2361 | else => { | ||
| 2362 | if (base.requireSemiColon()) { | ||
| 2363 | try renderExpression(allocator, ais, tree, base, Space.None); | ||
| 2364 | 2292 | ||
| 2365 | const semicolon_index = tree.nextToken(base.lastToken()); | 2293 | .comma_space => if (token_tags[token_index + 1] == .comma) { |
| 2366 | assert(tree.token_ids[semicolon_index] == .Semicolon); | 2294 | try renderToken(ais, tree, token_index + 1, .space); |
| 2367 | try renderToken(tree, ais, semicolon_index, Space.Newline); | 2295 | } else if (!comment) { |
| 2368 | } else { | 2296 | try ais.writer().writeByte(' '); |
| 2369 | try renderExpression(allocator, ais, tree, base, Space.Newline); | 2297 | }, |
| 2370 | } | 2298 | |
| 2299 | .semicolon => if (token_tags[token_index + 1] == .semicolon) { | ||
| 2300 | try renderToken(ais, tree, token_index + 1, .newline); | ||
| 2301 | } else if (!comment) { | ||
| 2302 | try ais.insertNewline(); | ||
| 2371 | }, | 2303 | }, |
| 2304 | |||
| 2305 | .skip => unreachable, | ||
| 2372 | } | 2306 | } |
| 2373 | } | 2307 | } |
| 2374 | 2308 | ||
| 2375 | const Space = enum { | 2309 | /// Returns true if there exists a comment between the start of token |
| 2376 | None, | 2310 | /// `start_token` and the start of token `end_token`. This is used to determine |
| 2377 | Newline, | 2311 | /// if e.g. a fn_proto should be wrapped and have a trailing comma inserted |
| 2378 | Comma, | 2312 | /// even if there is none in the source. |
| 2379 | Space, | 2313 | fn hasComment(tree: ast.Tree, start_token: ast.TokenIndex, end_token: ast.TokenIndex) bool { |
| 2380 | SpaceOrOutdent, | 2314 | const token_starts = tree.tokens.items(.start); |
| 2381 | NoNewline, | ||
| 2382 | NoComment, | ||
| 2383 | BlockStart, | ||
| 2384 | }; | ||
| 2385 | 2315 | ||
| 2386 | fn renderTokenOffset( | 2316 | const start = token_starts[start_token]; |
| 2387 | tree: *ast.Tree, | 2317 | const end = token_starts[end_token]; |
| 2388 | ais: anytype, | ||
| 2389 | token_index: ast.TokenIndex, | ||
| 2390 | space: Space, | ||
| 2391 | token_skip_bytes: usize, | ||
| 2392 | ) (@TypeOf(ais.*).Error || Error)!void { | ||
| 2393 | if (space == Space.BlockStart) { | ||
| 2394 | // If placing the lbrace on the current line would cause an uggly gap then put the lbrace on the next line | ||
| 2395 | const new_space = if (ais.isLineOverIndented()) Space.Newline else Space.Space; | ||
| 2396 | return renderToken(tree, ais, token_index, new_space); | ||
| 2397 | } | ||
| 2398 | 2318 | ||
| 2399 | var token_loc = tree.token_locs[token_index]; | 2319 | return mem.indexOf(u8, tree.source[start..end], "//") != null; |
| 2400 | try ais.writer().writeAll(mem.trimRight(u8, tree.tokenSliceLoc(token_loc)[token_skip_bytes..], " ")); | 2320 | } |
| 2401 | 2321 | ||
| 2402 | if (space == Space.NoComment) | 2322 | /// Assumes that start is the first byte past the previous token and |
| 2403 | return; | 2323 | /// that end is the last byte before the next token. |
| 2324 | fn renderComments(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!bool { | ||
| 2325 | var index: usize = start; | ||
| 2326 | while (mem.indexOf(u8, tree.source[index..end], "//")) |offset| { | ||
| 2327 | const comment_start = index + offset; | ||
| 2404 | 2328 | ||
| 2405 | var next_token_id = tree.token_ids[token_index + 1]; | 2329 | // If there is no newline, the comment ends with EOF |
| 2406 | var next_token_loc = tree.token_locs[token_index + 1]; | 2330 | const newline_index = mem.indexOfScalar(u8, tree.source[comment_start..end], '\n'); |
| 2331 | const newline = if (newline_index) |i| comment_start + i else null; | ||
| 2407 | 2332 | ||
| 2408 | if (space == Space.Comma) switch (next_token_id) { | 2333 | const untrimmed_comment = tree.source[comment_start .. newline orelse tree.source.len]; |
| 2409 | .Comma => return renderToken(tree, ais, token_index + 1, Space.Newline), | 2334 | const trimmed_comment = mem.trimRight(u8, untrimmed_comment, &std.ascii.spaces); |
| 2410 | .LineComment => { | 2335 | |
| 2411 | try ais.writer().writeAll(", "); | 2336 | // Don't leave any whitespace at the start of the file |
| 2412 | return renderToken(tree, ais, token_index + 1, Space.Newline); | 2337 | if (index != 0) { |
| 2413 | }, | 2338 | if (index == start and mem.containsAtLeast(u8, tree.source[index..comment_start], 2, "\n")) { |
| 2414 | else => { | 2339 | // Leave up to one empty line before the first comment |
| 2415 | if (token_index + 2 < tree.token_ids.len and | ||
| 2416 | tree.token_ids[token_index + 2] == .MultilineStringLiteralLine) | ||
| 2417 | { | ||
| 2418 | try ais.writer().writeAll(","); | ||
| 2419 | return; | ||
| 2420 | } else { | ||
| 2421 | try ais.writer().writeAll(","); | ||
| 2422 | try ais.insertNewline(); | 2340 | try ais.insertNewline(); |
| 2423 | return; | 2341 | try ais.insertNewline(); |
| 2342 | } else if (mem.indexOfScalar(u8, tree.source[index..comment_start], '\n') != null) { | ||
| 2343 | // Respect the newline directly before the comment. | ||
| 2344 | // Note: This allows an empty line between comments | ||
| 2345 | try ais.insertNewline(); | ||
| 2346 | } else if (index == start) { | ||
| 2347 | // Otherwise if the first comment is on the same line as | ||
| 2348 | // the token before it, prefix it with a single space. | ||
| 2349 | try ais.writer().writeByte(' '); | ||
| 2424 | } | 2350 | } |
| 2425 | }, | ||
| 2426 | }; | ||
| 2427 | |||
| 2428 | // Skip over same line doc comments | ||
| 2429 | var offset: usize = 1; | ||
| 2430 | if (next_token_id == .DocComment) { | ||
| 2431 | const loc = tree.tokenLocationLoc(token_loc.end, next_token_loc); | ||
| 2432 | if (loc.line == 0) { | ||
| 2433 | offset += 1; | ||
| 2434 | next_token_id = tree.token_ids[token_index + offset]; | ||
| 2435 | next_token_loc = tree.token_locs[token_index + offset]; | ||
| 2436 | } | 2351 | } |
| 2437 | } | ||
| 2438 | 2352 | ||
| 2439 | if (next_token_id != .LineComment) { | 2353 | try ais.writer().print("{s}\n", .{trimmed_comment}); |
| 2440 | switch (space) { | 2354 | index = 1 + (newline orelse return true); |
| 2441 | Space.None, Space.NoNewline => return, | ||
| 2442 | Space.Newline => { | ||
| 2443 | if (next_token_id == .MultilineStringLiteralLine) { | ||
| 2444 | return; | ||
| 2445 | } else { | ||
| 2446 | try ais.insertNewline(); | ||
| 2447 | return; | ||
| 2448 | } | ||
| 2449 | }, | ||
| 2450 | Space.Space, Space.SpaceOrOutdent => { | ||
| 2451 | if (next_token_id == .MultilineStringLiteralLine) | ||
| 2452 | return; | ||
| 2453 | try ais.writer().writeByte(' '); | ||
| 2454 | return; | ||
| 2455 | }, | ||
| 2456 | Space.NoComment, Space.Comma, Space.BlockStart => unreachable, | ||
| 2457 | } | ||
| 2458 | } | ||
| 2459 | 2355 | ||
| 2460 | while (true) { | 2356 | if (ais.disabled_offset) |disabled_offset| { |
| 2461 | const comment_is_empty = mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " ").len == 2; | 2357 | if (mem.eql(u8, trimmed_comment, "// zig fmt: on")) { |
| 2462 | if (comment_is_empty) { | 2358 | // write the source for which formatting was disabled directly |
| 2463 | switch (space) { | 2359 | // to the underlying writer, fixing up invaild whitespace |
| 2464 | Space.Newline => { | 2360 | try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..index]); |
| 2465 | offset += 1; | 2361 | ais.disabled_offset = null; |
| 2466 | token_loc = next_token_loc; | ||
| 2467 | next_token_id = tree.token_ids[token_index + offset]; | ||
| 2468 | next_token_loc = tree.token_locs[token_index + offset]; | ||
| 2469 | if (next_token_id != .LineComment) { | ||
| 2470 | try ais.insertNewline(); | ||
| 2471 | return; | ||
| 2472 | } | ||
| 2473 | }, | ||
| 2474 | else => break, | ||
| 2475 | } | 2362 | } |
| 2476 | } else { | 2363 | } else if (mem.eql(u8, trimmed_comment, "// zig fmt: off")) { |
| 2477 | break; | 2364 | ais.disabled_offset = index; |
| 2478 | } | 2365 | } |
| 2479 | } | 2366 | } |
| 2480 | 2367 | ||
| 2481 | var loc = tree.tokenLocationLoc(token_loc.end, next_token_loc); | 2368 | if (index != start and mem.containsAtLeast(u8, tree.source[index - 1 .. end], 2, "\n")) { |
| 2482 | if (loc.line == 0) { | 2369 | try ais.insertNewline(); |
| 2483 | if (tree.token_ids[token_index] != .MultilineStringLiteralLine) { | ||
| 2484 | try ais.writer().writeByte(' '); | ||
| 2485 | } | ||
| 2486 | try ais.writer().writeAll(mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " ")); | ||
| 2487 | offset = 2; | ||
| 2488 | token_loc = next_token_loc; | ||
| 2489 | next_token_loc = tree.token_locs[token_index + offset]; | ||
| 2490 | next_token_id = tree.token_ids[token_index + offset]; | ||
| 2491 | if (next_token_id != .LineComment) { | ||
| 2492 | switch (space) { | ||
| 2493 | .None, .Space, .SpaceOrOutdent => { | ||
| 2494 | try ais.insertNewline(); | ||
| 2495 | }, | ||
| 2496 | .Newline => { | ||
| 2497 | if (next_token_id == .MultilineStringLiteralLine) { | ||
| 2498 | return; | ||
| 2499 | } else { | ||
| 2500 | try ais.insertNewline(); | ||
| 2501 | return; | ||
| 2502 | } | ||
| 2503 | }, | ||
| 2504 | .NoNewline => {}, | ||
| 2505 | .NoComment, .Comma, .BlockStart => unreachable, | ||
| 2506 | } | ||
| 2507 | return; | ||
| 2508 | } | ||
| 2509 | loc = tree.tokenLocationLoc(token_loc.end, next_token_loc); | ||
| 2510 | } | 2370 | } |
| 2511 | 2371 | ||
| 2512 | while (true) { | 2372 | return index != start; |
| 2513 | // translate-c doesn't generate correct newlines | 2373 | } |
| 2514 | // in generated code (loc.line == 0) so treat that case | 2374 | |
| 2515 | // as though there was meant to be a newline between the tokens | 2375 | fn renderExtraNewline(ais: *Ais, tree: ast.Tree, node: ast.Node.Index) Error!void { |
| 2516 | var newline_count = if (loc.line <= 1) @as(u8, 1) else @as(u8, 2); | 2376 | return renderExtraNewlineToken(ais, tree, tree.firstToken(node)); |
| 2517 | while (newline_count > 0) : (newline_count -= 1) try ais.insertNewline(); | 2377 | } |
| 2518 | try ais.writer().writeAll(mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " ")); | 2378 | |
| 2519 | 2379 | /// Check if there is an empty line immediately before the given token. If so, render it. | |
| 2520 | offset += 1; | 2380 | fn renderExtraNewlineToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex) Error!void { |
| 2521 | token_loc = next_token_loc; | 2381 | const token_starts = tree.tokens.items(.start); |
| 2522 | next_token_loc = tree.token_locs[token_index + offset]; | 2382 | const token_start = token_starts[token_index]; |
| 2523 | next_token_id = tree.token_ids[token_index + offset]; | 2383 | if (token_start == 0) return; |
| 2524 | if (next_token_id != .LineComment) { | 2384 | const prev_token_end = if (token_index == 0) |
| 2525 | switch (space) { | 2385 | 0 |
| 2526 | .Newline => { | 2386 | else |
| 2527 | if (next_token_id == .MultilineStringLiteralLine) { | 2387 | token_starts[token_index - 1] + tokenSliceForRender(tree, token_index - 1).len; |
| 2528 | return; | 2388 | |
| 2529 | } else { | 2389 | // If there is a comment present, it will handle the empty line |
| 2530 | try ais.insertNewline(); | 2390 | if (mem.indexOf(u8, tree.source[prev_token_end..token_start], "//") != null) return; |
| 2531 | return; | 2391 | |
| 2532 | } | 2392 | // Iterate backwards to the end of the previous token, stopping if a |
| 2533 | }, | 2393 | // non-whitespace character is encountered or two newlines have been found. |
| 2534 | .None, .Space, .SpaceOrOutdent => { | 2394 | var i = token_start - 1; |
| 2535 | try ais.insertNewline(); | 2395 | var newlines: u2 = 0; |
| 2536 | }, | 2396 | while (std.ascii.isSpace(tree.source[i])) : (i -= 1) { |
| 2537 | .NoNewline => {}, | 2397 | if (tree.source[i] == '\n') newlines += 1; |
| 2538 | .NoComment, .Comma, .BlockStart => unreachable, | 2398 | if (newlines == 2) return ais.insertNewline(); |
| 2539 | } | 2399 | if (i == prev_token_end) break; |
| 2540 | return; | ||
| 2541 | } | ||
| 2542 | loc = tree.tokenLocationLoc(token_loc.end, next_token_loc); | ||
| 2543 | } | 2400 | } |
| 2544 | } | 2401 | } |
| 2545 | 2402 | ||
| 2546 | fn renderToken( | 2403 | /// end_token is the token one past the last doc comment token. This function |
| 2547 | tree: *ast.Tree, | 2404 | /// searches backwards from there. |
| 2548 | ais: anytype, | 2405 | fn renderDocComments(ais: *Ais, tree: ast.Tree, end_token: ast.TokenIndex) Error!void { |
| 2549 | token_index: ast.TokenIndex, | 2406 | // Search backwards for the first doc comment. |
| 2550 | space: Space, | 2407 | const token_tags = tree.tokens.items(.tag); |
| 2551 | ) (@TypeOf(ais.*).Error || Error)!void { | 2408 | if (end_token == 0) return; |
| 2552 | return renderTokenOffset(tree, ais, token_index, space, 0); | 2409 | var tok = end_token - 1; |
| 2410 | while (token_tags[tok] == .doc_comment) { | ||
| 2411 | if (tok == 0) break; | ||
| 2412 | tok -= 1; | ||
| 2413 | } else { | ||
| 2414 | tok += 1; | ||
| 2415 | } | ||
| 2416 | const first_tok = tok; | ||
| 2417 | if (first_tok == end_token) return; | ||
| 2418 | try renderExtraNewlineToken(ais, tree, first_tok); | ||
| 2419 | |||
| 2420 | while (token_tags[tok] == .doc_comment) : (tok += 1) { | ||
| 2421 | try renderToken(ais, tree, tok, .newline); | ||
| 2422 | } | ||
| 2553 | } | 2423 | } |
| 2554 | 2424 | ||
| 2555 | fn renderDocComments( | 2425 | /// start_token is first container doc comment token. |
| 2556 | tree: *ast.Tree, | 2426 | fn renderContainerDocComments(ais: *Ais, tree: ast.Tree, start_token: ast.TokenIndex) Error!void { |
| 2557 | ais: anytype, | 2427 | const token_tags = tree.tokens.items(.tag); |
| 2558 | node: anytype, | 2428 | var tok = start_token; |
| 2559 | doc_comments: ?*ast.Node.DocComment, | 2429 | while (token_tags[tok] == .container_doc_comment) : (tok += 1) { |
| 2560 | ) (@TypeOf(ais.*).Error || Error)!void { | 2430 | try renderToken(ais, tree, tok, .newline); |
| 2561 | const comment = doc_comments orelse return; | 2431 | } |
| 2562 | return renderDocCommentsToken(tree, ais, comment, node.firstToken()); | 2432 | // Render extra newline if there is one between final container doc comment and |
| 2433 | // the next token. If the next token is a doc comment, that code path | ||
| 2434 | // will have its own logic to insert a newline. | ||
| 2435 | if (token_tags[tok] != .doc_comment) { | ||
| 2436 | try renderExtraNewlineToken(ais, tree, tok); | ||
| 2437 | } | ||
| 2563 | } | 2438 | } |
| 2564 | 2439 | ||
| 2565 | fn renderDocCommentsToken( | 2440 | fn tokenSliceForRender(tree: ast.Tree, token_index: ast.TokenIndex) []const u8 { |
| 2566 | tree: *ast.Tree, | 2441 | var ret = tree.tokenSlice(token_index); |
| 2567 | ais: anytype, | 2442 | if (tree.tokens.items(.tag)[token_index] == .multiline_string_literal_line) { |
| 2568 | comment: *ast.Node.DocComment, | 2443 | assert(ret[ret.len - 1] == '\n'); |
| 2569 | first_token: ast.TokenIndex, | 2444 | ret.len -= 1; |
| 2570 | ) (@TypeOf(ais.*).Error || Error)!void { | ||
| 2571 | var tok_i = comment.first_line; | ||
| 2572 | while (true) : (tok_i += 1) { | ||
| 2573 | switch (tree.token_ids[tok_i]) { | ||
| 2574 | .DocComment, .ContainerDocComment => { | ||
| 2575 | if (comment.first_line < first_token) { | ||
| 2576 | try renderToken(tree, ais, tok_i, Space.Newline); | ||
| 2577 | } else { | ||
| 2578 | try renderToken(tree, ais, tok_i, Space.NoComment); | ||
| 2579 | try ais.insertNewline(); | ||
| 2580 | } | ||
| 2581 | }, | ||
| 2582 | .LineComment => continue, | ||
| 2583 | else => break, | ||
| 2584 | } | ||
| 2585 | } | 2445 | } |
| 2446 | return ret; | ||
| 2586 | } | 2447 | } |
| 2587 | 2448 | ||
| 2588 | fn nodeIsBlock(base: *const ast.Node) bool { | 2449 | fn hasSameLineComment(tree: ast.Tree, token_index: ast.TokenIndex) bool { |
| 2589 | return switch (base.tag) { | 2450 | const token_starts = tree.tokens.items(.start); |
| 2590 | .Block, | 2451 | const between_source = tree.source[token_starts[token_index]..token_starts[token_index + 1]]; |
| 2591 | .LabeledBlock, | 2452 | for (between_source) |byte| switch (byte) { |
| 2592 | .If, | 2453 | '\n' => return false, |
| 2593 | .For, | 2454 | '/' => return true, |
| 2594 | .While, | 2455 | else => continue, |
| 2595 | .Switch, | 2456 | }; |
| 2596 | => true, | 2457 | return false; |
| 2597 | else => false, | 2458 | } |
| 2459 | |||
| 2460 | /// Returns `true` if and only if there are any tokens or line comments between | ||
| 2461 | /// start_token and end_token. | ||
| 2462 | fn anythingBetween(tree: ast.Tree, start_token: ast.TokenIndex, end_token: ast.TokenIndex) bool { | ||
| 2463 | if (start_token + 1 != end_token) return true; | ||
| 2464 | const token_starts = tree.tokens.items(.start); | ||
| 2465 | const between_source = tree.source[token_starts[start_token]..token_starts[start_token + 1]]; | ||
| 2466 | for (between_source) |byte| switch (byte) { | ||
| 2467 | '/' => return true, | ||
| 2468 | else => continue, | ||
| 2598 | }; | 2469 | }; |
| 2470 | return false; | ||
| 2599 | } | 2471 | } |
| 2600 | 2472 | ||
| 2601 | fn nodeCausesSliceOpSpace(base: *ast.Node) bool { | 2473 | fn writeFixingWhitespace(writer: std.ArrayList(u8).Writer, slice: []const u8) Error!void { |
| 2602 | return switch (base.tag) { | 2474 | for (slice) |byte| switch (byte) { |
| 2603 | .Catch, | 2475 | '\t' => try writer.writeAll(" " ** 4), |
| 2604 | .Add, | 2476 | '\r' => {}, |
| 2605 | .AddWrap, | 2477 | else => try writer.writeByte(byte), |
| 2606 | .ArrayCat, | 2478 | }; |
| 2607 | .ArrayMult, | 2479 | } |
| 2608 | .Assign, | 2480 | |
| 2609 | .AssignBitAnd, | 2481 | fn nodeIsBlock(tag: ast.Node.Tag) bool { |
| 2610 | .AssignBitOr, | 2482 | return switch (tag) { |
| 2611 | .AssignBitShiftLeft, | 2483 | .block, |
| 2612 | .AssignBitShiftRight, | 2484 | .block_semicolon, |
| 2613 | .AssignBitXor, | 2485 | .block_two, |
| 2614 | .AssignDiv, | 2486 | .block_two_semicolon, |
| 2615 | .AssignSub, | 2487 | .@"if", |
| 2616 | .AssignSubWrap, | 2488 | .if_simple, |
| 2617 | .AssignMod, | 2489 | .@"for", |
| 2618 | .AssignAdd, | 2490 | .for_simple, |
| 2619 | .AssignAddWrap, | 2491 | .@"while", |
| 2620 | .AssignMul, | 2492 | .while_simple, |
| 2621 | .AssignMulWrap, | 2493 | .while_cont, |
| 2622 | .BangEqual, | 2494 | .@"switch", |
| 2623 | .BitAnd, | 2495 | .switch_comma, |
| 2624 | .BitOr, | ||
| 2625 | .BitShiftLeft, | ||
| 2626 | .BitShiftRight, | ||
| 2627 | .BitXor, | ||
| 2628 | .BoolAnd, | ||
| 2629 | .BoolOr, | ||
| 2630 | .Div, | ||
| 2631 | .EqualEqual, | ||
| 2632 | .ErrorUnion, | ||
| 2633 | .GreaterOrEqual, | ||
| 2634 | .GreaterThan, | ||
| 2635 | .LessOrEqual, | ||
| 2636 | .LessThan, | ||
| 2637 | .MergeErrorSets, | ||
| 2638 | .Mod, | ||
| 2639 | .Mul, | ||
| 2640 | .MulWrap, | ||
| 2641 | .Range, | ||
| 2642 | .Sub, | ||
| 2643 | .SubWrap, | ||
| 2644 | .OrElse, | ||
| 2645 | => true, | 2496 | => true, |
| 2497 | else => false, | ||
| 2498 | }; | ||
| 2499 | } | ||
| 2646 | 2500 | ||
| 2501 | fn nodeIsIf(tag: ast.Node.Tag) bool { | ||
| 2502 | return switch (tag) { | ||
| 2503 | .@"if", .if_simple => true, | ||
| 2647 | else => false, | 2504 | else => false, |
| 2648 | }; | 2505 | }; |
| 2649 | } | 2506 | } |
| 2650 | 2507 | ||
| 2651 | fn copyFixingWhitespace(ais: anytype, slice: []const u8) @TypeOf(ais.*).Error!void { | 2508 | fn nodeCausesSliceOpSpace(tag: ast.Node.Tag) bool { |
| 2652 | for (slice) |byte| switch (byte) { | 2509 | return switch (tag) { |
| 2653 | '\t' => try ais.writer().writeAll(" "), | 2510 | .@"catch", |
| 2654 | '\r' => {}, | 2511 | .add, |
| 2655 | else => try ais.writer().writeByte(byte), | 2512 | .add_wrap, |
| 2513 | .array_cat, | ||
| 2514 | .array_mult, | ||
| 2515 | .assign, | ||
| 2516 | .assign_bit_and, | ||
| 2517 | .assign_bit_or, | ||
| 2518 | .assign_bit_shift_left, | ||
| 2519 | .assign_bit_shift_right, | ||
| 2520 | .assign_bit_xor, | ||
| 2521 | .assign_div, | ||
| 2522 | .assign_sub, | ||
| 2523 | .assign_sub_wrap, | ||
| 2524 | .assign_mod, | ||
| 2525 | .assign_add, | ||
| 2526 | .assign_add_wrap, | ||
| 2527 | .assign_mul, | ||
| 2528 | .assign_mul_wrap, | ||
| 2529 | .bang_equal, | ||
| 2530 | .bit_and, | ||
| 2531 | .bit_or, | ||
| 2532 | .bit_shift_left, | ||
| 2533 | .bit_shift_right, | ||
| 2534 | .bit_xor, | ||
| 2535 | .bool_and, | ||
| 2536 | .bool_or, | ||
| 2537 | .div, | ||
| 2538 | .equal_equal, | ||
| 2539 | .error_union, | ||
| 2540 | .greater_or_equal, | ||
| 2541 | .greater_than, | ||
| 2542 | .less_or_equal, | ||
| 2543 | .less_than, | ||
| 2544 | .merge_error_sets, | ||
| 2545 | .mod, | ||
| 2546 | .mul, | ||
| 2547 | .mul_wrap, | ||
| 2548 | .sub, | ||
| 2549 | .sub_wrap, | ||
| 2550 | .@"orelse", | ||
| 2551 | => true, | ||
| 2552 | |||
| 2553 | else => false, | ||
| 2656 | }; | 2554 | }; |
| 2657 | } | 2555 | } |
| 2658 | 2556 | ||
| 2659 | // Returns the number of nodes in `expr` that are on the same line as `rtoken`, | 2557 | // Returns the number of nodes in `expr` that are on the same line as `rtoken`, |
| 2660 | // or null if they all are on the same line. | 2558 | // or null if they all are on the same line. |
| 2661 | fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize { | 2559 | fn rowSize(tree: ast.Tree, exprs: []const ast.Node.Index, rtoken: ast.TokenIndex) ?usize { |
| 2662 | const first_token = exprs[0].firstToken(); | 2560 | const token_tags = tree.tokens.items(.tag); |
| 2663 | const first_loc = tree.tokenLocation(tree.token_locs[first_token].start, rtoken); | 2561 | |
| 2664 | if (first_loc.line == 0) { | 2562 | const first_token = tree.firstToken(exprs[0]); |
| 2665 | const maybe_comma = tree.prevToken(rtoken); | 2563 | if (tree.tokensOnSameLine(first_token, rtoken)) { |
| 2666 | if (tree.token_ids[maybe_comma] == .Comma) | 2564 | const maybe_comma = rtoken - 1; |
| 2565 | if (token_tags[maybe_comma] == .comma) | ||
| 2667 | return 1; | 2566 | return 1; |
| 2668 | return null; // no newlines | 2567 | return null; // no newlines |
| 2669 | } | 2568 | } |
| ... | @@ -2671,9 +2570,8 @@ fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize { | ... | @@ -2671,9 +2570,8 @@ fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize { |
| 2671 | var count: usize = 1; | 2570 | var count: usize = 1; |
| 2672 | for (exprs) |expr, i| { | 2571 | for (exprs) |expr, i| { |
| 2673 | if (i + 1 < exprs.len) { | 2572 | if (i + 1 < exprs.len) { |
| 2674 | const expr_last_token = expr.lastToken() + 1; | 2573 | const expr_last_token = tree.lastToken(expr) + 1; |
| 2675 | const loc = tree.tokenLocation(tree.token_locs[expr_last_token].start, exprs[i + 1].firstToken()); | 2574 | if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(exprs[i + 1]))) return count; |
| 2676 | if (loc.line != 0) return count; | ||
| 2677 | count += 1; | 2575 | count += 1; |
| 2678 | } else { | 2576 | } else { |
| 2679 | return count; | 2577 | return count; |
| ... | @@ -2681,3 +2579,150 @@ fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize { | ... | @@ -2681,3 +2579,150 @@ fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize { |
| 2681 | } | 2579 | } |
| 2682 | unreachable; | 2580 | unreachable; |
| 2683 | } | 2581 | } |
| 2582 | |||
| 2583 | /// Automatically inserts indentation of written data by keeping | ||
| 2584 | /// track of the current indentation level | ||
| 2585 | fn AutoIndentingStream(comptime UnderlyingWriter: type) type { | ||
| 2586 | return struct { | ||
| 2587 | const Self = @This(); | ||
| 2588 | pub const Error = UnderlyingWriter.Error; | ||
| 2589 | pub const Writer = std.io.Writer(*Self, Error, write); | ||
| 2590 | |||
| 2591 | underlying_writer: UnderlyingWriter, | ||
| 2592 | |||
| 2593 | /// Offset into the source at which formatting has been disabled with | ||
| 2594 | /// a `zig fmt: off` comment. | ||
| 2595 | /// | ||
| 2596 | /// If non-null, the AutoIndentingStream will not write any bytes | ||
| 2597 | /// to the underlying writer. It will however continue to track the | ||
| 2598 | /// indentation level. | ||
| 2599 | disabled_offset: ?usize = null, | ||
| 2600 | |||
| 2601 | indent_count: usize = 0, | ||
| 2602 | indent_delta: usize, | ||
| 2603 | current_line_empty: bool = true, | ||
| 2604 | /// automatically popped when applied | ||
| 2605 | indent_one_shot_count: usize = 0, | ||
| 2606 | /// the most recently applied indent | ||
| 2607 | applied_indent: usize = 0, | ||
| 2608 | /// not used until the next line | ||
| 2609 | indent_next_line: usize = 0, | ||
| 2610 | |||
| 2611 | pub fn writer(self: *Self) Writer { | ||
| 2612 | return .{ .context = self }; | ||
| 2613 | } | ||
| 2614 | |||
| 2615 | pub fn write(self: *Self, bytes: []const u8) Error!usize { | ||
| 2616 | if (bytes.len == 0) | ||
| 2617 | return @as(usize, 0); | ||
| 2618 | |||
| 2619 | try self.applyIndent(); | ||
| 2620 | return self.writeNoIndent(bytes); | ||
| 2621 | } | ||
| 2622 | |||
| 2623 | // Change the indent delta without changing the final indentation level | ||
| 2624 | pub fn setIndentDelta(self: *Self, new_indent_delta: usize) void { | ||
| 2625 | if (self.indent_delta == new_indent_delta) { | ||
| 2626 | return; | ||
| 2627 | } else if (self.indent_delta > new_indent_delta) { | ||
| 2628 | assert(self.indent_delta % new_indent_delta == 0); | ||
| 2629 | self.indent_count = self.indent_count * (self.indent_delta / new_indent_delta); | ||
| 2630 | } else { | ||
| 2631 | // assert that the current indentation (in spaces) in a multiple of the new delta | ||
| 2632 | assert((self.indent_count * self.indent_delta) % new_indent_delta == 0); | ||
| 2633 | self.indent_count = self.indent_count / (new_indent_delta / self.indent_delta); | ||
| 2634 | } | ||
| 2635 | self.indent_delta = new_indent_delta; | ||
| 2636 | } | ||
| 2637 | |||
| 2638 | fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize { | ||
| 2639 | if (bytes.len == 0) | ||
| 2640 | return @as(usize, 0); | ||
| 2641 | |||
| 2642 | if (self.disabled_offset == null) try self.underlying_writer.writeAll(bytes); | ||
| 2643 | if (bytes[bytes.len - 1] == '\n') | ||
| 2644 | self.resetLine(); | ||
| 2645 | return bytes.len; | ||
| 2646 | } | ||
| 2647 | |||
| 2648 | pub fn insertNewline(self: *Self) Error!void { | ||
| 2649 | _ = try self.writeNoIndent("\n"); | ||
| 2650 | } | ||
| 2651 | |||
| 2652 | fn resetLine(self: *Self) void { | ||
| 2653 | self.current_line_empty = true; | ||
| 2654 | self.indent_next_line = 0; | ||
| 2655 | } | ||
| 2656 | |||
| 2657 | /// Insert a newline unless the current line is blank | ||
| 2658 | pub fn maybeInsertNewline(self: *Self) Error!void { | ||
| 2659 | if (!self.current_line_empty) | ||
| 2660 | try self.insertNewline(); | ||
| 2661 | } | ||
| 2662 | |||
| 2663 | /// Push default indentation | ||
| 2664 | /// Doesn't actually write any indentation. | ||
| 2665 | /// Just primes the stream to be able to write the correct indentation if it needs to. | ||
| 2666 | pub fn pushIndent(self: *Self) void { | ||
| 2667 | self.indent_count += 1; | ||
| 2668 | } | ||
| 2669 | |||
| 2670 | /// Push an indent that is automatically popped after being applied | ||
| 2671 | pub fn pushIndentOneShot(self: *Self) void { | ||
| 2672 | self.indent_one_shot_count += 1; | ||
| 2673 | self.pushIndent(); | ||
| 2674 | } | ||
| 2675 | |||
| 2676 | /// Turns all one-shot indents into regular indents | ||
| 2677 | /// Returns number of indents that must now be manually popped | ||
| 2678 | pub fn lockOneShotIndent(self: *Self) usize { | ||
| 2679 | var locked_count = self.indent_one_shot_count; | ||
| 2680 | self.indent_one_shot_count = 0; | ||
| 2681 | return locked_count; | ||
| 2682 | } | ||
| 2683 | |||
| 2684 | /// Push an indent that should not take effect until the next line | ||
| 2685 | pub fn pushIndentNextLine(self: *Self) void { | ||
| 2686 | self.indent_next_line += 1; | ||
| 2687 | self.pushIndent(); | ||
| 2688 | } | ||
| 2689 | |||
| 2690 | pub fn popIndent(self: *Self) void { | ||
| 2691 | assert(self.indent_count != 0); | ||
| 2692 | self.indent_count -= 1; | ||
| 2693 | |||
| 2694 | if (self.indent_next_line > 0) | ||
| 2695 | self.indent_next_line -= 1; | ||
| 2696 | } | ||
| 2697 | |||
| 2698 | /// Writes ' ' bytes if the current line is empty | ||
| 2699 | fn applyIndent(self: *Self) Error!void { | ||
| 2700 | const current_indent = self.currentIndent(); | ||
| 2701 | if (self.current_line_empty and current_indent > 0) { | ||
| 2702 | if (self.disabled_offset == null) { | ||
| 2703 | try self.underlying_writer.writeByteNTimes(' ', current_indent); | ||
| 2704 | } | ||
| 2705 | self.applied_indent = current_indent; | ||
| 2706 | } | ||
| 2707 | |||
| 2708 | self.indent_count -= self.indent_one_shot_count; | ||
| 2709 | self.indent_one_shot_count = 0; | ||
| 2710 | self.current_line_empty = false; | ||
| 2711 | } | ||
| 2712 | |||
| 2713 | /// Checks to see if the most recent indentation exceeds the currently pushed indents | ||
| 2714 | pub fn isLineOverIndented(self: *Self) bool { | ||
| 2715 | if (self.current_line_empty) return false; | ||
| 2716 | return self.applied_indent > self.currentIndent(); | ||
| 2717 | } | ||
| 2718 | |||
| 2719 | fn currentIndent(self: *Self) usize { | ||
| 2720 | var indent_current: usize = 0; | ||
| 2721 | if (self.indent_count > 0) { | ||
| 2722 | const indent_count = self.indent_count - self.indent_next_line; | ||
| 2723 | indent_current = indent_count * self.indent_delta; | ||
| 2724 | } | ||
| 2725 | return indent_current; | ||
| 2726 | } | ||
| 2727 | }; | ||
| 2728 | } |
lib/std/zig/tokenizer.zig+832-826| ... | @@ -7,7 +7,7 @@ const std = @import("../std.zig"); | ... | @@ -7,7 +7,7 @@ const std = @import("../std.zig"); |
| 7 | const mem = std.mem; | 7 | const mem = std.mem; |
| 8 | 8 | ||
| 9 | pub const Token = struct { | 9 | pub const Token = struct { |
| 10 | id: Id, | 10 | tag: Tag, |
| 11 | loc: Loc, | 11 | loc: Loc, |
| 12 | 12 | ||
| 13 | pub const Loc = struct { | 13 | pub const Loc = struct { |
| ... | @@ -15,315 +15,315 @@ pub const Token = struct { | ... | @@ -15,315 +15,315 @@ pub const Token = struct { |
| 15 | end: usize, | 15 | end: usize, |
| 16 | }; | 16 | }; |
| 17 | 17 | ||
| 18 | pub const keywords = std.ComptimeStringMap(Id, .{ | 18 | pub const keywords = std.ComptimeStringMap(Tag, .{ |
| 19 | .{ "align", .Keyword_align }, | 19 | .{ "align", .keyword_align }, |
| 20 | .{ "allowzero", .Keyword_allowzero }, | 20 | .{ "allowzero", .keyword_allowzero }, |
| 21 | .{ "and", .Keyword_and }, | 21 | .{ "and", .keyword_and }, |
| 22 | .{ "anyframe", .Keyword_anyframe }, | 22 | .{ "anyframe", .keyword_anyframe }, |
| 23 | .{ "anytype", .Keyword_anytype }, | 23 | .{ "anytype", .keyword_anytype }, |
| 24 | .{ "asm", .Keyword_asm }, | 24 | .{ "asm", .keyword_asm }, |
| 25 | .{ "async", .Keyword_async }, | 25 | .{ "async", .keyword_async }, |
| 26 | .{ "await", .Keyword_await }, | 26 | .{ "await", .keyword_await }, |
| 27 | .{ "break", .Keyword_break }, | 27 | .{ "break", .keyword_break }, |
| 28 | .{ "callconv", .Keyword_callconv }, | 28 | .{ "callconv", .keyword_callconv }, |
| 29 | .{ "catch", .Keyword_catch }, | 29 | .{ "catch", .keyword_catch }, |
| 30 | .{ "comptime", .Keyword_comptime }, | 30 | .{ "comptime", .keyword_comptime }, |
| 31 | .{ "const", .Keyword_const }, | 31 | .{ "const", .keyword_const }, |
| 32 | .{ "continue", .Keyword_continue }, | 32 | .{ "continue", .keyword_continue }, |
| 33 | .{ "defer", .Keyword_defer }, | 33 | .{ "defer", .keyword_defer }, |
| 34 | .{ "else", .Keyword_else }, | 34 | .{ "else", .keyword_else }, |
| 35 | .{ "enum", .Keyword_enum }, | 35 | .{ "enum", .keyword_enum }, |
| 36 | .{ "errdefer", .Keyword_errdefer }, | 36 | .{ "errdefer", .keyword_errdefer }, |
| 37 | .{ "error", .Keyword_error }, | 37 | .{ "error", .keyword_error }, |
| 38 | .{ "export", .Keyword_export }, | 38 | .{ "export", .keyword_export }, |
| 39 | .{ "extern", .Keyword_extern }, | 39 | .{ "extern", .keyword_extern }, |
| 40 | .{ "false", .Keyword_false }, | 40 | .{ "false", .keyword_false }, |
| 41 | .{ "fn", .Keyword_fn }, | 41 | .{ "fn", .keyword_fn }, |
| 42 | .{ "for", .Keyword_for }, | 42 | .{ "for", .keyword_for }, |
| 43 | .{ "if", .Keyword_if }, | 43 | .{ "if", .keyword_if }, |
| 44 | .{ "inline", .Keyword_inline }, | 44 | .{ "inline", .keyword_inline }, |
| 45 | .{ "noalias", .Keyword_noalias }, | 45 | .{ "noalias", .keyword_noalias }, |
| 46 | .{ "noasync", .Keyword_nosuspend }, // TODO: remove this | 46 | .{ "noinline", .keyword_noinline }, |
| 47 | .{ "noinline", .Keyword_noinline }, | 47 | .{ "nosuspend", .keyword_nosuspend }, |
| 48 | .{ "nosuspend", .Keyword_nosuspend }, | 48 | .{ "null", .keyword_null }, |
| 49 | .{ "null", .Keyword_null }, | 49 | .{ "opaque", .keyword_opaque }, |
| 50 | .{ "opaque", .Keyword_opaque }, | 50 | .{ "or", .keyword_or }, |
| 51 | .{ "or", .Keyword_or }, | 51 | .{ "orelse", .keyword_orelse }, |
| 52 | .{ "orelse", .Keyword_orelse }, | 52 | .{ "packed", .keyword_packed }, |
| 53 | .{ "packed", .Keyword_packed }, | 53 | .{ "pub", .keyword_pub }, |
| 54 | .{ "pub", .Keyword_pub }, | 54 | .{ "resume", .keyword_resume }, |
| 55 | .{ "resume", .Keyword_resume }, | 55 | .{ "return", .keyword_return }, |
| 56 | .{ "return", .Keyword_return }, | 56 | .{ "linksection", .keyword_linksection }, |
| 57 | .{ "linksection", .Keyword_linksection }, | 57 | .{ "struct", .keyword_struct }, |
| 58 | .{ "struct", .Keyword_struct }, | 58 | .{ "suspend", .keyword_suspend }, |
| 59 | .{ "suspend", .Keyword_suspend }, | 59 | .{ "switch", .keyword_switch }, |
| 60 | .{ "switch", .Keyword_switch }, | 60 | .{ "test", .keyword_test }, |
| 61 | .{ "test", .Keyword_test }, | 61 | .{ "threadlocal", .keyword_threadlocal }, |
| 62 | .{ "threadlocal", .Keyword_threadlocal }, | 62 | .{ "true", .keyword_true }, |
| 63 | .{ "true", .Keyword_true }, | 63 | .{ "try", .keyword_try }, |
| 64 | .{ "try", .Keyword_try }, | 64 | .{ "undefined", .keyword_undefined }, |
| 65 | .{ "undefined", .Keyword_undefined }, | 65 | .{ "union", .keyword_union }, |
| 66 | .{ "union", .Keyword_union }, | 66 | .{ "unreachable", .keyword_unreachable }, |
| 67 | .{ "unreachable", .Keyword_unreachable }, | 67 | .{ "usingnamespace", .keyword_usingnamespace }, |
| 68 | .{ "usingnamespace", .Keyword_usingnamespace }, | 68 | .{ "var", .keyword_var }, |
| 69 | .{ "var", .Keyword_var }, | 69 | .{ "volatile", .keyword_volatile }, |
| 70 | .{ "volatile", .Keyword_volatile }, | 70 | .{ "while", .keyword_while }, |
| 71 | .{ "while", .Keyword_while }, | ||
| 72 | }); | 71 | }); |
| 73 | 72 | ||
| 74 | pub fn getKeyword(bytes: []const u8) ?Id { | 73 | pub fn getKeyword(bytes: []const u8) ?Tag { |
| 75 | return keywords.get(bytes); | 74 | return keywords.get(bytes); |
| 76 | } | 75 | } |
| 77 | 76 | ||
| 78 | pub const Id = enum { | 77 | pub const Tag = enum { |
| 79 | Invalid, | 78 | invalid, |
| 80 | Invalid_ampersands, | 79 | invalid_ampersands, |
| 81 | Invalid_periodasterisks, | 80 | invalid_periodasterisks, |
| 82 | Identifier, | 81 | identifier, |
| 83 | StringLiteral, | 82 | string_literal, |
| 84 | MultilineStringLiteralLine, | 83 | multiline_string_literal_line, |
| 85 | CharLiteral, | 84 | char_literal, |
| 86 | Eof, | 85 | eof, |
| 87 | Builtin, | 86 | builtin, |
| 88 | Bang, | 87 | bang, |
| 89 | Pipe, | 88 | pipe, |
| 90 | PipePipe, | 89 | pipe_pipe, |
| 91 | PipeEqual, | 90 | pipe_equal, |
| 92 | Equal, | 91 | equal, |
| 93 | EqualEqual, | 92 | equal_equal, |
| 94 | EqualAngleBracketRight, | 93 | equal_angle_bracket_right, |
| 95 | BangEqual, | 94 | bang_equal, |
| 96 | LParen, | 95 | l_paren, |
| 97 | RParen, | 96 | r_paren, |
| 98 | Semicolon, | 97 | semicolon, |
| 99 | Percent, | 98 | percent, |
| 100 | PercentEqual, | 99 | percent_equal, |
| 101 | LBrace, | 100 | l_brace, |
| 102 | RBrace, | 101 | r_brace, |
| 103 | LBracket, | 102 | l_bracket, |
| 104 | RBracket, | 103 | r_bracket, |
| 105 | Period, | 104 | period, |
| 106 | PeriodAsterisk, | 105 | period_asterisk, |
| 107 | Ellipsis2, | 106 | ellipsis2, |
| 108 | Ellipsis3, | 107 | ellipsis3, |
| 109 | Caret, | 108 | caret, |
| 110 | CaretEqual, | 109 | caret_equal, |
| 111 | Plus, | 110 | plus, |
| 112 | PlusPlus, | 111 | plus_plus, |
| 113 | PlusEqual, | 112 | plus_equal, |
| 114 | PlusPercent, | 113 | plus_percent, |
| 115 | PlusPercentEqual, | 114 | plus_percent_equal, |
| 116 | Minus, | 115 | minus, |
| 117 | MinusEqual, | 116 | minus_equal, |
| 118 | MinusPercent, | 117 | minus_percent, |
| 119 | MinusPercentEqual, | 118 | minus_percent_equal, |
| 120 | Asterisk, | 119 | asterisk, |
| 121 | AsteriskEqual, | 120 | asterisk_equal, |
| 122 | AsteriskAsterisk, | 121 | asterisk_asterisk, |
| 123 | AsteriskPercent, | 122 | asterisk_percent, |
| 124 | AsteriskPercentEqual, | 123 | asterisk_percent_equal, |
| 125 | Arrow, | 124 | arrow, |
| 126 | Colon, | 125 | colon, |
| 127 | Slash, | 126 | slash, |
| 128 | SlashEqual, | 127 | slash_equal, |
| 129 | Comma, | 128 | comma, |
| 130 | Ampersand, | 129 | ampersand, |
| 131 | AmpersandEqual, | 130 | ampersand_equal, |
| 132 | QuestionMark, | 131 | question_mark, |
| 133 | AngleBracketLeft, | 132 | angle_bracket_left, |
| 134 | AngleBracketLeftEqual, | 133 | angle_bracket_left_equal, |
| 135 | AngleBracketAngleBracketLeft, | 134 | angle_bracket_angle_bracket_left, |
| 136 | AngleBracketAngleBracketLeftEqual, | 135 | angle_bracket_angle_bracket_left_equal, |
| 137 | AngleBracketRight, | 136 | angle_bracket_right, |
| 138 | AngleBracketRightEqual, | 137 | angle_bracket_right_equal, |
| 139 | AngleBracketAngleBracketRight, | 138 | angle_bracket_angle_bracket_right, |
| 140 | AngleBracketAngleBracketRightEqual, | 139 | angle_bracket_angle_bracket_right_equal, |
| 141 | Tilde, | 140 | tilde, |
| 142 | IntegerLiteral, | 141 | integer_literal, |
| 143 | FloatLiteral, | 142 | float_literal, |
| 144 | LineComment, | 143 | doc_comment, |
| 145 | DocComment, | 144 | container_doc_comment, |
| 146 | ContainerDocComment, | 145 | keyword_align, |
| 147 | ShebangLine, | 146 | keyword_allowzero, |
| 148 | Keyword_align, | 147 | keyword_and, |
| 149 | Keyword_allowzero, | 148 | keyword_anyframe, |
| 150 | Keyword_and, | 149 | keyword_anytype, |
| 151 | Keyword_anyframe, | 150 | keyword_asm, |
| 152 | Keyword_anytype, | 151 | keyword_async, |
| 153 | Keyword_asm, | 152 | keyword_await, |
| 154 | Keyword_async, | 153 | keyword_break, |
| 155 | Keyword_await, | 154 | keyword_callconv, |
| 156 | Keyword_break, | 155 | keyword_catch, |
| 157 | Keyword_callconv, | 156 | keyword_comptime, |
| 158 | Keyword_catch, | 157 | keyword_const, |
| 159 | Keyword_comptime, | 158 | keyword_continue, |
| 160 | Keyword_const, | 159 | keyword_defer, |
| 161 | Keyword_continue, | 160 | keyword_else, |
| 162 | Keyword_defer, | 161 | keyword_enum, |
| 163 | Keyword_else, | 162 | keyword_errdefer, |
| 164 | Keyword_enum, | 163 | keyword_error, |
| 165 | Keyword_errdefer, | 164 | keyword_export, |
| 166 | Keyword_error, | 165 | keyword_extern, |
| 167 | Keyword_export, | 166 | keyword_false, |
| 168 | Keyword_extern, | 167 | keyword_fn, |
| 169 | Keyword_false, | 168 | keyword_for, |
| 170 | Keyword_fn, | 169 | keyword_if, |
| 171 | Keyword_for, | 170 | keyword_inline, |
| 172 | Keyword_if, | 171 | keyword_noalias, |
| 173 | Keyword_inline, | 172 | keyword_noinline, |
| 174 | Keyword_noalias, | 173 | keyword_nosuspend, |
| 175 | Keyword_noinline, | 174 | keyword_null, |
| 176 | Keyword_nosuspend, | 175 | keyword_opaque, |
| 177 | Keyword_null, | 176 | keyword_or, |
| 178 | Keyword_opaque, | 177 | keyword_orelse, |
| 179 | Keyword_or, | 178 | keyword_packed, |
| 180 | Keyword_orelse, | 179 | keyword_pub, |
| 181 | Keyword_packed, | 180 | keyword_resume, |
| 182 | Keyword_pub, | 181 | keyword_return, |
| 183 | Keyword_resume, | 182 | keyword_linksection, |
| 184 | Keyword_return, | 183 | keyword_struct, |
| 185 | Keyword_linksection, | 184 | keyword_suspend, |
| 186 | Keyword_struct, | 185 | keyword_switch, |
| 187 | Keyword_suspend, | 186 | keyword_test, |
| 188 | Keyword_switch, | 187 | keyword_threadlocal, |
| 189 | Keyword_test, | 188 | keyword_true, |
| 190 | Keyword_threadlocal, | 189 | keyword_try, |
| 191 | Keyword_true, | 190 | keyword_undefined, |
| 192 | Keyword_try, | 191 | keyword_union, |
| 193 | Keyword_undefined, | 192 | keyword_unreachable, |
| 194 | Keyword_union, | 193 | keyword_usingnamespace, |
| 195 | Keyword_unreachable, | 194 | keyword_var, |
| 196 | Keyword_usingnamespace, | 195 | keyword_volatile, |
| 197 | Keyword_var, | 196 | keyword_while, |
| 198 | Keyword_volatile, | 197 | |
| 199 | Keyword_while, | 198 | pub fn lexeme(tag: Tag) ?[]const u8 { |
| 200 | 199 | return switch (tag) { | |
| 201 | pub fn symbol(id: Id) []const u8 { | 200 | .invalid, |
| 202 | return switch (id) { | 201 | .identifier, |
| 203 | .Invalid => "Invalid", | 202 | .string_literal, |
| 204 | .Invalid_ampersands => "&&", | 203 | .multiline_string_literal_line, |
| 205 | .Invalid_periodasterisks => ".**", | 204 | .char_literal, |
| 206 | .Identifier => "Identifier", | 205 | .eof, |
| 207 | .StringLiteral => "StringLiteral", | 206 | .builtin, |
| 208 | .MultilineStringLiteralLine => "MultilineStringLiteralLine", | 207 | .integer_literal, |
| 209 | .CharLiteral => "CharLiteral", | 208 | .float_literal, |
| 210 | .Eof => "Eof", | 209 | .doc_comment, |
| 211 | .Builtin => "Builtin", | 210 | .container_doc_comment, |
| 212 | .IntegerLiteral => "IntegerLiteral", | 211 | => null, |
| 213 | .FloatLiteral => "FloatLiteral", | 212 | |
| 214 | .LineComment => "LineComment", | 213 | .invalid_ampersands => "&&", |
| 215 | .DocComment => "DocComment", | 214 | .invalid_periodasterisks => ".**", |
| 216 | .ContainerDocComment => "ContainerDocComment", | 215 | .bang => "!", |
| 217 | .ShebangLine => "ShebangLine", | 216 | .pipe => "|", |
| 218 | 217 | .pipe_pipe => "||", | |
| 219 | .Bang => "!", | 218 | .pipe_equal => "|=", |
| 220 | .Pipe => "|", | 219 | .equal => "=", |
| 221 | .PipePipe => "||", | 220 | .equal_equal => "==", |
| 222 | .PipeEqual => "|=", | 221 | .equal_angle_bracket_right => "=>", |
| 223 | .Equal => "=", | 222 | .bang_equal => "!=", |
| 224 | .EqualEqual => "==", | 223 | .l_paren => "(", |
| 225 | .EqualAngleBracketRight => "=>", | 224 | .r_paren => ")", |
| 226 | .BangEqual => "!=", | 225 | .semicolon => ";", |
| 227 | .LParen => "(", | 226 | .percent => "%", |
| 228 | .RParen => ")", | 227 | .percent_equal => "%=", |
| 229 | .Semicolon => ";", | 228 | .l_brace => "{", |
| 230 | .Percent => "%", | 229 | .r_brace => "}", |
| 231 | .PercentEqual => "%=", | 230 | .l_bracket => "[", |
| 232 | .LBrace => "{", | 231 | .r_bracket => "]", |
| 233 | .RBrace => "}", | 232 | .period => ".", |
| 234 | .LBracket => "[", | 233 | .period_asterisk => ".*", |
| 235 | .RBracket => "]", | 234 | .ellipsis2 => "..", |
| 236 | .Period => ".", | 235 | .ellipsis3 => "...", |
| 237 | .PeriodAsterisk => ".*", | 236 | .caret => "^", |
| 238 | .Ellipsis2 => "..", | 237 | .caret_equal => "^=", |
| 239 | .Ellipsis3 => "...", | 238 | .plus => "+", |
| 240 | .Caret => "^", | 239 | .plus_plus => "++", |
| 241 | .CaretEqual => "^=", | 240 | .plus_equal => "+=", |
| 242 | .Plus => "+", | 241 | .plus_percent => "+%", |
| 243 | .PlusPlus => "++", | 242 | .plus_percent_equal => "+%=", |
| 244 | .PlusEqual => "+=", | 243 | .minus => "-", |
| 245 | .PlusPercent => "+%", | 244 | .minus_equal => "-=", |
| 246 | .PlusPercentEqual => "+%=", | 245 | .minus_percent => "-%", |
| 247 | .Minus => "-", | 246 | .minus_percent_equal => "-%=", |
| 248 | .MinusEqual => "-=", | 247 | .asterisk => "*", |
| 249 | .MinusPercent => "-%", | 248 | .asterisk_equal => "*=", |
| 250 | .MinusPercentEqual => "-%=", | 249 | .asterisk_asterisk => "**", |
| 251 | .Asterisk => "*", | 250 | .asterisk_percent => "*%", |
| 252 | .AsteriskEqual => "*=", | 251 | .asterisk_percent_equal => "*%=", |
| 253 | .AsteriskAsterisk => "**", | 252 | .arrow => "->", |
| 254 | .AsteriskPercent => "*%", | 253 | .colon => ":", |
| 255 | .AsteriskPercentEqual => "*%=", | 254 | .slash => "/", |
| 256 | .Arrow => "->", | 255 | .slash_equal => "/=", |
| 257 | .Colon => ":", | 256 | .comma => ",", |
| 258 | .Slash => "/", | 257 | .ampersand => "&", |
| 259 | .SlashEqual => "/=", | 258 | .ampersand_equal => "&=", |
| 260 | .Comma => ",", | 259 | .question_mark => "?", |
| 261 | .Ampersand => "&", | 260 | .angle_bracket_left => "<", |
| 262 | .AmpersandEqual => "&=", | 261 | .angle_bracket_left_equal => "<=", |
| 263 | .QuestionMark => "?", | 262 | .angle_bracket_angle_bracket_left => "<<", |
| 264 | .AngleBracketLeft => "<", | 263 | .angle_bracket_angle_bracket_left_equal => "<<=", |
| 265 | .AngleBracketLeftEqual => "<=", | 264 | .angle_bracket_right => ">", |
| 266 | .AngleBracketAngleBracketLeft => "<<", | 265 | .angle_bracket_right_equal => ">=", |
| 267 | .AngleBracketAngleBracketLeftEqual => "<<=", | 266 | .angle_bracket_angle_bracket_right => ">>", |
| 268 | .AngleBracketRight => ">", | 267 | .angle_bracket_angle_bracket_right_equal => ">>=", |
| 269 | .AngleBracketRightEqual => ">=", | 268 | .tilde => "~", |
| 270 | .AngleBracketAngleBracketRight => ">>", | 269 | .keyword_align => "align", |
| 271 | .AngleBracketAngleBracketRightEqual => ">>=", | 270 | .keyword_allowzero => "allowzero", |
| 272 | .Tilde => "~", | 271 | .keyword_and => "and", |
| 273 | .Keyword_align => "align", | 272 | .keyword_anyframe => "anyframe", |
| 274 | .Keyword_allowzero => "allowzero", | 273 | .keyword_anytype => "anytype", |
| 275 | .Keyword_and => "and", | 274 | .keyword_asm => "asm", |
| 276 | .Keyword_anyframe => "anyframe", | 275 | .keyword_async => "async", |
| 277 | .Keyword_anytype => "anytype", | 276 | .keyword_await => "await", |
| 278 | .Keyword_asm => "asm", | 277 | .keyword_break => "break", |
| 279 | .Keyword_async => "async", | 278 | .keyword_callconv => "callconv", |
| 280 | .Keyword_await => "await", | 279 | .keyword_catch => "catch", |
| 281 | .Keyword_break => "break", | 280 | .keyword_comptime => "comptime", |
| 282 | .Keyword_callconv => "callconv", | 281 | .keyword_const => "const", |
| 283 | .Keyword_catch => "catch", | 282 | .keyword_continue => "continue", |
| 284 | .Keyword_comptime => "comptime", | 283 | .keyword_defer => "defer", |
| 285 | .Keyword_const => "const", | 284 | .keyword_else => "else", |
| 286 | .Keyword_continue => "continue", | 285 | .keyword_enum => "enum", |
| 287 | .Keyword_defer => "defer", | 286 | .keyword_errdefer => "errdefer", |
| 288 | .Keyword_else => "else", | 287 | .keyword_error => "error", |
| 289 | .Keyword_enum => "enum", | 288 | .keyword_export => "export", |
| 290 | .Keyword_errdefer => "errdefer", | 289 | .keyword_extern => "extern", |
| 291 | .Keyword_error => "error", | 290 | .keyword_false => "false", |
| 292 | .Keyword_export => "export", | 291 | .keyword_fn => "fn", |
| 293 | .Keyword_extern => "extern", | 292 | .keyword_for => "for", |
| 294 | .Keyword_false => "false", | 293 | .keyword_if => "if", |
| 295 | .Keyword_fn => "fn", | 294 | .keyword_inline => "inline", |
| 296 | .Keyword_for => "for", | 295 | .keyword_noalias => "noalias", |
| 297 | .Keyword_if => "if", | 296 | .keyword_noinline => "noinline", |
| 298 | .Keyword_inline => "inline", | 297 | .keyword_nosuspend => "nosuspend", |
| 299 | .Keyword_noalias => "noalias", | 298 | .keyword_null => "null", |
| 300 | .Keyword_noinline => "noinline", | 299 | .keyword_opaque => "opaque", |
| 301 | .Keyword_nosuspend => "nosuspend", | 300 | .keyword_or => "or", |
| 302 | .Keyword_null => "null", | 301 | .keyword_orelse => "orelse", |
| 303 | .Keyword_opaque => "opaque", | 302 | .keyword_packed => "packed", |
| 304 | .Keyword_or => "or", | 303 | .keyword_pub => "pub", |
| 305 | .Keyword_orelse => "orelse", | 304 | .keyword_resume => "resume", |
| 306 | .Keyword_packed => "packed", | 305 | .keyword_return => "return", |
| 307 | .Keyword_pub => "pub", | 306 | .keyword_linksection => "linksection", |
| 308 | .Keyword_resume => "resume", | 307 | .keyword_struct => "struct", |
| 309 | .Keyword_return => "return", | 308 | .keyword_suspend => "suspend", |
| 310 | .Keyword_linksection => "linksection", | 309 | .keyword_switch => "switch", |
| 311 | .Keyword_struct => "struct", | 310 | .keyword_test => "test", |
| 312 | .Keyword_suspend => "suspend", | 311 | .keyword_threadlocal => "threadlocal", |
| 313 | .Keyword_switch => "switch", | 312 | .keyword_true => "true", |
| 314 | .Keyword_test => "test", | 313 | .keyword_try => "try", |
| 315 | .Keyword_threadlocal => "threadlocal", | 314 | .keyword_undefined => "undefined", |
| 316 | .Keyword_true => "true", | 315 | .keyword_union => "union", |
| 317 | .Keyword_try => "try", | 316 | .keyword_unreachable => "unreachable", |
| 318 | .Keyword_undefined => "undefined", | 317 | .keyword_usingnamespace => "usingnamespace", |
| 319 | .Keyword_union => "union", | 318 | .keyword_var => "var", |
| 320 | .Keyword_unreachable => "unreachable", | 319 | .keyword_volatile => "volatile", |
| 321 | .Keyword_usingnamespace => "usingnamespace", | 320 | .keyword_while => "while", |
| 322 | .Keyword_var => "var", | ||
| 323 | .Keyword_volatile => "volatile", | ||
| 324 | .Keyword_while => "while", | ||
| 325 | }; | 321 | }; |
| 326 | } | 322 | } |
| 323 | |||
| 324 | pub fn symbol(tag: Tag) []const u8 { | ||
| 325 | return tag.lexeme() orelse @tagName(tag); | ||
| 326 | } | ||
| 327 | }; | 327 | }; |
| 328 | }; | 328 | }; |
| 329 | 329 | ||
| ... | @@ -334,7 +334,7 @@ pub const Tokenizer = struct { | ... | @@ -334,7 +334,7 @@ pub const Tokenizer = struct { |
| 334 | 334 | ||
| 335 | /// For debugging purposes | 335 | /// For debugging purposes |
| 336 | pub fn dump(self: *Tokenizer, token: *const Token) void { | 336 | pub fn dump(self: *Tokenizer, token: *const Token) void { |
| 337 | std.debug.warn("{s} \"{s}\"\n", .{ @tagName(token.id), self.buffer[token.start..token.end] }); | 337 | std.debug.warn("{s} \"{s}\"\n", .{ @tagName(token.tag), self.buffer[token.start..token.end] }); |
| 338 | } | 338 | } |
| 339 | 339 | ||
| 340 | pub fn init(buffer: []const u8) Tokenizer { | 340 | pub fn init(buffer: []const u8) Tokenizer { |
| ... | @@ -421,7 +421,7 @@ pub const Tokenizer = struct { | ... | @@ -421,7 +421,7 @@ pub const Tokenizer = struct { |
| 421 | const start_index = self.index; | 421 | const start_index = self.index; |
| 422 | var state: State = .start; | 422 | var state: State = .start; |
| 423 | var result = Token{ | 423 | var result = Token{ |
| 424 | .id = .Eof, | 424 | .tag = .eof, |
| 425 | .loc = .{ | 425 | .loc = .{ |
| 426 | .start = self.index, | 426 | .start = self.index, |
| 427 | .end = undefined, | 427 | .end = undefined, |
| ... | @@ -438,14 +438,14 @@ pub const Tokenizer = struct { | ... | @@ -438,14 +438,14 @@ pub const Tokenizer = struct { |
| 438 | }, | 438 | }, |
| 439 | '"' => { | 439 | '"' => { |
| 440 | state = .string_literal; | 440 | state = .string_literal; |
| 441 | result.id = .StringLiteral; | 441 | result.tag = .string_literal; |
| 442 | }, | 442 | }, |
| 443 | '\'' => { | 443 | '\'' => { |
| 444 | state = .char_literal; | 444 | state = .char_literal; |
| 445 | }, | 445 | }, |
| 446 | 'a'...'z', 'A'...'Z', '_' => { | 446 | 'a'...'z', 'A'...'Z', '_' => { |
| 447 | state = .identifier; | 447 | state = .identifier; |
| 448 | result.id = .Identifier; | 448 | result.tag = .identifier; |
| 449 | }, | 449 | }, |
| 450 | '@' => { | 450 | '@' => { |
| 451 | state = .saw_at_sign; | 451 | state = .saw_at_sign; |
| ... | @@ -460,42 +460,42 @@ pub const Tokenizer = struct { | ... | @@ -460,42 +460,42 @@ pub const Tokenizer = struct { |
| 460 | state = .pipe; | 460 | state = .pipe; |
| 461 | }, | 461 | }, |
| 462 | '(' => { | 462 | '(' => { |
| 463 | result.id = .LParen; | 463 | result.tag = .l_paren; |
| 464 | self.index += 1; | 464 | self.index += 1; |
| 465 | break; | 465 | break; |
| 466 | }, | 466 | }, |
| 467 | ')' => { | 467 | ')' => { |
| 468 | result.id = .RParen; | 468 | result.tag = .r_paren; |
| 469 | self.index += 1; | 469 | self.index += 1; |
| 470 | break; | 470 | break; |
| 471 | }, | 471 | }, |
| 472 | '[' => { | 472 | '[' => { |
| 473 | result.id = .LBracket; | 473 | result.tag = .l_bracket; |
| 474 | self.index += 1; | 474 | self.index += 1; |
| 475 | break; | 475 | break; |
| 476 | }, | 476 | }, |
| 477 | ']' => { | 477 | ']' => { |
| 478 | result.id = .RBracket; | 478 | result.tag = .r_bracket; |
| 479 | self.index += 1; | 479 | self.index += 1; |
| 480 | break; | 480 | break; |
| 481 | }, | 481 | }, |
| 482 | ';' => { | 482 | ';' => { |
| 483 | result.id = .Semicolon; | 483 | result.tag = .semicolon; |
| 484 | self.index += 1; | 484 | self.index += 1; |
| 485 | break; | 485 | break; |
| 486 | }, | 486 | }, |
| 487 | ',' => { | 487 | ',' => { |
| 488 | result.id = .Comma; | 488 | result.tag = .comma; |
| 489 | self.index += 1; | 489 | self.index += 1; |
| 490 | break; | 490 | break; |
| 491 | }, | 491 | }, |
| 492 | '?' => { | 492 | '?' => { |
| 493 | result.id = .QuestionMark; | 493 | result.tag = .question_mark; |
| 494 | self.index += 1; | 494 | self.index += 1; |
| 495 | break; | 495 | break; |
| 496 | }, | 496 | }, |
| 497 | ':' => { | 497 | ':' => { |
| 498 | result.id = .Colon; | 498 | result.tag = .colon; |
| 499 | self.index += 1; | 499 | self.index += 1; |
| 500 | break; | 500 | break; |
| 501 | }, | 501 | }, |
| ... | @@ -519,20 +519,20 @@ pub const Tokenizer = struct { | ... | @@ -519,20 +519,20 @@ pub const Tokenizer = struct { |
| 519 | }, | 519 | }, |
| 520 | '\\' => { | 520 | '\\' => { |
| 521 | state = .backslash; | 521 | state = .backslash; |
| 522 | result.id = .MultilineStringLiteralLine; | 522 | result.tag = .multiline_string_literal_line; |
| 523 | }, | 523 | }, |
| 524 | '{' => { | 524 | '{' => { |
| 525 | result.id = .LBrace; | 525 | result.tag = .l_brace; |
| 526 | self.index += 1; | 526 | self.index += 1; |
| 527 | break; | 527 | break; |
| 528 | }, | 528 | }, |
| 529 | '}' => { | 529 | '}' => { |
| 530 | result.id = .RBrace; | 530 | result.tag = .r_brace; |
| 531 | self.index += 1; | 531 | self.index += 1; |
| 532 | break; | 532 | break; |
| 533 | }, | 533 | }, |
| 534 | '~' => { | 534 | '~' => { |
| 535 | result.id = .Tilde; | 535 | result.tag = .tilde; |
| 536 | self.index += 1; | 536 | self.index += 1; |
| 537 | break; | 537 | break; |
| 538 | }, | 538 | }, |
| ... | @@ -550,14 +550,14 @@ pub const Tokenizer = struct { | ... | @@ -550,14 +550,14 @@ pub const Tokenizer = struct { |
| 550 | }, | 550 | }, |
| 551 | '0' => { | 551 | '0' => { |
| 552 | state = .zero; | 552 | state = .zero; |
| 553 | result.id = .IntegerLiteral; | 553 | result.tag = .integer_literal; |
| 554 | }, | 554 | }, |
| 555 | '1'...'9' => { | 555 | '1'...'9' => { |
| 556 | state = .int_literal_dec; | 556 | state = .int_literal_dec; |
| 557 | result.id = .IntegerLiteral; | 557 | result.tag = .integer_literal; |
| 558 | }, | 558 | }, |
| 559 | else => { | 559 | else => { |
| 560 | result.id = .Invalid; | 560 | result.tag = .invalid; |
| 561 | self.index += 1; | 561 | self.index += 1; |
| 562 | break; | 562 | break; |
| 563 | }, | 563 | }, |
| ... | @@ -565,42 +565,42 @@ pub const Tokenizer = struct { | ... | @@ -565,42 +565,42 @@ pub const Tokenizer = struct { |
| 565 | 565 | ||
| 566 | .saw_at_sign => switch (c) { | 566 | .saw_at_sign => switch (c) { |
| 567 | '"' => { | 567 | '"' => { |
| 568 | result.id = .Identifier; | 568 | result.tag = .identifier; |
| 569 | state = .string_literal; | 569 | state = .string_literal; |
| 570 | }, | 570 | }, |
| 571 | else => { | 571 | else => { |
| 572 | // reinterpret as a builtin | 572 | // reinterpret as a builtin |
| 573 | self.index -= 1; | 573 | self.index -= 1; |
| 574 | state = .builtin; | 574 | state = .builtin; |
| 575 | result.id = .Builtin; | 575 | result.tag = .builtin; |
| 576 | }, | 576 | }, |
| 577 | }, | 577 | }, |
| 578 | 578 | ||
| 579 | .ampersand => switch (c) { | 579 | .ampersand => switch (c) { |
| 580 | '&' => { | 580 | '&' => { |
| 581 | result.id = .Invalid_ampersands; | 581 | result.tag = .invalid_ampersands; |
| 582 | self.index += 1; | 582 | self.index += 1; |
| 583 | break; | 583 | break; |
| 584 | }, | 584 | }, |
| 585 | '=' => { | 585 | '=' => { |
| 586 | result.id = .AmpersandEqual; | 586 | result.tag = .ampersand_equal; |
| 587 | self.index += 1; | 587 | self.index += 1; |
| 588 | break; | 588 | break; |
| 589 | }, | 589 | }, |
| 590 | else => { | 590 | else => { |
| 591 | result.id = .Ampersand; | 591 | result.tag = .ampersand; |
| 592 | break; | 592 | break; |
| 593 | }, | 593 | }, |
| 594 | }, | 594 | }, |
| 595 | 595 | ||
| 596 | .asterisk => switch (c) { | 596 | .asterisk => switch (c) { |
| 597 | '=' => { | 597 | '=' => { |
| 598 | result.id = .AsteriskEqual; | 598 | result.tag = .asterisk_equal; |
| 599 | self.index += 1; | 599 | self.index += 1; |
| 600 | break; | 600 | break; |
| 601 | }, | 601 | }, |
| 602 | '*' => { | 602 | '*' => { |
| 603 | result.id = .AsteriskAsterisk; | 603 | result.tag = .asterisk_asterisk; |
| 604 | self.index += 1; | 604 | self.index += 1; |
| 605 | break; | 605 | break; |
| 606 | }, | 606 | }, |
| ... | @@ -608,43 +608,43 @@ pub const Tokenizer = struct { | ... | @@ -608,43 +608,43 @@ pub const Tokenizer = struct { |
| 608 | state = .asterisk_percent; | 608 | state = .asterisk_percent; |
| 609 | }, | 609 | }, |
| 610 | else => { | 610 | else => { |
| 611 | result.id = .Asterisk; | 611 | result.tag = .asterisk; |
| 612 | break; | 612 | break; |
| 613 | }, | 613 | }, |
| 614 | }, | 614 | }, |
| 615 | 615 | ||
| 616 | .asterisk_percent => switch (c) { | 616 | .asterisk_percent => switch (c) { |
| 617 | '=' => { | 617 | '=' => { |
| 618 | result.id = .AsteriskPercentEqual; | 618 | result.tag = .asterisk_percent_equal; |
| 619 | self.index += 1; | 619 | self.index += 1; |
| 620 | break; | 620 | break; |
| 621 | }, | 621 | }, |
| 622 | else => { | 622 | else => { |
| 623 | result.id = .AsteriskPercent; | 623 | result.tag = .asterisk_percent; |
| 624 | break; | 624 | break; |
| 625 | }, | 625 | }, |
| 626 | }, | 626 | }, |
| 627 | 627 | ||
| 628 | .percent => switch (c) { | 628 | .percent => switch (c) { |
| 629 | '=' => { | 629 | '=' => { |
| 630 | result.id = .PercentEqual; | 630 | result.tag = .percent_equal; |
| 631 | self.index += 1; | 631 | self.index += 1; |
| 632 | break; | 632 | break; |
| 633 | }, | 633 | }, |
| 634 | else => { | 634 | else => { |
| 635 | result.id = .Percent; | 635 | result.tag = .percent; |
| 636 | break; | 636 | break; |
| 637 | }, | 637 | }, |
| 638 | }, | 638 | }, |
| 639 | 639 | ||
| 640 | .plus => switch (c) { | 640 | .plus => switch (c) { |
| 641 | '=' => { | 641 | '=' => { |
| 642 | result.id = .PlusEqual; | 642 | result.tag = .plus_equal; |
| 643 | self.index += 1; | 643 | self.index += 1; |
| 644 | break; | 644 | break; |
| 645 | }, | 645 | }, |
| 646 | '+' => { | 646 | '+' => { |
| 647 | result.id = .PlusPlus; | 647 | result.tag = .plus_plus; |
| 648 | self.index += 1; | 648 | self.index += 1; |
| 649 | break; | 649 | break; |
| 650 | }, | 650 | }, |
| ... | @@ -652,31 +652,31 @@ pub const Tokenizer = struct { | ... | @@ -652,31 +652,31 @@ pub const Tokenizer = struct { |
| 652 | state = .plus_percent; | 652 | state = .plus_percent; |
| 653 | }, | 653 | }, |
| 654 | else => { | 654 | else => { |
| 655 | result.id = .Plus; | 655 | result.tag = .plus; |
| 656 | break; | 656 | break; |
| 657 | }, | 657 | }, |
| 658 | }, | 658 | }, |
| 659 | 659 | ||
| 660 | .plus_percent => switch (c) { | 660 | .plus_percent => switch (c) { |
| 661 | '=' => { | 661 | '=' => { |
| 662 | result.id = .PlusPercentEqual; | 662 | result.tag = .plus_percent_equal; |
| 663 | self.index += 1; | 663 | self.index += 1; |
| 664 | break; | 664 | break; |
| 665 | }, | 665 | }, |
| 666 | else => { | 666 | else => { |
| 667 | result.id = .PlusPercent; | 667 | result.tag = .plus_percent; |
| 668 | break; | 668 | break; |
| 669 | }, | 669 | }, |
| 670 | }, | 670 | }, |
| 671 | 671 | ||
| 672 | .caret => switch (c) { | 672 | .caret => switch (c) { |
| 673 | '=' => { | 673 | '=' => { |
| 674 | result.id = .CaretEqual; | 674 | result.tag = .caret_equal; |
| 675 | self.index += 1; | 675 | self.index += 1; |
| 676 | break; | 676 | break; |
| 677 | }, | 677 | }, |
| 678 | else => { | 678 | else => { |
| 679 | result.id = .Caret; | 679 | result.tag = .caret; |
| 680 | break; | 680 | break; |
| 681 | }, | 681 | }, |
| 682 | }, | 682 | }, |
| ... | @@ -684,8 +684,8 @@ pub const Tokenizer = struct { | ... | @@ -684,8 +684,8 @@ pub const Tokenizer = struct { |
| 684 | .identifier => switch (c) { | 684 | .identifier => switch (c) { |
| 685 | 'a'...'z', 'A'...'Z', '_', '0'...'9' => {}, | 685 | 'a'...'z', 'A'...'Z', '_', '0'...'9' => {}, |
| 686 | else => { | 686 | else => { |
| 687 | if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |id| { | 687 | if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |tag| { |
| 688 | result.id = id; | 688 | result.tag = tag; |
| 689 | } | 689 | } |
| 690 | break; | 690 | break; |
| 691 | }, | 691 | }, |
| ... | @@ -724,7 +724,7 @@ pub const Tokenizer = struct { | ... | @@ -724,7 +724,7 @@ pub const Tokenizer = struct { |
| 724 | state = .char_literal_backslash; | 724 | state = .char_literal_backslash; |
| 725 | }, | 725 | }, |
| 726 | '\'', 0x80...0xbf, 0xf8...0xff => { | 726 | '\'', 0x80...0xbf, 0xf8...0xff => { |
| 727 | result.id = .Invalid; | 727 | result.tag = .invalid; |
| 728 | break; | 728 | break; |
| 729 | }, | 729 | }, |
| 730 | 0xc0...0xdf => { // 110xxxxx | 730 | 0xc0...0xdf => { // 110xxxxx |
| ... | @@ -746,7 +746,7 @@ pub const Tokenizer = struct { | ... | @@ -746,7 +746,7 @@ pub const Tokenizer = struct { |
| 746 | 746 | ||
| 747 | .char_literal_backslash => switch (c) { | 747 | .char_literal_backslash => switch (c) { |
| 748 | '\n' => { | 748 | '\n' => { |
| 749 | result.id = .Invalid; | 749 | result.tag = .invalid; |
| 750 | break; | 750 | break; |
| 751 | }, | 751 | }, |
| 752 | 'x' => { | 752 | 'x' => { |
| ... | @@ -769,7 +769,7 @@ pub const Tokenizer = struct { | ... | @@ -769,7 +769,7 @@ pub const Tokenizer = struct { |
| 769 | } | 769 | } |
| 770 | }, | 770 | }, |
| 771 | else => { | 771 | else => { |
| 772 | result.id = .Invalid; | 772 | result.tag = .invalid; |
| 773 | break; | 773 | break; |
| 774 | }, | 774 | }, |
| 775 | }, | 775 | }, |
| ... | @@ -780,7 +780,7 @@ pub const Tokenizer = struct { | ... | @@ -780,7 +780,7 @@ pub const Tokenizer = struct { |
| 780 | seen_escape_digits = 0; | 780 | seen_escape_digits = 0; |
| 781 | }, | 781 | }, |
| 782 | else => { | 782 | else => { |
| 783 | result.id = .Invalid; | 783 | result.tag = .invalid; |
| 784 | state = .char_literal_unicode_invalid; | 784 | state = .char_literal_unicode_invalid; |
| 785 | }, | 785 | }, |
| 786 | }, | 786 | }, |
| ... | @@ -791,14 +791,14 @@ pub const Tokenizer = struct { | ... | @@ -791,14 +791,14 @@ pub const Tokenizer = struct { |
| 791 | }, | 791 | }, |
| 792 | '}' => { | 792 | '}' => { |
| 793 | if (seen_escape_digits == 0) { | 793 | if (seen_escape_digits == 0) { |
| 794 | result.id = .Invalid; | 794 | result.tag = .invalid; |
| 795 | state = .char_literal_unicode_invalid; | 795 | state = .char_literal_unicode_invalid; |
| 796 | } else { | 796 | } else { |
| 797 | state = .char_literal_end; | 797 | state = .char_literal_end; |
| 798 | } | 798 | } |
| 799 | }, | 799 | }, |
| 800 | else => { | 800 | else => { |
| 801 | result.id = .Invalid; | 801 | result.tag = .invalid; |
| 802 | state = .char_literal_unicode_invalid; | 802 | state = .char_literal_unicode_invalid; |
| 803 | }, | 803 | }, |
| 804 | }, | 804 | }, |
| ... | @@ -813,12 +813,12 @@ pub const Tokenizer = struct { | ... | @@ -813,12 +813,12 @@ pub const Tokenizer = struct { |
| 813 | 813 | ||
| 814 | .char_literal_end => switch (c) { | 814 | .char_literal_end => switch (c) { |
| 815 | '\'' => { | 815 | '\'' => { |
| 816 | result.id = .CharLiteral; | 816 | result.tag = .char_literal; |
| 817 | self.index += 1; | 817 | self.index += 1; |
| 818 | break; | 818 | break; |
| 819 | }, | 819 | }, |
| 820 | else => { | 820 | else => { |
| 821 | result.id = .Invalid; | 821 | result.tag = .invalid; |
| 822 | break; | 822 | break; |
| 823 | }, | 823 | }, |
| 824 | }, | 824 | }, |
| ... | @@ -831,7 +831,7 @@ pub const Tokenizer = struct { | ... | @@ -831,7 +831,7 @@ pub const Tokenizer = struct { |
| 831 | } | 831 | } |
| 832 | }, | 832 | }, |
| 833 | else => { | 833 | else => { |
| 834 | result.id = .Invalid; | 834 | result.tag = .invalid; |
| 835 | break; | 835 | break; |
| 836 | }, | 836 | }, |
| 837 | }, | 837 | }, |
| ... | @@ -847,58 +847,58 @@ pub const Tokenizer = struct { | ... | @@ -847,58 +847,58 @@ pub const Tokenizer = struct { |
| 847 | 847 | ||
| 848 | .bang => switch (c) { | 848 | .bang => switch (c) { |
| 849 | '=' => { | 849 | '=' => { |
| 850 | result.id = .BangEqual; | 850 | result.tag = .bang_equal; |
| 851 | self.index += 1; | 851 | self.index += 1; |
| 852 | break; | 852 | break; |
| 853 | }, | 853 | }, |
| 854 | else => { | 854 | else => { |
| 855 | result.id = .Bang; | 855 | result.tag = .bang; |
| 856 | break; | 856 | break; |
| 857 | }, | 857 | }, |
| 858 | }, | 858 | }, |
| 859 | 859 | ||
| 860 | .pipe => switch (c) { | 860 | .pipe => switch (c) { |
| 861 | '=' => { | 861 | '=' => { |
| 862 | result.id = .PipeEqual; | 862 | result.tag = .pipe_equal; |
| 863 | self.index += 1; | 863 | self.index += 1; |
| 864 | break; | 864 | break; |
| 865 | }, | 865 | }, |
| 866 | '|' => { | 866 | '|' => { |
| 867 | result.id = .PipePipe; | 867 | result.tag = .pipe_pipe; |
| 868 | self.index += 1; | 868 | self.index += 1; |
| 869 | break; | 869 | break; |
| 870 | }, | 870 | }, |
| 871 | else => { | 871 | else => { |
| 872 | result.id = .Pipe; | 872 | result.tag = .pipe; |
| 873 | break; | 873 | break; |
| 874 | }, | 874 | }, |
| 875 | }, | 875 | }, |
| 876 | 876 | ||
| 877 | .equal => switch (c) { | 877 | .equal => switch (c) { |
| 878 | '=' => { | 878 | '=' => { |
| 879 | result.id = .EqualEqual; | 879 | result.tag = .equal_equal; |
| 880 | self.index += 1; | 880 | self.index += 1; |
| 881 | break; | 881 | break; |
| 882 | }, | 882 | }, |
| 883 | '>' => { | 883 | '>' => { |
| 884 | result.id = .EqualAngleBracketRight; | 884 | result.tag = .equal_angle_bracket_right; |
| 885 | self.index += 1; | 885 | self.index += 1; |
| 886 | break; | 886 | break; |
| 887 | }, | 887 | }, |
| 888 | else => { | 888 | else => { |
| 889 | result.id = .Equal; | 889 | result.tag = .equal; |
| 890 | break; | 890 | break; |
| 891 | }, | 891 | }, |
| 892 | }, | 892 | }, |
| 893 | 893 | ||
| 894 | .minus => switch (c) { | 894 | .minus => switch (c) { |
| 895 | '>' => { | 895 | '>' => { |
| 896 | result.id = .Arrow; | 896 | result.tag = .arrow; |
| 897 | self.index += 1; | 897 | self.index += 1; |
| 898 | break; | 898 | break; |
| 899 | }, | 899 | }, |
| 900 | '=' => { | 900 | '=' => { |
| 901 | result.id = .MinusEqual; | 901 | result.tag = .minus_equal; |
| 902 | self.index += 1; | 902 | self.index += 1; |
| 903 | break; | 903 | break; |
| 904 | }, | 904 | }, |
| ... | @@ -906,19 +906,19 @@ pub const Tokenizer = struct { | ... | @@ -906,19 +906,19 @@ pub const Tokenizer = struct { |
| 906 | state = .minus_percent; | 906 | state = .minus_percent; |
| 907 | }, | 907 | }, |
| 908 | else => { | 908 | else => { |
| 909 | result.id = .Minus; | 909 | result.tag = .minus; |
| 910 | break; | 910 | break; |
| 911 | }, | 911 | }, |
| 912 | }, | 912 | }, |
| 913 | 913 | ||
| 914 | .minus_percent => switch (c) { | 914 | .minus_percent => switch (c) { |
| 915 | '=' => { | 915 | '=' => { |
| 916 | result.id = .MinusPercentEqual; | 916 | result.tag = .minus_percent_equal; |
| 917 | self.index += 1; | 917 | self.index += 1; |
| 918 | break; | 918 | break; |
| 919 | }, | 919 | }, |
| 920 | else => { | 920 | else => { |
| 921 | result.id = .MinusPercent; | 921 | result.tag = .minus_percent; |
| 922 | break; | 922 | break; |
| 923 | }, | 923 | }, |
| 924 | }, | 924 | }, |
| ... | @@ -928,24 +928,24 @@ pub const Tokenizer = struct { | ... | @@ -928,24 +928,24 @@ pub const Tokenizer = struct { |
| 928 | state = .angle_bracket_angle_bracket_left; | 928 | state = .angle_bracket_angle_bracket_left; |
| 929 | }, | 929 | }, |
| 930 | '=' => { | 930 | '=' => { |
| 931 | result.id = .AngleBracketLeftEqual; | 931 | result.tag = .angle_bracket_left_equal; |
| 932 | self.index += 1; | 932 | self.index += 1; |
| 933 | break; | 933 | break; |
| 934 | }, | 934 | }, |
| 935 | else => { | 935 | else => { |
| 936 | result.id = .AngleBracketLeft; | 936 | result.tag = .angle_bracket_left; |
| 937 | break; | 937 | break; |
| 938 | }, | 938 | }, |
| 939 | }, | 939 | }, |
| 940 | 940 | ||
| 941 | .angle_bracket_angle_bracket_left => switch (c) { | 941 | .angle_bracket_angle_bracket_left => switch (c) { |
| 942 | '=' => { | 942 | '=' => { |
| 943 | result.id = .AngleBracketAngleBracketLeftEqual; | 943 | result.tag = .angle_bracket_angle_bracket_left_equal; |
| 944 | self.index += 1; | 944 | self.index += 1; |
| 945 | break; | 945 | break; |
| 946 | }, | 946 | }, |
| 947 | else => { | 947 | else => { |
| 948 | result.id = .AngleBracketAngleBracketLeft; | 948 | result.tag = .angle_bracket_angle_bracket_left; |
| 949 | break; | 949 | break; |
| 950 | }, | 950 | }, |
| 951 | }, | 951 | }, |
| ... | @@ -955,24 +955,24 @@ pub const Tokenizer = struct { | ... | @@ -955,24 +955,24 @@ pub const Tokenizer = struct { |
| 955 | state = .angle_bracket_angle_bracket_right; | 955 | state = .angle_bracket_angle_bracket_right; |
| 956 | }, | 956 | }, |
| 957 | '=' => { | 957 | '=' => { |
| 958 | result.id = .AngleBracketRightEqual; | 958 | result.tag = .angle_bracket_right_equal; |
| 959 | self.index += 1; | 959 | self.index += 1; |
| 960 | break; | 960 | break; |
| 961 | }, | 961 | }, |
| 962 | else => { | 962 | else => { |
| 963 | result.id = .AngleBracketRight; | 963 | result.tag = .angle_bracket_right; |
| 964 | break; | 964 | break; |
| 965 | }, | 965 | }, |
| 966 | }, | 966 | }, |
| 967 | 967 | ||
| 968 | .angle_bracket_angle_bracket_right => switch (c) { | 968 | .angle_bracket_angle_bracket_right => switch (c) { |
| 969 | '=' => { | 969 | '=' => { |
| 970 | result.id = .AngleBracketAngleBracketRightEqual; | 970 | result.tag = .angle_bracket_angle_bracket_right_equal; |
| 971 | self.index += 1; | 971 | self.index += 1; |
| 972 | break; | 972 | break; |
| 973 | }, | 973 | }, |
| 974 | else => { | 974 | else => { |
| 975 | result.id = .AngleBracketAngleBracketRight; | 975 | result.tag = .angle_bracket_angle_bracket_right; |
| 976 | break; | 976 | break; |
| 977 | }, | 977 | }, |
| 978 | }, | 978 | }, |
| ... | @@ -985,30 +985,30 @@ pub const Tokenizer = struct { | ... | @@ -985,30 +985,30 @@ pub const Tokenizer = struct { |
| 985 | state = .period_asterisk; | 985 | state = .period_asterisk; |
| 986 | }, | 986 | }, |
| 987 | else => { | 987 | else => { |
| 988 | result.id = .Period; | 988 | result.tag = .period; |
| 989 | break; | 989 | break; |
| 990 | }, | 990 | }, |
| 991 | }, | 991 | }, |
| 992 | 992 | ||
| 993 | .period_2 => switch (c) { | 993 | .period_2 => switch (c) { |
| 994 | '.' => { | 994 | '.' => { |
| 995 | result.id = .Ellipsis3; | 995 | result.tag = .ellipsis3; |
| 996 | self.index += 1; | 996 | self.index += 1; |
| 997 | break; | 997 | break; |
| 998 | }, | 998 | }, |
| 999 | else => { | 999 | else => { |
| 1000 | result.id = .Ellipsis2; | 1000 | result.tag = .ellipsis2; |
| 1001 | break; | 1001 | break; |
| 1002 | }, | 1002 | }, |
| 1003 | }, | 1003 | }, |
| 1004 | 1004 | ||
| 1005 | .period_asterisk => switch (c) { | 1005 | .period_asterisk => switch (c) { |
| 1006 | '*' => { | 1006 | '*' => { |
| 1007 | result.id = .Invalid_periodasterisks; | 1007 | result.tag = .invalid_periodasterisks; |
| 1008 | break; | 1008 | break; |
| 1009 | }, | 1009 | }, |
| 1010 | else => { | 1010 | else => { |
| 1011 | result.id = .PeriodAsterisk; | 1011 | result.tag = .period_asterisk; |
| 1012 | break; | 1012 | break; |
| 1013 | }, | 1013 | }, |
| 1014 | }, | 1014 | }, |
| ... | @@ -1016,15 +1016,14 @@ pub const Tokenizer = struct { | ... | @@ -1016,15 +1016,14 @@ pub const Tokenizer = struct { |
| 1016 | .slash => switch (c) { | 1016 | .slash => switch (c) { |
| 1017 | '/' => { | 1017 | '/' => { |
| 1018 | state = .line_comment_start; | 1018 | state = .line_comment_start; |
| 1019 | result.id = .LineComment; | ||
| 1020 | }, | 1019 | }, |
| 1021 | '=' => { | 1020 | '=' => { |
| 1022 | result.id = .SlashEqual; | 1021 | result.tag = .slash_equal; |
| 1023 | self.index += 1; | 1022 | self.index += 1; |
| 1024 | break; | 1023 | break; |
| 1025 | }, | 1024 | }, |
| 1026 | else => { | 1025 | else => { |
| 1027 | result.id = .Slash; | 1026 | result.tag = .slash; |
| 1028 | break; | 1027 | break; |
| 1029 | }, | 1028 | }, |
| 1030 | }, | 1029 | }, |
| ... | @@ -1033,10 +1032,13 @@ pub const Tokenizer = struct { | ... | @@ -1033,10 +1032,13 @@ pub const Tokenizer = struct { |
| 1033 | state = .doc_comment_start; | 1032 | state = .doc_comment_start; |
| 1034 | }, | 1033 | }, |
| 1035 | '!' => { | 1034 | '!' => { |
| 1036 | result.id = .ContainerDocComment; | 1035 | result.tag = .container_doc_comment; |
| 1037 | state = .container_doc_comment; | 1036 | state = .container_doc_comment; |
| 1038 | }, | 1037 | }, |
| 1039 | '\n' => break, | 1038 | '\n' => { |
| 1039 | state = .start; | ||
| 1040 | result.loc.start = self.index + 1; | ||
| 1041 | }, | ||
| 1040 | '\t', '\r' => state = .line_comment, | 1042 | '\t', '\r' => state = .line_comment, |
| 1041 | else => { | 1043 | else => { |
| 1042 | state = .line_comment; | 1044 | state = .line_comment; |
| ... | @@ -1048,20 +1050,28 @@ pub const Tokenizer = struct { | ... | @@ -1048,20 +1050,28 @@ pub const Tokenizer = struct { |
| 1048 | state = .line_comment; | 1050 | state = .line_comment; |
| 1049 | }, | 1051 | }, |
| 1050 | '\n' => { | 1052 | '\n' => { |
| 1051 | result.id = .DocComment; | 1053 | result.tag = .doc_comment; |
| 1052 | break; | 1054 | break; |
| 1053 | }, | 1055 | }, |
| 1054 | '\t', '\r' => { | 1056 | '\t', '\r' => { |
| 1055 | state = .doc_comment; | 1057 | state = .doc_comment; |
| 1056 | result.id = .DocComment; | 1058 | result.tag = .doc_comment; |
| 1057 | }, | 1059 | }, |
| 1058 | else => { | 1060 | else => { |
| 1059 | state = .doc_comment; | 1061 | state = .doc_comment; |
| 1060 | result.id = .DocComment; | 1062 | result.tag = .doc_comment; |
| 1061 | self.checkLiteralCharacter(); | 1063 | self.checkLiteralCharacter(); |
| 1062 | }, | 1064 | }, |
| 1063 | }, | 1065 | }, |
| 1064 | .line_comment, .doc_comment, .container_doc_comment => switch (c) { | 1066 | .line_comment => switch (c) { |
| 1067 | '\n' => { | ||
| 1068 | state = .start; | ||
| 1069 | result.loc.start = self.index + 1; | ||
| 1070 | }, | ||
| 1071 | '\t', '\r' => {}, | ||
| 1072 | else => self.checkLiteralCharacter(), | ||
| 1073 | }, | ||
| 1074 | .doc_comment, .container_doc_comment => switch (c) { | ||
| 1065 | '\n' => break, | 1075 | '\n' => break, |
| 1066 | '\t', '\r' => {}, | 1076 | '\t', '\r' => {}, |
| 1067 | else => self.checkLiteralCharacter(), | 1077 | else => self.checkLiteralCharacter(), |
| ... | @@ -1083,7 +1093,7 @@ pub const Tokenizer = struct { | ... | @@ -1083,7 +1093,7 @@ pub const Tokenizer = struct { |
| 1083 | }, | 1093 | }, |
| 1084 | else => { | 1094 | else => { |
| 1085 | if (isIdentifierChar(c)) { | 1095 | if (isIdentifierChar(c)) { |
| 1086 | result.id = .Invalid; | 1096 | result.tag = .invalid; |
| 1087 | } | 1097 | } |
| 1088 | break; | 1098 | break; |
| 1089 | }, | 1099 | }, |
| ... | @@ -1093,7 +1103,7 @@ pub const Tokenizer = struct { | ... | @@ -1093,7 +1103,7 @@ pub const Tokenizer = struct { |
| 1093 | state = .int_literal_bin; | 1103 | state = .int_literal_bin; |
| 1094 | }, | 1104 | }, |
| 1095 | else => { | 1105 | else => { |
| 1096 | result.id = .Invalid; | 1106 | result.tag = .invalid; |
| 1097 | break; | 1107 | break; |
| 1098 | }, | 1108 | }, |
| 1099 | }, | 1109 | }, |
| ... | @@ -1104,7 +1114,7 @@ pub const Tokenizer = struct { | ... | @@ -1104,7 +1114,7 @@ pub const Tokenizer = struct { |
| 1104 | '0'...'1' => {}, | 1114 | '0'...'1' => {}, |
| 1105 | else => { | 1115 | else => { |
| 1106 | if (isIdentifierChar(c)) { | 1116 | if (isIdentifierChar(c)) { |
| 1107 | result.id = .Invalid; | 1117 | result.tag = .invalid; |
| 1108 | } | 1118 | } |
| 1109 | break; | 1119 | break; |
| 1110 | }, | 1120 | }, |
| ... | @@ -1114,7 +1124,7 @@ pub const Tokenizer = struct { | ... | @@ -1114,7 +1124,7 @@ pub const Tokenizer = struct { |
| 1114 | state = .int_literal_oct; | 1124 | state = .int_literal_oct; |
| 1115 | }, | 1125 | }, |
| 1116 | else => { | 1126 | else => { |
| 1117 | result.id = .Invalid; | 1127 | result.tag = .invalid; |
| 1118 | break; | 1128 | break; |
| 1119 | }, | 1129 | }, |
| 1120 | }, | 1130 | }, |
| ... | @@ -1125,7 +1135,7 @@ pub const Tokenizer = struct { | ... | @@ -1125,7 +1135,7 @@ pub const Tokenizer = struct { |
| 1125 | '0'...'7' => {}, | 1135 | '0'...'7' => {}, |
| 1126 | else => { | 1136 | else => { |
| 1127 | if (isIdentifierChar(c)) { | 1137 | if (isIdentifierChar(c)) { |
| 1128 | result.id = .Invalid; | 1138 | result.tag = .invalid; |
| 1129 | } | 1139 | } |
| 1130 | break; | 1140 | break; |
| 1131 | }, | 1141 | }, |
| ... | @@ -1135,7 +1145,7 @@ pub const Tokenizer = struct { | ... | @@ -1135,7 +1145,7 @@ pub const Tokenizer = struct { |
| 1135 | state = .int_literal_dec; | 1145 | state = .int_literal_dec; |
| 1136 | }, | 1146 | }, |
| 1137 | else => { | 1147 | else => { |
| 1138 | result.id = .Invalid; | 1148 | result.tag = .invalid; |
| 1139 | break; | 1149 | break; |
| 1140 | }, | 1150 | }, |
| 1141 | }, | 1151 | }, |
| ... | @@ -1145,16 +1155,16 @@ pub const Tokenizer = struct { | ... | @@ -1145,16 +1155,16 @@ pub const Tokenizer = struct { |
| 1145 | }, | 1155 | }, |
| 1146 | '.' => { | 1156 | '.' => { |
| 1147 | state = .num_dot_dec; | 1157 | state = .num_dot_dec; |
| 1148 | result.id = .FloatLiteral; | 1158 | result.tag = .float_literal; |
| 1149 | }, | 1159 | }, |
| 1150 | 'e', 'E' => { | 1160 | 'e', 'E' => { |
| 1151 | state = .float_exponent_unsigned; | 1161 | state = .float_exponent_unsigned; |
| 1152 | result.id = .FloatLiteral; | 1162 | result.tag = .float_literal; |
| 1153 | }, | 1163 | }, |
| 1154 | '0'...'9' => {}, | 1164 | '0'...'9' => {}, |
| 1155 | else => { | 1165 | else => { |
| 1156 | if (isIdentifierChar(c)) { | 1166 | if (isIdentifierChar(c)) { |
| 1157 | result.id = .Invalid; | 1167 | result.tag = .invalid; |
| 1158 | } | 1168 | } |
| 1159 | break; | 1169 | break; |
| 1160 | }, | 1170 | }, |
| ... | @@ -1164,7 +1174,7 @@ pub const Tokenizer = struct { | ... | @@ -1164,7 +1174,7 @@ pub const Tokenizer = struct { |
| 1164 | state = .int_literal_hex; | 1174 | state = .int_literal_hex; |
| 1165 | }, | 1175 | }, |
| 1166 | else => { | 1176 | else => { |
| 1167 | result.id = .Invalid; | 1177 | result.tag = .invalid; |
| 1168 | break; | 1178 | break; |
| 1169 | }, | 1179 | }, |
| 1170 | }, | 1180 | }, |
| ... | @@ -1174,23 +1184,23 @@ pub const Tokenizer = struct { | ... | @@ -1174,23 +1184,23 @@ pub const Tokenizer = struct { |
| 1174 | }, | 1184 | }, |
| 1175 | '.' => { | 1185 | '.' => { |
| 1176 | state = .num_dot_hex; | 1186 | state = .num_dot_hex; |
| 1177 | result.id = .FloatLiteral; | 1187 | result.tag = .float_literal; |
| 1178 | }, | 1188 | }, |
| 1179 | 'p', 'P' => { | 1189 | 'p', 'P' => { |
| 1180 | state = .float_exponent_unsigned; | 1190 | state = .float_exponent_unsigned; |
| 1181 | result.id = .FloatLiteral; | 1191 | result.tag = .float_literal; |
| 1182 | }, | 1192 | }, |
| 1183 | '0'...'9', 'a'...'f', 'A'...'F' => {}, | 1193 | '0'...'9', 'a'...'f', 'A'...'F' => {}, |
| 1184 | else => { | 1194 | else => { |
| 1185 | if (isIdentifierChar(c)) { | 1195 | if (isIdentifierChar(c)) { |
| 1186 | result.id = .Invalid; | 1196 | result.tag = .invalid; |
| 1187 | } | 1197 | } |
| 1188 | break; | 1198 | break; |
| 1189 | }, | 1199 | }, |
| 1190 | }, | 1200 | }, |
| 1191 | .num_dot_dec => switch (c) { | 1201 | .num_dot_dec => switch (c) { |
| 1192 | '.' => { | 1202 | '.' => { |
| 1193 | result.id = .IntegerLiteral; | 1203 | result.tag = .integer_literal; |
| 1194 | self.index -= 1; | 1204 | self.index -= 1; |
| 1195 | state = .start; | 1205 | state = .start; |
| 1196 | break; | 1206 | break; |
| ... | @@ -1203,14 +1213,14 @@ pub const Tokenizer = struct { | ... | @@ -1203,14 +1213,14 @@ pub const Tokenizer = struct { |
| 1203 | }, | 1213 | }, |
| 1204 | else => { | 1214 | else => { |
| 1205 | if (isIdentifierChar(c)) { | 1215 | if (isIdentifierChar(c)) { |
| 1206 | result.id = .Invalid; | 1216 | result.tag = .invalid; |
| 1207 | } | 1217 | } |
| 1208 | break; | 1218 | break; |
| 1209 | }, | 1219 | }, |
| 1210 | }, | 1220 | }, |
| 1211 | .num_dot_hex => switch (c) { | 1221 | .num_dot_hex => switch (c) { |
| 1212 | '.' => { | 1222 | '.' => { |
| 1213 | result.id = .IntegerLiteral; | 1223 | result.tag = .integer_literal; |
| 1214 | self.index -= 1; | 1224 | self.index -= 1; |
| 1215 | state = .start; | 1225 | state = .start; |
| 1216 | break; | 1226 | break; |
| ... | @@ -1219,12 +1229,12 @@ pub const Tokenizer = struct { | ... | @@ -1219,12 +1229,12 @@ pub const Tokenizer = struct { |
| 1219 | state = .float_exponent_unsigned; | 1229 | state = .float_exponent_unsigned; |
| 1220 | }, | 1230 | }, |
| 1221 | '0'...'9', 'a'...'f', 'A'...'F' => { | 1231 | '0'...'9', 'a'...'f', 'A'...'F' => { |
| 1222 | result.id = .FloatLiteral; | 1232 | result.tag = .float_literal; |
| 1223 | state = .float_fraction_hex; | 1233 | state = .float_fraction_hex; |
| 1224 | }, | 1234 | }, |
| 1225 | else => { | 1235 | else => { |
| 1226 | if (isIdentifierChar(c)) { | 1236 | if (isIdentifierChar(c)) { |
| 1227 | result.id = .Invalid; | 1237 | result.tag = .invalid; |
| 1228 | } | 1238 | } |
| 1229 | break; | 1239 | break; |
| 1230 | }, | 1240 | }, |
| ... | @@ -1234,7 +1244,7 @@ pub const Tokenizer = struct { | ... | @@ -1234,7 +1244,7 @@ pub const Tokenizer = struct { |
| 1234 | state = .float_fraction_dec; | 1244 | state = .float_fraction_dec; |
| 1235 | }, | 1245 | }, |
| 1236 | else => { | 1246 | else => { |
| 1237 | result.id = .Invalid; | 1247 | result.tag = .invalid; |
| 1238 | break; | 1248 | break; |
| 1239 | }, | 1249 | }, |
| 1240 | }, | 1250 | }, |
| ... | @@ -1248,7 +1258,7 @@ pub const Tokenizer = struct { | ... | @@ -1248,7 +1258,7 @@ pub const Tokenizer = struct { |
| 1248 | '0'...'9' => {}, | 1258 | '0'...'9' => {}, |
| 1249 | else => { | 1259 | else => { |
| 1250 | if (isIdentifierChar(c)) { | 1260 | if (isIdentifierChar(c)) { |
| 1251 | result.id = .Invalid; | 1261 | result.tag = .invalid; |
| 1252 | } | 1262 | } |
| 1253 | break; | 1263 | break; |
| 1254 | }, | 1264 | }, |
| ... | @@ -1258,7 +1268,7 @@ pub const Tokenizer = struct { | ... | @@ -1258,7 +1268,7 @@ pub const Tokenizer = struct { |
| 1258 | state = .float_fraction_hex; | 1268 | state = .float_fraction_hex; |
| 1259 | }, | 1269 | }, |
| 1260 | else => { | 1270 | else => { |
| 1261 | result.id = .Invalid; | 1271 | result.tag = .invalid; |
| 1262 | break; | 1272 | break; |
| 1263 | }, | 1273 | }, |
| 1264 | }, | 1274 | }, |
| ... | @@ -1272,7 +1282,7 @@ pub const Tokenizer = struct { | ... | @@ -1272,7 +1282,7 @@ pub const Tokenizer = struct { |
| 1272 | '0'...'9', 'a'...'f', 'A'...'F' => {}, | 1282 | '0'...'9', 'a'...'f', 'A'...'F' => {}, |
| 1273 | else => { | 1283 | else => { |
| 1274 | if (isIdentifierChar(c)) { | 1284 | if (isIdentifierChar(c)) { |
| 1275 | result.id = .Invalid; | 1285 | result.tag = .invalid; |
| 1276 | } | 1286 | } |
| 1277 | break; | 1287 | break; |
| 1278 | }, | 1288 | }, |
| ... | @@ -1292,7 +1302,7 @@ pub const Tokenizer = struct { | ... | @@ -1292,7 +1302,7 @@ pub const Tokenizer = struct { |
| 1292 | state = .float_exponent_num; | 1302 | state = .float_exponent_num; |
| 1293 | }, | 1303 | }, |
| 1294 | else => { | 1304 | else => { |
| 1295 | result.id = .Invalid; | 1305 | result.tag = .invalid; |
| 1296 | break; | 1306 | break; |
| 1297 | }, | 1307 | }, |
| 1298 | }, | 1308 | }, |
| ... | @@ -1303,7 +1313,7 @@ pub const Tokenizer = struct { | ... | @@ -1303,7 +1313,7 @@ pub const Tokenizer = struct { |
| 1303 | '0'...'9' => {}, | 1313 | '0'...'9' => {}, |
| 1304 | else => { | 1314 | else => { |
| 1305 | if (isIdentifierChar(c)) { | 1315 | if (isIdentifierChar(c)) { |
| 1306 | result.id = .Invalid; | 1316 | result.tag = .invalid; |
| 1307 | } | 1317 | } |
| 1308 | break; | 1318 | break; |
| 1309 | }, | 1319 | }, |
| ... | @@ -1324,21 +1334,20 @@ pub const Tokenizer = struct { | ... | @@ -1324,21 +1334,20 @@ pub const Tokenizer = struct { |
| 1324 | .string_literal, // find this error later | 1334 | .string_literal, // find this error later |
| 1325 | .multiline_string_literal_line, | 1335 | .multiline_string_literal_line, |
| 1326 | .builtin, | 1336 | .builtin, |
| 1337 | .line_comment, | ||
| 1338 | .line_comment_start, | ||
| 1327 | => {}, | 1339 | => {}, |
| 1328 | 1340 | ||
| 1329 | .identifier => { | 1341 | .identifier => { |
| 1330 | if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |id| { | 1342 | if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |tag| { |
| 1331 | result.id = id; | 1343 | result.tag = tag; |
| 1332 | } | 1344 | } |
| 1333 | }, | 1345 | }, |
| 1334 | .line_comment, .line_comment_start => { | ||
| 1335 | result.id = .LineComment; | ||
| 1336 | }, | ||
| 1337 | .doc_comment, .doc_comment_start => { | 1346 | .doc_comment, .doc_comment_start => { |
| 1338 | result.id = .DocComment; | 1347 | result.tag = .doc_comment; |
| 1339 | }, | 1348 | }, |
| 1340 | .container_doc_comment => { | 1349 | .container_doc_comment => { |
| 1341 | result.id = .ContainerDocComment; | 1350 | result.tag = .container_doc_comment; |
| 1342 | }, | 1351 | }, |
| 1343 | 1352 | ||
| 1344 | .int_literal_dec_no_underscore, | 1353 | .int_literal_dec_no_underscore, |
| ... | @@ -1361,80 +1370,81 @@ pub const Tokenizer = struct { | ... | @@ -1361,80 +1370,81 @@ pub const Tokenizer = struct { |
| 1361 | .char_literal_unicode, | 1370 | .char_literal_unicode, |
| 1362 | .string_literal_backslash, | 1371 | .string_literal_backslash, |
| 1363 | => { | 1372 | => { |
| 1364 | result.id = .Invalid; | 1373 | result.tag = .invalid; |
| 1365 | }, | 1374 | }, |
| 1366 | 1375 | ||
| 1367 | .equal => { | 1376 | .equal => { |
| 1368 | result.id = .Equal; | 1377 | result.tag = .equal; |
| 1369 | }, | 1378 | }, |
| 1370 | .bang => { | 1379 | .bang => { |
| 1371 | result.id = .Bang; | 1380 | result.tag = .bang; |
| 1372 | }, | 1381 | }, |
| 1373 | .minus => { | 1382 | .minus => { |
| 1374 | result.id = .Minus; | 1383 | result.tag = .minus; |
| 1375 | }, | 1384 | }, |
| 1376 | .slash => { | 1385 | .slash => { |
| 1377 | result.id = .Slash; | 1386 | result.tag = .slash; |
| 1378 | }, | 1387 | }, |
| 1379 | .zero => { | 1388 | .zero => { |
| 1380 | result.id = .IntegerLiteral; | 1389 | result.tag = .integer_literal; |
| 1381 | }, | 1390 | }, |
| 1382 | .ampersand => { | 1391 | .ampersand => { |
| 1383 | result.id = .Ampersand; | 1392 | result.tag = .ampersand; |
| 1384 | }, | 1393 | }, |
| 1385 | .period => { | 1394 | .period => { |
| 1386 | result.id = .Period; | 1395 | result.tag = .period; |
| 1387 | }, | 1396 | }, |
| 1388 | .period_2 => { | 1397 | .period_2 => { |
| 1389 | result.id = .Ellipsis2; | 1398 | result.tag = .ellipsis2; |
| 1390 | }, | 1399 | }, |
| 1391 | .period_asterisk => { | 1400 | .period_asterisk => { |
| 1392 | result.id = .PeriodAsterisk; | 1401 | result.tag = .period_asterisk; |
| 1393 | }, | 1402 | }, |
| 1394 | .pipe => { | 1403 | .pipe => { |
| 1395 | result.id = .Pipe; | 1404 | result.tag = .pipe; |
| 1396 | }, | 1405 | }, |
| 1397 | .angle_bracket_angle_bracket_right => { | 1406 | .angle_bracket_angle_bracket_right => { |
| 1398 | result.id = .AngleBracketAngleBracketRight; | 1407 | result.tag = .angle_bracket_angle_bracket_right; |
| 1399 | }, | 1408 | }, |
| 1400 | .angle_bracket_right => { | 1409 | .angle_bracket_right => { |
| 1401 | result.id = .AngleBracketRight; | 1410 | result.tag = .angle_bracket_right; |
| 1402 | }, | 1411 | }, |
| 1403 | .angle_bracket_angle_bracket_left => { | 1412 | .angle_bracket_angle_bracket_left => { |
| 1404 | result.id = .AngleBracketAngleBracketLeft; | 1413 | result.tag = .angle_bracket_angle_bracket_left; |
| 1405 | }, | 1414 | }, |
| 1406 | .angle_bracket_left => { | 1415 | .angle_bracket_left => { |
| 1407 | result.id = .AngleBracketLeft; | 1416 | result.tag = .angle_bracket_left; |
| 1408 | }, | 1417 | }, |
| 1409 | .plus_percent => { | 1418 | .plus_percent => { |
| 1410 | result.id = .PlusPercent; | 1419 | result.tag = .plus_percent; |
| 1411 | }, | 1420 | }, |
| 1412 | .plus => { | 1421 | .plus => { |
| 1413 | result.id = .Plus; | 1422 | result.tag = .plus; |
| 1414 | }, | 1423 | }, |
| 1415 | .percent => { | 1424 | .percent => { |
| 1416 | result.id = .Percent; | 1425 | result.tag = .percent; |
| 1417 | }, | 1426 | }, |
| 1418 | .caret => { | 1427 | .caret => { |
| 1419 | result.id = .Caret; | 1428 | result.tag = .caret; |
| 1420 | }, | 1429 | }, |
| 1421 | .asterisk_percent => { | 1430 | .asterisk_percent => { |
| 1422 | result.id = .AsteriskPercent; | 1431 | result.tag = .asterisk_percent; |
| 1423 | }, | 1432 | }, |
| 1424 | .asterisk => { | 1433 | .asterisk => { |
| 1425 | result.id = .Asterisk; | 1434 | result.tag = .asterisk; |
| 1426 | }, | 1435 | }, |
| 1427 | .minus_percent => { | 1436 | .minus_percent => { |
| 1428 | result.id = .MinusPercent; | 1437 | result.tag = .minus_percent; |
| 1429 | }, | 1438 | }, |
| 1430 | } | 1439 | } |
| 1431 | } | 1440 | } |
| 1432 | 1441 | ||
| 1433 | if (result.id == .Eof) { | 1442 | if (result.tag == .eof) { |
| 1434 | if (self.pending_invalid_token) |token| { | 1443 | if (self.pending_invalid_token) |token| { |
| 1435 | self.pending_invalid_token = null; | 1444 | self.pending_invalid_token = null; |
| 1436 | return token; | 1445 | return token; |
| 1437 | } | 1446 | } |
| 1447 | result.loc.start = self.index; | ||
| 1438 | } | 1448 | } |
| 1439 | 1449 | ||
| 1440 | result.loc.end = self.index; | 1450 | result.loc.end = self.index; |
| ... | @@ -1446,7 +1456,7 @@ pub const Tokenizer = struct { | ... | @@ -1446,7 +1456,7 @@ pub const Tokenizer = struct { |
| 1446 | const invalid_length = self.getInvalidCharacterLength(); | 1456 | const invalid_length = self.getInvalidCharacterLength(); |
| 1447 | if (invalid_length == 0) return; | 1457 | if (invalid_length == 0) return; |
| 1448 | self.pending_invalid_token = .{ | 1458 | self.pending_invalid_token = .{ |
| 1449 | .id = .Invalid, | 1459 | .tag = .invalid, |
| 1450 | .loc = .{ | 1460 | .loc = .{ |
| 1451 | .start = self.index, | 1461 | .start = self.index, |
| 1452 | .end = self.index + invalid_length, | 1462 | .end = self.index + invalid_length, |
| ... | @@ -1493,220 +1503,218 @@ pub const Tokenizer = struct { | ... | @@ -1493,220 +1503,218 @@ pub const Tokenizer = struct { |
| 1493 | }; | 1503 | }; |
| 1494 | 1504 | ||
| 1495 | test "tokenizer" { | 1505 | test "tokenizer" { |
| 1496 | testTokenize("test", &[_]Token.Id{.Keyword_test}); | 1506 | testTokenize("test", &.{.keyword_test}); |
| 1507 | } | ||
| 1508 | |||
| 1509 | test "line comment followed by top-level comptime" { | ||
| 1510 | testTokenize( | ||
| 1511 | \\// line comment | ||
| 1512 | \\comptime {} | ||
| 1513 | \\ | ||
| 1514 | , &.{ | ||
| 1515 | .keyword_comptime, | ||
| 1516 | .l_brace, | ||
| 1517 | .r_brace, | ||
| 1518 | }); | ||
| 1497 | } | 1519 | } |
| 1498 | 1520 | ||
| 1499 | test "tokenizer - unknown length pointer and then c pointer" { | 1521 | test "tokenizer - unknown length pointer and then c pointer" { |
| 1500 | testTokenize( | 1522 | testTokenize( |
| 1501 | \\[*]u8 | 1523 | \\[*]u8 |
| 1502 | \\[*c]u8 | 1524 | \\[*c]u8 |
| 1503 | , &[_]Token.Id{ | 1525 | , &.{ |
| 1504 | .LBracket, | 1526 | .l_bracket, |
| 1505 | .Asterisk, | 1527 | .asterisk, |
| 1506 | .RBracket, | 1528 | .r_bracket, |
| 1507 | .Identifier, | 1529 | .identifier, |
| 1508 | .LBracket, | 1530 | .l_bracket, |
| 1509 | .Asterisk, | 1531 | .asterisk, |
| 1510 | .Identifier, | 1532 | .identifier, |
| 1511 | .RBracket, | 1533 | .r_bracket, |
| 1512 | .Identifier, | 1534 | .identifier, |
| 1513 | }); | 1535 | }); |
| 1514 | } | 1536 | } |
| 1515 | 1537 | ||
| 1516 | test "tokenizer - code point literal with hex escape" { | 1538 | test "tokenizer - code point literal with hex escape" { |
| 1517 | testTokenize( | 1539 | testTokenize( |
| 1518 | \\'\x1b' | 1540 | \\'\x1b' |
| 1519 | , &[_]Token.Id{.CharLiteral}); | 1541 | , &.{.char_literal}); |
| 1520 | testTokenize( | 1542 | testTokenize( |
| 1521 | \\'\x1' | 1543 | \\'\x1' |
| 1522 | , &[_]Token.Id{ .Invalid, .Invalid }); | 1544 | , &.{ .invalid, .invalid }); |
| 1523 | } | 1545 | } |
| 1524 | 1546 | ||
| 1525 | test "tokenizer - code point literal with unicode escapes" { | 1547 | test "tokenizer - code point literal with unicode escapes" { |
| 1526 | // Valid unicode escapes | 1548 | // Valid unicode escapes |
| 1527 | testTokenize( | 1549 | testTokenize( |
| 1528 | \\'\u{3}' | 1550 | \\'\u{3}' |
| 1529 | , &[_]Token.Id{.CharLiteral}); | 1551 | , &.{.char_literal}); |
| 1530 | testTokenize( | 1552 | testTokenize( |
| 1531 | \\'\u{01}' | 1553 | \\'\u{01}' |
| 1532 | , &[_]Token.Id{.CharLiteral}); | 1554 | , &.{.char_literal}); |
| 1533 | testTokenize( | 1555 | testTokenize( |
| 1534 | \\'\u{2a}' | 1556 | \\'\u{2a}' |
| 1535 | , &[_]Token.Id{.CharLiteral}); | 1557 | , &.{.char_literal}); |
| 1536 | testTokenize( | 1558 | testTokenize( |
| 1537 | \\'\u{3f9}' | 1559 | \\'\u{3f9}' |
| 1538 | , &[_]Token.Id{.CharLiteral}); | 1560 | , &.{.char_literal}); |
| 1539 | testTokenize( | 1561 | testTokenize( |
| 1540 | \\'\u{6E09aBc1523}' | 1562 | \\'\u{6E09aBc1523}' |
| 1541 | , &[_]Token.Id{.CharLiteral}); | 1563 | , &.{.char_literal}); |
| 1542 | testTokenize( | 1564 | testTokenize( |
| 1543 | \\"\u{440}" | 1565 | \\"\u{440}" |
| 1544 | , &[_]Token.Id{.StringLiteral}); | 1566 | , &.{.string_literal}); |
| 1545 | 1567 | ||
| 1546 | // Invalid unicode escapes | 1568 | // Invalid unicode escapes |
| 1547 | testTokenize( | 1569 | testTokenize( |
| 1548 | \\'\u' | 1570 | \\'\u' |
| 1549 | , &[_]Token.Id{.Invalid}); | 1571 | , &.{.invalid}); |
| 1550 | testTokenize( | 1572 | testTokenize( |
| 1551 | \\'\u{{' | 1573 | \\'\u{{' |
| 1552 | , &[_]Token.Id{ .Invalid, .Invalid }); | 1574 | , &.{ .invalid, .invalid }); |
| 1553 | testTokenize( | 1575 | testTokenize( |
| 1554 | \\'\u{}' | 1576 | \\'\u{}' |
| 1555 | , &[_]Token.Id{ .Invalid, .Invalid }); | 1577 | , &.{ .invalid, .invalid }); |
| 1556 | testTokenize( | 1578 | testTokenize( |
| 1557 | \\'\u{s}' | 1579 | \\'\u{s}' |
| 1558 | , &[_]Token.Id{ .Invalid, .Invalid }); | 1580 | , &.{ .invalid, .invalid }); |
| 1559 | testTokenize( | 1581 | testTokenize( |
| 1560 | \\'\u{2z}' | 1582 | \\'\u{2z}' |
| 1561 | , &[_]Token.Id{ .Invalid, .Invalid }); | 1583 | , &.{ .invalid, .invalid }); |
| 1562 | testTokenize( | 1584 | testTokenize( |
| 1563 | \\'\u{4a' | 1585 | \\'\u{4a' |
| 1564 | , &[_]Token.Id{.Invalid}); | 1586 | , &.{.invalid}); |
| 1565 | 1587 | ||
| 1566 | // Test old-style unicode literals | 1588 | // Test old-style unicode literals |
| 1567 | testTokenize( | 1589 | testTokenize( |
| 1568 | \\'\u0333' | 1590 | \\'\u0333' |
| 1569 | , &[_]Token.Id{ .Invalid, .Invalid }); | 1591 | , &.{ .invalid, .invalid }); |
| 1570 | testTokenize( | 1592 | testTokenize( |
| 1571 | \\'\U0333' | 1593 | \\'\U0333' |
| 1572 | , &[_]Token.Id{ .Invalid, .IntegerLiteral, .Invalid }); | 1594 | , &.{ .invalid, .integer_literal, .invalid }); |
| 1573 | } | 1595 | } |
| 1574 | 1596 | ||
| 1575 | test "tokenizer - code point literal with unicode code point" { | 1597 | test "tokenizer - code point literal with unicode code point" { |
| 1576 | testTokenize( | 1598 | testTokenize( |
| 1577 | \\'💩' | 1599 | \\'💩' |
| 1578 | , &[_]Token.Id{.CharLiteral}); | 1600 | , &.{.char_literal}); |
| 1579 | } | 1601 | } |
| 1580 | 1602 | ||
| 1581 | test "tokenizer - float literal e exponent" { | 1603 | test "tokenizer - float literal e exponent" { |
| 1582 | testTokenize("a = 4.94065645841246544177e-324;\n", &[_]Token.Id{ | 1604 | testTokenize("a = 4.94065645841246544177e-324;\n", &.{ |
| 1583 | .Identifier, | 1605 | .identifier, |
| 1584 | .Equal, | 1606 | .equal, |
| 1585 | .FloatLiteral, | 1607 | .float_literal, |
| 1586 | .Semicolon, | 1608 | .semicolon, |
| 1587 | }); | 1609 | }); |
| 1588 | } | 1610 | } |
| 1589 | 1611 | ||
| 1590 | test "tokenizer - float literal p exponent" { | 1612 | test "tokenizer - float literal p exponent" { |
| 1591 | testTokenize("a = 0x1.a827999fcef32p+1022;\n", &[_]Token.Id{ | 1613 | testTokenize("a = 0x1.a827999fcef32p+1022;\n", &.{ |
| 1592 | .Identifier, | 1614 | .identifier, |
| 1593 | .Equal, | 1615 | .equal, |
| 1594 | .FloatLiteral, | 1616 | .float_literal, |
| 1595 | .Semicolon, | 1617 | .semicolon, |
| 1596 | }); | 1618 | }); |
| 1597 | } | 1619 | } |
| 1598 | 1620 | ||
| 1599 | test "tokenizer - chars" { | 1621 | test "tokenizer - chars" { |
| 1600 | testTokenize("'c'", &[_]Token.Id{.CharLiteral}); | 1622 | testTokenize("'c'", &.{.char_literal}); |
| 1601 | } | 1623 | } |
| 1602 | 1624 | ||
| 1603 | test "tokenizer - invalid token characters" { | 1625 | test "tokenizer - invalid token characters" { |
| 1604 | testTokenize("#", &[_]Token.Id{.Invalid}); | 1626 | testTokenize("#", &.{.invalid}); |
| 1605 | testTokenize("`", &[_]Token.Id{.Invalid}); | 1627 | testTokenize("`", &.{.invalid}); |
| 1606 | testTokenize("'c", &[_]Token.Id{.Invalid}); | 1628 | testTokenize("'c", &.{.invalid}); |
| 1607 | testTokenize("'", &[_]Token.Id{.Invalid}); | 1629 | testTokenize("'", &.{.invalid}); |
| 1608 | testTokenize("''", &[_]Token.Id{ .Invalid, .Invalid }); | 1630 | testTokenize("''", &.{ .invalid, .invalid }); |
| 1609 | } | 1631 | } |
| 1610 | 1632 | ||
| 1611 | test "tokenizer - invalid literal/comment characters" { | 1633 | test "tokenizer - invalid literal/comment characters" { |
| 1612 | testTokenize("\"\x00\"", &[_]Token.Id{ | 1634 | testTokenize("\"\x00\"", &.{ |
| 1613 | .StringLiteral, | 1635 | .string_literal, |
| 1614 | .Invalid, | 1636 | .invalid, |
| 1615 | }); | 1637 | }); |
| 1616 | testTokenize("//\x00", &[_]Token.Id{ | 1638 | testTokenize("//\x00", &.{ |
| 1617 | .LineComment, | 1639 | .invalid, |
| 1618 | .Invalid, | ||
| 1619 | }); | 1640 | }); |
| 1620 | testTokenize("//\x1f", &[_]Token.Id{ | 1641 | testTokenize("//\x1f", &.{ |
| 1621 | .LineComment, | 1642 | .invalid, |
| 1622 | .Invalid, | ||
| 1623 | }); | 1643 | }); |
| 1624 | testTokenize("//\x7f", &[_]Token.Id{ | 1644 | testTokenize("//\x7f", &.{ |
| 1625 | .LineComment, | 1645 | .invalid, |
| 1626 | .Invalid, | ||
| 1627 | }); | 1646 | }); |
| 1628 | } | 1647 | } |
| 1629 | 1648 | ||
| 1630 | test "tokenizer - utf8" { | 1649 | test "tokenizer - utf8" { |
| 1631 | testTokenize("//\xc2\x80", &[_]Token.Id{.LineComment}); | 1650 | testTokenize("//\xc2\x80", &.{}); |
| 1632 | testTokenize("//\xf4\x8f\xbf\xbf", &[_]Token.Id{.LineComment}); | 1651 | testTokenize("//\xf4\x8f\xbf\xbf", &.{}); |
| 1633 | } | 1652 | } |
| 1634 | 1653 | ||
| 1635 | test "tokenizer - invalid utf8" { | 1654 | test "tokenizer - invalid utf8" { |
| 1636 | testTokenize("//\x80", &[_]Token.Id{ | 1655 | testTokenize("//\x80", &.{ |
| 1637 | .LineComment, | 1656 | .invalid, |
| 1638 | .Invalid, | ||
| 1639 | }); | 1657 | }); |
| 1640 | testTokenize("//\xbf", &[_]Token.Id{ | 1658 | testTokenize("//\xbf", &.{ |
| 1641 | .LineComment, | 1659 | .invalid, |
| 1642 | .Invalid, | ||
| 1643 | }); | 1660 | }); |
| 1644 | testTokenize("//\xf8", &[_]Token.Id{ | 1661 | testTokenize("//\xf8", &.{ |
| 1645 | .LineComment, | 1662 | .invalid, |
| 1646 | .Invalid, | ||
| 1647 | }); | 1663 | }); |
| 1648 | testTokenize("//\xff", &[_]Token.Id{ | 1664 | testTokenize("//\xff", &.{ |
| 1649 | .LineComment, | 1665 | .invalid, |
| 1650 | .Invalid, | ||
| 1651 | }); | 1666 | }); |
| 1652 | testTokenize("//\xc2\xc0", &[_]Token.Id{ | 1667 | testTokenize("//\xc2\xc0", &.{ |
| 1653 | .LineComment, | 1668 | .invalid, |
| 1654 | .Invalid, | ||
| 1655 | }); | 1669 | }); |
| 1656 | testTokenize("//\xe0", &[_]Token.Id{ | 1670 | testTokenize("//\xe0", &.{ |
| 1657 | .LineComment, | 1671 | .invalid, |
| 1658 | .Invalid, | ||
| 1659 | }); | 1672 | }); |
| 1660 | testTokenize("//\xf0", &[_]Token.Id{ | 1673 | testTokenize("//\xf0", &.{ |
| 1661 | .LineComment, | 1674 | .invalid, |
| 1662 | .Invalid, | ||
| 1663 | }); | 1675 | }); |
| 1664 | testTokenize("//\xf0\x90\x80\xc0", &[_]Token.Id{ | 1676 | testTokenize("//\xf0\x90\x80\xc0", &.{ |
| 1665 | .LineComment, | 1677 | .invalid, |
| 1666 | .Invalid, | ||
| 1667 | }); | 1678 | }); |
| 1668 | } | 1679 | } |
| 1669 | 1680 | ||
| 1670 | test "tokenizer - illegal unicode codepoints" { | 1681 | test "tokenizer - illegal unicode codepoints" { |
| 1671 | // unicode newline characters.U+0085, U+2028, U+2029 | 1682 | // unicode newline characters.U+0085, U+2028, U+2029 |
| 1672 | testTokenize("//\xc2\x84", &[_]Token.Id{.LineComment}); | 1683 | testTokenize("//\xc2\x84", &.{}); |
| 1673 | testTokenize("//\xc2\x85", &[_]Token.Id{ | 1684 | testTokenize("//\xc2\x85", &.{ |
| 1674 | .LineComment, | 1685 | .invalid, |
| 1675 | .Invalid, | ||
| 1676 | }); | 1686 | }); |
| 1677 | testTokenize("//\xc2\x86", &[_]Token.Id{.LineComment}); | 1687 | testTokenize("//\xc2\x86", &.{}); |
| 1678 | testTokenize("//\xe2\x80\xa7", &[_]Token.Id{.LineComment}); | 1688 | testTokenize("//\xe2\x80\xa7", &.{}); |
| 1679 | testTokenize("//\xe2\x80\xa8", &[_]Token.Id{ | 1689 | testTokenize("//\xe2\x80\xa8", &.{ |
| 1680 | .LineComment, | 1690 | .invalid, |
| 1681 | .Invalid, | ||
| 1682 | }); | 1691 | }); |
| 1683 | testTokenize("//\xe2\x80\xa9", &[_]Token.Id{ | 1692 | testTokenize("//\xe2\x80\xa9", &.{ |
| 1684 | .LineComment, | 1693 | .invalid, |
| 1685 | .Invalid, | ||
| 1686 | }); | 1694 | }); |
| 1687 | testTokenize("//\xe2\x80\xaa", &[_]Token.Id{.LineComment}); | 1695 | testTokenize("//\xe2\x80\xaa", &.{}); |
| 1688 | } | 1696 | } |
| 1689 | 1697 | ||
| 1690 | test "tokenizer - string identifier and builtin fns" { | 1698 | test "tokenizer - string identifier and builtin fns" { |
| 1691 | testTokenize( | 1699 | testTokenize( |
| 1692 | \\const @"if" = @import("std"); | 1700 | \\const @"if" = @import("std"); |
| 1693 | , &[_]Token.Id{ | 1701 | , &.{ |
| 1694 | .Keyword_const, | 1702 | .keyword_const, |
| 1695 | .Identifier, | 1703 | .identifier, |
| 1696 | .Equal, | 1704 | .equal, |
| 1697 | .Builtin, | 1705 | .builtin, |
| 1698 | .LParen, | 1706 | .l_paren, |
| 1699 | .StringLiteral, | 1707 | .string_literal, |
| 1700 | .RParen, | 1708 | .r_paren, |
| 1701 | .Semicolon, | 1709 | .semicolon, |
| 1702 | }); | 1710 | }); |
| 1703 | } | 1711 | } |
| 1704 | 1712 | ||
| 1705 | test "tokenizer - multiline string literal with literal tab" { | 1713 | test "tokenizer - multiline string literal with literal tab" { |
| 1706 | testTokenize( | 1714 | testTokenize( |
| 1707 | \\\\foo	bar | 1715 | \\\\foo	bar |
| 1708 | , &[_]Token.Id{ | 1716 | , &.{ |
| 1709 | .MultilineStringLiteralLine, | 1717 | .multiline_string_literal_line, |
| 1710 | }); | 1718 | }); |
| 1711 | } | 1719 | } |
| 1712 | 1720 | ||
| ... | @@ -1718,32 +1726,30 @@ test "tokenizer - comments with literal tab" { | ... | @@ -1718,32 +1726,30 @@ test "tokenizer - comments with literal tab" { |
| 1718 | \\//	foo | 1726 | \\//	foo |
| 1719 | \\///	foo | 1727 | \\///	foo |
| 1720 | \\///	/foo | 1728 | \\///	/foo |
| 1721 | , &[_]Token.Id{ | 1729 | , &.{ |
| 1722 | .LineComment, | 1730 | .container_doc_comment, |
| 1723 | .ContainerDocComment, | 1731 | .doc_comment, |
| 1724 | .DocComment, | 1732 | .doc_comment, |
| 1725 | .LineComment, | 1733 | .doc_comment, |
| 1726 | .DocComment, | ||
| 1727 | .DocComment, | ||
| 1728 | }); | 1734 | }); |
| 1729 | } | 1735 | } |
| 1730 | 1736 | ||
| 1731 | test "tokenizer - pipe and then invalid" { | 1737 | test "tokenizer - pipe and then invalid" { |
| 1732 | testTokenize("||=", &[_]Token.Id{ | 1738 | testTokenize("||=", &.{ |
| 1733 | .PipePipe, | 1739 | .pipe_pipe, |
| 1734 | .Equal, | 1740 | .equal, |
| 1735 | }); | 1741 | }); |
| 1736 | } | 1742 | } |
| 1737 | 1743 | ||
| 1738 | test "tokenizer - line comment and doc comment" { | 1744 | test "tokenizer - line comment and doc comment" { |
| 1739 | testTokenize("//", &[_]Token.Id{.LineComment}); | 1745 | testTokenize("//", &.{}); |
| 1740 | testTokenize("// a / b", &[_]Token.Id{.LineComment}); | 1746 | testTokenize("// a / b", &.{}); |
| 1741 | testTokenize("// /", &[_]Token.Id{.LineComment}); | 1747 | testTokenize("// /", &.{}); |
| 1742 | testTokenize("/// a", &[_]Token.Id{.DocComment}); | 1748 | testTokenize("/// a", &.{.doc_comment}); |
| 1743 | testTokenize("///", &[_]Token.Id{.DocComment}); | 1749 | testTokenize("///", &.{.doc_comment}); |
| 1744 | testTokenize("////", &[_]Token.Id{.LineComment}); | 1750 | testTokenize("////", &.{}); |
| 1745 | testTokenize("//!", &[_]Token.Id{.ContainerDocComment}); | 1751 | testTokenize("//!", &.{.container_doc_comment}); |
| 1746 | testTokenize("//!!", &[_]Token.Id{.ContainerDocComment}); | 1752 | testTokenize("//!!", &.{.container_doc_comment}); |
| 1747 | } | 1753 | } |
| 1748 | 1754 | ||
| 1749 | test "tokenizer - line comment followed by identifier" { | 1755 | test "tokenizer - line comment followed by identifier" { |
| ... | @@ -1751,304 +1757,304 @@ test "tokenizer - line comment followed by identifier" { | ... | @@ -1751,304 +1757,304 @@ test "tokenizer - line comment followed by identifier" { |
| 1751 | \\ Unexpected, | 1757 | \\ Unexpected, |
| 1752 | \\ // another | 1758 | \\ // another |
| 1753 | \\ Another, | 1759 | \\ Another, |
| 1754 | , &[_]Token.Id{ | 1760 | , &.{ |
| 1755 | .Identifier, | 1761 | .identifier, |
| 1756 | .Comma, | 1762 | .comma, |
| 1757 | .LineComment, | 1763 | .identifier, |
| 1758 | .Identifier, | 1764 | .comma, |
| 1759 | .Comma, | ||
| 1760 | }); | 1765 | }); |
| 1761 | } | 1766 | } |
| 1762 | 1767 | ||
| 1763 | test "tokenizer - UTF-8 BOM is recognized and skipped" { | 1768 | test "tokenizer - UTF-8 BOM is recognized and skipped" { |
| 1764 | testTokenize("\xEF\xBB\xBFa;\n", &[_]Token.Id{ | 1769 | testTokenize("\xEF\xBB\xBFa;\n", &.{ |
| 1765 | .Identifier, | 1770 | .identifier, |
| 1766 | .Semicolon, | 1771 | .semicolon, |
| 1767 | }); | 1772 | }); |
| 1768 | } | 1773 | } |
| 1769 | 1774 | ||
| 1770 | test "correctly parse pointer assignment" { | 1775 | test "correctly parse pointer assignment" { |
| 1771 | testTokenize("b.*=3;\n", &[_]Token.Id{ | 1776 | testTokenize("b.*=3;\n", &.{ |
| 1772 | .Identifier, | 1777 | .identifier, |
| 1773 | .PeriodAsterisk, | 1778 | .period_asterisk, |
| 1774 | .Equal, | 1779 | .equal, |
| 1775 | .IntegerLiteral, | 1780 | .integer_literal, |
| 1776 | .Semicolon, | 1781 | .semicolon, |
| 1777 | }); | 1782 | }); |
| 1778 | } | 1783 | } |
| 1779 | 1784 | ||
| 1780 | test "correctly parse pointer dereference followed by asterisk" { | 1785 | test "correctly parse pointer dereference followed by asterisk" { |
| 1781 | testTokenize("\"b\".* ** 10", &[_]Token.Id{ | 1786 | testTokenize("\"b\".* ** 10", &.{ |
| 1782 | .StringLiteral, | 1787 | .string_literal, |
| 1783 | .PeriodAsterisk, | 1788 | .period_asterisk, |
| 1784 | .AsteriskAsterisk, | 1789 | .asterisk_asterisk, |
| 1785 | .IntegerLiteral, | 1790 | .integer_literal, |
| 1786 | }); | 1791 | }); |
| 1787 | 1792 | ||
| 1788 | testTokenize("(\"b\".*)** 10", &[_]Token.Id{ | 1793 | testTokenize("(\"b\".*)** 10", &.{ |
| 1789 | .LParen, | 1794 | .l_paren, |
| 1790 | .StringLiteral, | 1795 | .string_literal, |
| 1791 | .PeriodAsterisk, | 1796 | .period_asterisk, |
| 1792 | .RParen, | 1797 | .r_paren, |
| 1793 | .AsteriskAsterisk, | 1798 | .asterisk_asterisk, |
| 1794 | .IntegerLiteral, | 1799 | .integer_literal, |
| 1795 | }); | 1800 | }); |
| 1796 | 1801 | ||
| 1797 | testTokenize("\"b\".*** 10", &[_]Token.Id{ | 1802 | testTokenize("\"b\".*** 10", &.{ |
| 1798 | .StringLiteral, | 1803 | .string_literal, |
| 1799 | .Invalid_periodasterisks, | 1804 | .invalid_periodasterisks, |
| 1800 | .AsteriskAsterisk, | 1805 | .asterisk_asterisk, |
| 1801 | .IntegerLiteral, | 1806 | .integer_literal, |
| 1802 | }); | 1807 | }); |
| 1803 | } | 1808 | } |
| 1804 | 1809 | ||
| 1805 | test "tokenizer - range literals" { | 1810 | test "tokenizer - range literals" { |
| 1806 | testTokenize("0...9", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral }); | 1811 | testTokenize("0...9", &.{ .integer_literal, .ellipsis3, .integer_literal }); |
| 1807 | testTokenize("'0'...'9'", &[_]Token.Id{ .CharLiteral, .Ellipsis3, .CharLiteral }); | 1812 | testTokenize("'0'...'9'", &.{ .char_literal, .ellipsis3, .char_literal }); |
| 1808 | testTokenize("0x00...0x09", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral }); | 1813 | testTokenize("0x00...0x09", &.{ .integer_literal, .ellipsis3, .integer_literal }); |
| 1809 | testTokenize("0b00...0b11", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral }); | 1814 | testTokenize("0b00...0b11", &.{ .integer_literal, .ellipsis3, .integer_literal }); |
| 1810 | testTokenize("0o00...0o11", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral }); | 1815 | testTokenize("0o00...0o11", &.{ .integer_literal, .ellipsis3, .integer_literal }); |
| 1811 | } | 1816 | } |
| 1812 | 1817 | ||
| 1813 | test "tokenizer - number literals decimal" { | 1818 | test "tokenizer - number literals decimal" { |
| 1814 | testTokenize("0", &[_]Token.Id{.IntegerLiteral}); | 1819 | testTokenize("0", &.{.integer_literal}); |
| 1815 | testTokenize("1", &[_]Token.Id{.IntegerLiteral}); | 1820 | testTokenize("1", &.{.integer_literal}); |
| 1816 | testTokenize("2", &[_]Token.Id{.IntegerLiteral}); | 1821 | testTokenize("2", &.{.integer_literal}); |
| 1817 | testTokenize("3", &[_]Token.Id{.IntegerLiteral}); | 1822 | testTokenize("3", &.{.integer_literal}); |
| 1818 | testTokenize("4", &[_]Token.Id{.IntegerLiteral}); | 1823 | testTokenize("4", &.{.integer_literal}); |
| 1819 | testTokenize("5", &[_]Token.Id{.IntegerLiteral}); | 1824 | testTokenize("5", &.{.integer_literal}); |
| 1820 | testTokenize("6", &[_]Token.Id{.IntegerLiteral}); | 1825 | testTokenize("6", &.{.integer_literal}); |
| 1821 | testTokenize("7", &[_]Token.Id{.IntegerLiteral}); | 1826 | testTokenize("7", &.{.integer_literal}); |
| 1822 | testTokenize("8", &[_]Token.Id{.IntegerLiteral}); | 1827 | testTokenize("8", &.{.integer_literal}); |
| 1823 | testTokenize("9", &[_]Token.Id{.IntegerLiteral}); | 1828 | testTokenize("9", &.{.integer_literal}); |
| 1824 | testTokenize("1..", &[_]Token.Id{ .IntegerLiteral, .Ellipsis2 }); | 1829 | testTokenize("1..", &.{ .integer_literal, .ellipsis2 }); |
| 1825 | testTokenize("0a", &[_]Token.Id{ .Invalid, .Identifier }); | 1830 | testTokenize("0a", &.{ .invalid, .identifier }); |
| 1826 | testTokenize("9b", &[_]Token.Id{ .Invalid, .Identifier }); | 1831 | testTokenize("9b", &.{ .invalid, .identifier }); |
| 1827 | testTokenize("1z", &[_]Token.Id{ .Invalid, .Identifier }); | 1832 | testTokenize("1z", &.{ .invalid, .identifier }); |
| 1828 | testTokenize("1z_1", &[_]Token.Id{ .Invalid, .Identifier }); | 1833 | testTokenize("1z_1", &.{ .invalid, .identifier }); |
| 1829 | testTokenize("9z3", &[_]Token.Id{ .Invalid, .Identifier }); | 1834 | testTokenize("9z3", &.{ .invalid, .identifier }); |
| 1830 | 1835 | ||
| 1831 | testTokenize("0_0", &[_]Token.Id{.IntegerLiteral}); | 1836 | testTokenize("0_0", &.{.integer_literal}); |
| 1832 | testTokenize("0001", &[_]Token.Id{.IntegerLiteral}); | 1837 | testTokenize("0001", &.{.integer_literal}); |
| 1833 | testTokenize("01234567890", &[_]Token.Id{.IntegerLiteral}); | 1838 | testTokenize("01234567890", &.{.integer_literal}); |
| 1834 | testTokenize("012_345_6789_0", &[_]Token.Id{.IntegerLiteral}); | 1839 | testTokenize("012_345_6789_0", &.{.integer_literal}); |
| 1835 | testTokenize("0_1_2_3_4_5_6_7_8_9_0", &[_]Token.Id{.IntegerLiteral}); | 1840 | testTokenize("0_1_2_3_4_5_6_7_8_9_0", &.{.integer_literal}); |
| 1836 | 1841 | ||
| 1837 | testTokenize("00_", &[_]Token.Id{.Invalid}); | 1842 | testTokenize("00_", &.{.invalid}); |
| 1838 | testTokenize("0_0_", &[_]Token.Id{.Invalid}); | 1843 | testTokenize("0_0_", &.{.invalid}); |
| 1839 | testTokenize("0__0", &[_]Token.Id{ .Invalid, .Identifier }); | 1844 | testTokenize("0__0", &.{ .invalid, .identifier }); |
| 1840 | testTokenize("0_0f", &[_]Token.Id{ .Invalid, .Identifier }); | 1845 | testTokenize("0_0f", &.{ .invalid, .identifier }); |
| 1841 | testTokenize("0_0_f", &[_]Token.Id{ .Invalid, .Identifier }); | 1846 | testTokenize("0_0_f", &.{ .invalid, .identifier }); |
| 1842 | testTokenize("0_0_f_00", &[_]Token.Id{ .Invalid, .Identifier }); | 1847 | testTokenize("0_0_f_00", &.{ .invalid, .identifier }); |
| 1843 | testTokenize("1_,", &[_]Token.Id{ .Invalid, .Comma }); | 1848 | testTokenize("1_,", &.{ .invalid, .comma }); |
| 1844 | 1849 | ||
| 1845 | testTokenize("1.", &[_]Token.Id{.FloatLiteral}); | 1850 | testTokenize("1.", &.{.float_literal}); |
| 1846 | testTokenize("0.0", &[_]Token.Id{.FloatLiteral}); | 1851 | testTokenize("0.0", &.{.float_literal}); |
| 1847 | testTokenize("1.0", &[_]Token.Id{.FloatLiteral}); | 1852 | testTokenize("1.0", &.{.float_literal}); |
| 1848 | testTokenize("10.0", &[_]Token.Id{.FloatLiteral}); | 1853 | testTokenize("10.0", &.{.float_literal}); |
| 1849 | testTokenize("0e0", &[_]Token.Id{.FloatLiteral}); | 1854 | testTokenize("0e0", &.{.float_literal}); |
| 1850 | testTokenize("1e0", &[_]Token.Id{.FloatLiteral}); | 1855 | testTokenize("1e0", &.{.float_literal}); |
| 1851 | testTokenize("1e100", &[_]Token.Id{.FloatLiteral}); | 1856 | testTokenize("1e100", &.{.float_literal}); |
| 1852 | testTokenize("1.e100", &[_]Token.Id{.FloatLiteral}); | 1857 | testTokenize("1.e100", &.{.float_literal}); |
| 1853 | testTokenize("1.0e100", &[_]Token.Id{.FloatLiteral}); | 1858 | testTokenize("1.0e100", &.{.float_literal}); |
| 1854 | testTokenize("1.0e+100", &[_]Token.Id{.FloatLiteral}); | 1859 | testTokenize("1.0e+100", &.{.float_literal}); |
| 1855 | testTokenize("1.0e-100", &[_]Token.Id{.FloatLiteral}); | 1860 | testTokenize("1.0e-100", &.{.float_literal}); |
| 1856 | testTokenize("1_0_0_0.0_0_0_0_0_1e1_0_0_0", &[_]Token.Id{.FloatLiteral}); | 1861 | testTokenize("1_0_0_0.0_0_0_0_0_1e1_0_0_0", &.{.float_literal}); |
| 1857 | testTokenize("1.+", &[_]Token.Id{ .FloatLiteral, .Plus }); | 1862 | testTokenize("1.+", &.{ .float_literal, .plus }); |
| 1858 | 1863 | ||
| 1859 | testTokenize("1e", &[_]Token.Id{.Invalid}); | 1864 | testTokenize("1e", &.{.invalid}); |
| 1860 | testTokenize("1.0e1f0", &[_]Token.Id{ .Invalid, .Identifier }); | 1865 | testTokenize("1.0e1f0", &.{ .invalid, .identifier }); |
| 1861 | testTokenize("1.0p100", &[_]Token.Id{ .Invalid, .Identifier }); | 1866 | testTokenize("1.0p100", &.{ .invalid, .identifier }); |
| 1862 | testTokenize("1.0p-100", &[_]Token.Id{ .Invalid, .Identifier, .Minus, .IntegerLiteral }); | 1867 | testTokenize("1.0p-100", &.{ .invalid, .identifier, .minus, .integer_literal }); |
| 1863 | testTokenize("1.0p1f0", &[_]Token.Id{ .Invalid, .Identifier }); | 1868 | testTokenize("1.0p1f0", &.{ .invalid, .identifier }); |
| 1864 | testTokenize("1.0_,", &[_]Token.Id{ .Invalid, .Comma }); | 1869 | testTokenize("1.0_,", &.{ .invalid, .comma }); |
| 1865 | testTokenize("1_.0", &[_]Token.Id{ .Invalid, .Period, .IntegerLiteral }); | 1870 | testTokenize("1_.0", &.{ .invalid, .period, .integer_literal }); |
| 1866 | testTokenize("1._", &[_]Token.Id{ .Invalid, .Identifier }); | 1871 | testTokenize("1._", &.{ .invalid, .identifier }); |
| 1867 | testTokenize("1.a", &[_]Token.Id{ .Invalid, .Identifier }); | 1872 | testTokenize("1.a", &.{ .invalid, .identifier }); |
| 1868 | testTokenize("1.z", &[_]Token.Id{ .Invalid, .Identifier }); | 1873 | testTokenize("1.z", &.{ .invalid, .identifier }); |
| 1869 | testTokenize("1._0", &[_]Token.Id{ .Invalid, .Identifier }); | 1874 | testTokenize("1._0", &.{ .invalid, .identifier }); |
| 1870 | testTokenize("1._+", &[_]Token.Id{ .Invalid, .Identifier, .Plus }); | 1875 | testTokenize("1._+", &.{ .invalid, .identifier, .plus }); |
| 1871 | testTokenize("1._e", &[_]Token.Id{ .Invalid, .Identifier }); | 1876 | testTokenize("1._e", &.{ .invalid, .identifier }); |
| 1872 | testTokenize("1.0e", &[_]Token.Id{.Invalid}); | 1877 | testTokenize("1.0e", &.{.invalid}); |
| 1873 | testTokenize("1.0e,", &[_]Token.Id{ .Invalid, .Comma }); | 1878 | testTokenize("1.0e,", &.{ .invalid, .comma }); |
| 1874 | testTokenize("1.0e_", &[_]Token.Id{ .Invalid, .Identifier }); | 1879 | testTokenize("1.0e_", &.{ .invalid, .identifier }); |
| 1875 | testTokenize("1.0e+_", &[_]Token.Id{ .Invalid, .Identifier }); | 1880 | testTokenize("1.0e+_", &.{ .invalid, .identifier }); |
| 1876 | testTokenize("1.0e-_", &[_]Token.Id{ .Invalid, .Identifier }); | 1881 | testTokenize("1.0e-_", &.{ .invalid, .identifier }); |
| 1877 | testTokenize("1.0e0_+", &[_]Token.Id{ .Invalid, .Plus }); | 1882 | testTokenize("1.0e0_+", &.{ .invalid, .plus }); |
| 1878 | } | 1883 | } |
| 1879 | 1884 | ||
| 1880 | test "tokenizer - number literals binary" { | 1885 | test "tokenizer - number literals binary" { |
| 1881 | testTokenize("0b0", &[_]Token.Id{.IntegerLiteral}); | 1886 | testTokenize("0b0", &.{.integer_literal}); |
| 1882 | testTokenize("0b1", &[_]Token.Id{.IntegerLiteral}); | 1887 | testTokenize("0b1", &.{.integer_literal}); |
| 1883 | testTokenize("0b2", &[_]Token.Id{ .Invalid, .IntegerLiteral }); | 1888 | testTokenize("0b2", &.{ .invalid, .integer_literal }); |
| 1884 | testTokenize("0b3", &[_]Token.Id{ .Invalid, .IntegerLiteral }); | 1889 | testTokenize("0b3", &.{ .invalid, .integer_literal }); |
| 1885 | testTokenize("0b4", &[_]Token.Id{ .Invalid, .IntegerLiteral }); | 1890 | testTokenize("0b4", &.{ .invalid, .integer_literal }); |
| 1886 | testTokenize("0b5", &[_]Token.Id{ .Invalid, .IntegerLiteral }); | 1891 | testTokenize("0b5", &.{ .invalid, .integer_literal }); |
| 1887 | testTokenize("0b6", &[_]Token.Id{ .Invalid, .IntegerLiteral }); | 1892 | testTokenize("0b6", &.{ .invalid, .integer_literal }); |
| 1888 | testTokenize("0b7", &[_]Token.Id{ .Invalid, .IntegerLiteral }); | 1893 | testTokenize("0b7", &.{ .invalid, .integer_literal }); |
| 1889 | testTokenize("0b8", &[_]Token.Id{ .Invalid, .IntegerLiteral }); | 1894 | testTokenize("0b8", &.{ .invalid, .integer_literal }); |
| 1890 | testTokenize("0b9", &[_]Token.Id{ .Invalid, .IntegerLiteral }); | 1895 | testTokenize("0b9", &.{ .invalid, .integer_literal }); |
| 1891 | testTokenize("0ba", &[_]Token.Id{ .Invalid, .Identifier }); | 1896 | testTokenize("0ba", &.{ .invalid, .identifier }); |
| 1892 | testTokenize("0bb", &[_]Token.Id{ .Invalid, .Identifier }); | 1897 | testTokenize("0bb", &.{ .invalid, .identifier }); |
| 1893 | testTokenize("0bc", &[_]Token.Id{ .Invalid, .Identifier }); | 1898 | testTokenize("0bc", &.{ .invalid, .identifier }); |
| 1894 | testTokenize("0bd", &[_]Token.Id{ .Invalid, .Identifier }); | 1899 | testTokenize("0bd", &.{ .invalid, .identifier }); |
| 1895 | testTokenize("0be", &[_]Token.Id{ .Invalid, .Identifier }); | 1900 | testTokenize("0be", &.{ .invalid, .identifier }); |
| 1896 | testTokenize("0bf", &[_]Token.Id{ .Invalid, .Identifier }); | 1901 | testTokenize("0bf", &.{ .invalid, .identifier }); |
| 1897 | testTokenize("0bz", &[_]Token.Id{ .Invalid, .Identifier }); | 1902 | testTokenize("0bz", &.{ .invalid, .identifier }); |
| 1898 | 1903 | ||
| 1899 | testTokenize("0b0000_0000", &[_]Token.Id{.IntegerLiteral}); | 1904 | testTokenize("0b0000_0000", &.{.integer_literal}); |
| 1900 | testTokenize("0b1111_1111", &[_]Token.Id{.IntegerLiteral}); | 1905 | testTokenize("0b1111_1111", &.{.integer_literal}); |
| 1901 | testTokenize("0b10_10_10_10", &[_]Token.Id{.IntegerLiteral}); | 1906 | testTokenize("0b10_10_10_10", &.{.integer_literal}); |
| 1902 | testTokenize("0b0_1_0_1_0_1_0_1", &[_]Token.Id{.IntegerLiteral}); | 1907 | testTokenize("0b0_1_0_1_0_1_0_1", &.{.integer_literal}); |
| 1903 | testTokenize("0b1.", &[_]Token.Id{ .IntegerLiteral, .Period }); | 1908 | testTokenize("0b1.", &.{ .integer_literal, .period }); |
| 1904 | testTokenize("0b1.0", &[_]Token.Id{ .IntegerLiteral, .Period, .IntegerLiteral }); | 1909 | testTokenize("0b1.0", &.{ .integer_literal, .period, .integer_literal }); |
| 1905 | 1910 | ||
| 1906 | testTokenize("0B0", &[_]Token.Id{ .Invalid, .Identifier }); | 1911 | testTokenize("0B0", &.{ .invalid, .identifier }); |
| 1907 | testTokenize("0b_", &[_]Token.Id{ .Invalid, .Identifier }); | 1912 | testTokenize("0b_", &.{ .invalid, .identifier }); |
| 1908 | testTokenize("0b_0", &[_]Token.Id{ .Invalid, .Identifier }); | 1913 | testTokenize("0b_0", &.{ .invalid, .identifier }); |
| 1909 | testTokenize("0b1_", &[_]Token.Id{.Invalid}); | 1914 | testTokenize("0b1_", &.{.invalid}); |
| 1910 | testTokenize("0b0__1", &[_]Token.Id{ .Invalid, .Identifier }); | 1915 | testTokenize("0b0__1", &.{ .invalid, .identifier }); |
| 1911 | testTokenize("0b0_1_", &[_]Token.Id{.Invalid}); | 1916 | testTokenize("0b0_1_", &.{.invalid}); |
| 1912 | testTokenize("0b1e", &[_]Token.Id{ .Invalid, .Identifier }); | 1917 | testTokenize("0b1e", &.{ .invalid, .identifier }); |
| 1913 | testTokenize("0b1p", &[_]Token.Id{ .Invalid, .Identifier }); | 1918 | testTokenize("0b1p", &.{ .invalid, .identifier }); |
| 1914 | testTokenize("0b1e0", &[_]Token.Id{ .Invalid, .Identifier }); | 1919 | testTokenize("0b1e0", &.{ .invalid, .identifier }); |
| 1915 | testTokenize("0b1p0", &[_]Token.Id{ .Invalid, .Identifier }); | 1920 | testTokenize("0b1p0", &.{ .invalid, .identifier }); |
| 1916 | testTokenize("0b1_,", &[_]Token.Id{ .Invalid, .Comma }); | 1921 | testTokenize("0b1_,", &.{ .invalid, .comma }); |
| 1917 | } | 1922 | } |
| 1918 | 1923 | ||
| 1919 | test "tokenizer - number literals octal" { | 1924 | test "tokenizer - number literals octal" { |
| 1920 | testTokenize("0o0", &[_]Token.Id{.IntegerLiteral}); | 1925 | testTokenize("0o0", &.{.integer_literal}); |
| 1921 | testTokenize("0o1", &[_]Token.Id{.IntegerLiteral}); | 1926 | testTokenize("0o1", &.{.integer_literal}); |
| 1922 | testTokenize("0o2", &[_]Token.Id{.IntegerLiteral}); | 1927 | testTokenize("0o2", &.{.integer_literal}); |
| 1923 | testTokenize("0o3", &[_]Token.Id{.IntegerLiteral}); | 1928 | testTokenize("0o3", &.{.integer_literal}); |
| 1924 | testTokenize("0o4", &[_]Token.Id{.IntegerLiteral}); | 1929 | testTokenize("0o4", &.{.integer_literal}); |
| 1925 | testTokenize("0o5", &[_]Token.Id{.IntegerLiteral}); | 1930 | testTokenize("0o5", &.{.integer_literal}); |
| 1926 | testTokenize("0o6", &[_]Token.Id{.IntegerLiteral}); | 1931 | testTokenize("0o6", &.{.integer_literal}); |
| 1927 | testTokenize("0o7", &[_]Token.Id{.IntegerLiteral}); | 1932 | testTokenize("0o7", &.{.integer_literal}); |
| 1928 | testTokenize("0o8", &[_]Token.Id{ .Invalid, .IntegerLiteral }); | 1933 | testTokenize("0o8", &.{ .invalid, .integer_literal }); |
| 1929 | testTokenize("0o9", &[_]Token.Id{ .Invalid, .IntegerLiteral }); | 1934 | testTokenize("0o9", &.{ .invalid, .integer_literal }); |
| 1930 | testTokenize("0oa", &[_]Token.Id{ .Invalid, .Identifier }); | 1935 | testTokenize("0oa", &.{ .invalid, .identifier }); |
| 1931 | testTokenize("0ob", &[_]Token.Id{ .Invalid, .Identifier }); | 1936 | testTokenize("0ob", &.{ .invalid, .identifier }); |
| 1932 | testTokenize("0oc", &[_]Token.Id{ .Invalid, .Identifier }); | 1937 | testTokenize("0oc", &.{ .invalid, .identifier }); |
| 1933 | testTokenize("0od", &[_]Token.Id{ .Invalid, .Identifier }); | 1938 | testTokenize("0od", &.{ .invalid, .identifier }); |
| 1934 | testTokenize("0oe", &[_]Token.Id{ .Invalid, .Identifier }); | 1939 | testTokenize("0oe", &.{ .invalid, .identifier }); |
| 1935 | testTokenize("0of", &[_]Token.Id{ .Invalid, .Identifier }); | 1940 | testTokenize("0of", &.{ .invalid, .identifier }); |
| 1936 | testTokenize("0oz", &[_]Token.Id{ .Invalid, .Identifier }); | 1941 | testTokenize("0oz", &.{ .invalid, .identifier }); |
| 1937 | 1942 | ||
| 1938 | testTokenize("0o01234567", &[_]Token.Id{.IntegerLiteral}); | 1943 | testTokenize("0o01234567", &.{.integer_literal}); |
| 1939 | testTokenize("0o0123_4567", &[_]Token.Id{.IntegerLiteral}); | 1944 | testTokenize("0o0123_4567", &.{.integer_literal}); |
| 1940 | testTokenize("0o01_23_45_67", &[_]Token.Id{.IntegerLiteral}); | 1945 | testTokenize("0o01_23_45_67", &.{.integer_literal}); |
| 1941 | testTokenize("0o0_1_2_3_4_5_6_7", &[_]Token.Id{.IntegerLiteral}); | 1946 | testTokenize("0o0_1_2_3_4_5_6_7", &.{.integer_literal}); |
| 1942 | testTokenize("0o7.", &[_]Token.Id{ .IntegerLiteral, .Period }); | 1947 | testTokenize("0o7.", &.{ .integer_literal, .period }); |
| 1943 | testTokenize("0o7.0", &[_]Token.Id{ .IntegerLiteral, .Period, .IntegerLiteral }); | 1948 | testTokenize("0o7.0", &.{ .integer_literal, .period, .integer_literal }); |
| 1944 | 1949 | ||
| 1945 | testTokenize("0O0", &[_]Token.Id{ .Invalid, .Identifier }); | 1950 | testTokenize("0O0", &.{ .invalid, .identifier }); |
| 1946 | testTokenize("0o_", &[_]Token.Id{ .Invalid, .Identifier }); | 1951 | testTokenize("0o_", &.{ .invalid, .identifier }); |
| 1947 | testTokenize("0o_0", &[_]Token.Id{ .Invalid, .Identifier }); | 1952 | testTokenize("0o_0", &.{ .invalid, .identifier }); |
| 1948 | testTokenize("0o1_", &[_]Token.Id{.Invalid}); | 1953 | testTokenize("0o1_", &.{.invalid}); |
| 1949 | testTokenize("0o0__1", &[_]Token.Id{ .Invalid, .Identifier }); | 1954 | testTokenize("0o0__1", &.{ .invalid, .identifier }); |
| 1950 | testTokenize("0o0_1_", &[_]Token.Id{.Invalid}); | 1955 | testTokenize("0o0_1_", &.{.invalid}); |
| 1951 | testTokenize("0o1e", &[_]Token.Id{ .Invalid, .Identifier }); | 1956 | testTokenize("0o1e", &.{ .invalid, .identifier }); |
| 1952 | testTokenize("0o1p", &[_]Token.Id{ .Invalid, .Identifier }); | 1957 | testTokenize("0o1p", &.{ .invalid, .identifier }); |
| 1953 | testTokenize("0o1e0", &[_]Token.Id{ .Invalid, .Identifier }); | 1958 | testTokenize("0o1e0", &.{ .invalid, .identifier }); |
| 1954 | testTokenize("0o1p0", &[_]Token.Id{ .Invalid, .Identifier }); | 1959 | testTokenize("0o1p0", &.{ .invalid, .identifier }); |
| 1955 | testTokenize("0o_,", &[_]Token.Id{ .Invalid, .Identifier, .Comma }); | 1960 | testTokenize("0o_,", &.{ .invalid, .identifier, .comma }); |
| 1956 | } | 1961 | } |
| 1957 | 1962 | ||
| 1958 | test "tokenizer - number literals hexadeciaml" { | 1963 | test "tokenizer - number literals hexadeciaml" { |
| 1959 | testTokenize("0x0", &[_]Token.Id{.IntegerLiteral}); | 1964 | testTokenize("0x0", &.{.integer_literal}); |
| 1960 | testTokenize("0x1", &[_]Token.Id{.IntegerLiteral}); | 1965 | testTokenize("0x1", &.{.integer_literal}); |
| 1961 | testTokenize("0x2", &[_]Token.Id{.IntegerLiteral}); | 1966 | testTokenize("0x2", &.{.integer_literal}); |
| 1962 | testTokenize("0x3", &[_]Token.Id{.IntegerLiteral}); | 1967 | testTokenize("0x3", &.{.integer_literal}); |
| 1963 | testTokenize("0x4", &[_]Token.Id{.IntegerLiteral}); | 1968 | testTokenize("0x4", &.{.integer_literal}); |
| 1964 | testTokenize("0x5", &[_]Token.Id{.IntegerLiteral}); | 1969 | testTokenize("0x5", &.{.integer_literal}); |
| 1965 | testTokenize("0x6", &[_]Token.Id{.IntegerLiteral}); | 1970 | testTokenize("0x6", &.{.integer_literal}); |
| 1966 | testTokenize("0x7", &[_]Token.Id{.IntegerLiteral}); | 1971 | testTokenize("0x7", &.{.integer_literal}); |
| 1967 | testTokenize("0x8", &[_]Token.Id{.IntegerLiteral}); | 1972 | testTokenize("0x8", &.{.integer_literal}); |
| 1968 | testTokenize("0x9", &[_]Token.Id{.IntegerLiteral}); | 1973 | testTokenize("0x9", &.{.integer_literal}); |
| 1969 | testTokenize("0xa", &[_]Token.Id{.IntegerLiteral}); | 1974 | testTokenize("0xa", &.{.integer_literal}); |
| 1970 | testTokenize("0xb", &[_]Token.Id{.IntegerLiteral}); | 1975 | testTokenize("0xb", &.{.integer_literal}); |
| 1971 | testTokenize("0xc", &[_]Token.Id{.IntegerLiteral}); | 1976 | testTokenize("0xc", &.{.integer_literal}); |
| 1972 | testTokenize("0xd", &[_]Token.Id{.IntegerLiteral}); | 1977 | testTokenize("0xd", &.{.integer_literal}); |
| 1973 | testTokenize("0xe", &[_]Token.Id{.IntegerLiteral}); | 1978 | testTokenize("0xe", &.{.integer_literal}); |
| 1974 | testTokenize("0xf", &[_]Token.Id{.IntegerLiteral}); | 1979 | testTokenize("0xf", &.{.integer_literal}); |
| 1975 | testTokenize("0xA", &[_]Token.Id{.IntegerLiteral}); | 1980 | testTokenize("0xA", &.{.integer_literal}); |
| 1976 | testTokenize("0xB", &[_]Token.Id{.IntegerLiteral}); | 1981 | testTokenize("0xB", &.{.integer_literal}); |
| 1977 | testTokenize("0xC", &[_]Token.Id{.IntegerLiteral}); | 1982 | testTokenize("0xC", &.{.integer_literal}); |
| 1978 | testTokenize("0xD", &[_]Token.Id{.IntegerLiteral}); | 1983 | testTokenize("0xD", &.{.integer_literal}); |
| 1979 | testTokenize("0xE", &[_]Token.Id{.IntegerLiteral}); | 1984 | testTokenize("0xE", &.{.integer_literal}); |
| 1980 | testTokenize("0xF", &[_]Token.Id{.IntegerLiteral}); | 1985 | testTokenize("0xF", &.{.integer_literal}); |
| 1981 | testTokenize("0x0z", &[_]Token.Id{ .Invalid, .Identifier }); | 1986 | testTokenize("0x0z", &.{ .invalid, .identifier }); |
| 1982 | testTokenize("0xz", &[_]Token.Id{ .Invalid, .Identifier }); | 1987 | testTokenize("0xz", &.{ .invalid, .identifier }); |
| 1983 | 1988 | ||
| 1984 | testTokenize("0x0123456789ABCDEF", &[_]Token.Id{.IntegerLiteral}); | 1989 | testTokenize("0x0123456789ABCDEF", &.{.integer_literal}); |
| 1985 | testTokenize("0x0123_4567_89AB_CDEF", &[_]Token.Id{.IntegerLiteral}); | 1990 | testTokenize("0x0123_4567_89AB_CDEF", &.{.integer_literal}); |
| 1986 | testTokenize("0x01_23_45_67_89AB_CDE_F", &[_]Token.Id{.IntegerLiteral}); | 1991 | testTokenize("0x01_23_45_67_89AB_CDE_F", &.{.integer_literal}); |
| 1987 | testTokenize("0x0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F", &[_]Token.Id{.IntegerLiteral}); | 1992 | testTokenize("0x0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F", &.{.integer_literal}); |
| 1988 | 1993 | ||
| 1989 | testTokenize("0X0", &[_]Token.Id{ .Invalid, .Identifier }); | 1994 | testTokenize("0X0", &.{ .invalid, .identifier }); |
| 1990 | testTokenize("0x_", &[_]Token.Id{ .Invalid, .Identifier }); | 1995 | testTokenize("0x_", &.{ .invalid, .identifier }); |
| 1991 | testTokenize("0x_1", &[_]Token.Id{ .Invalid, .Identifier }); | 1996 | testTokenize("0x_1", &.{ .invalid, .identifier }); |
| 1992 | testTokenize("0x1_", &[_]Token.Id{.Invalid}); | 1997 | testTokenize("0x1_", &.{.invalid}); |
| 1993 | testTokenize("0x0__1", &[_]Token.Id{ .Invalid, .Identifier }); | 1998 | testTokenize("0x0__1", &.{ .invalid, .identifier }); |
| 1994 | testTokenize("0x0_1_", &[_]Token.Id{.Invalid}); | 1999 | testTokenize("0x0_1_", &.{.invalid}); |
| 1995 | testTokenize("0x_,", &[_]Token.Id{ .Invalid, .Identifier, .Comma }); | 2000 | testTokenize("0x_,", &.{ .invalid, .identifier, .comma }); |
| 1996 | 2001 | ||
| 1997 | testTokenize("0x1.", &[_]Token.Id{.FloatLiteral}); | 2002 | testTokenize("0x1.", &.{.float_literal}); |
| 1998 | testTokenize("0x1.0", &[_]Token.Id{.FloatLiteral}); | 2003 | testTokenize("0x1.0", &.{.float_literal}); |
| 1999 | testTokenize("0xF.", &[_]Token.Id{.FloatLiteral}); | 2004 | testTokenize("0xF.", &.{.float_literal}); |
| 2000 | testTokenize("0xF.0", &[_]Token.Id{.FloatLiteral}); | 2005 | testTokenize("0xF.0", &.{.float_literal}); |
| 2001 | testTokenize("0xF.F", &[_]Token.Id{.FloatLiteral}); | 2006 | testTokenize("0xF.F", &.{.float_literal}); |
| 2002 | testTokenize("0xF.Fp0", &[_]Token.Id{.FloatLiteral}); | 2007 | testTokenize("0xF.Fp0", &.{.float_literal}); |
| 2003 | testTokenize("0xF.FP0", &[_]Token.Id{.FloatLiteral}); | 2008 | testTokenize("0xF.FP0", &.{.float_literal}); |
| 2004 | testTokenize("0x1p0", &[_]Token.Id{.FloatLiteral}); | 2009 | testTokenize("0x1p0", &.{.float_literal}); |
| 2005 | testTokenize("0xfp0", &[_]Token.Id{.FloatLiteral}); | 2010 | testTokenize("0xfp0", &.{.float_literal}); |
| 2006 | testTokenize("0x1.+0xF.", &[_]Token.Id{ .FloatLiteral, .Plus, .FloatLiteral }); | 2011 | testTokenize("0x1.+0xF.", &.{ .float_literal, .plus, .float_literal }); |
| 2007 | 2012 | ||
| 2008 | testTokenize("0x0123456.789ABCDEF", &[_]Token.Id{.FloatLiteral}); | 2013 | testTokenize("0x0123456.789ABCDEF", &.{.float_literal}); |
| 2009 | testTokenize("0x0_123_456.789_ABC_DEF", &[_]Token.Id{.FloatLiteral}); | 2014 | testTokenize("0x0_123_456.789_ABC_DEF", &.{.float_literal}); |
| 2010 | testTokenize("0x0_1_2_3_4_5_6.7_8_9_A_B_C_D_E_F", &[_]Token.Id{.FloatLiteral}); | 2015 | testTokenize("0x0_1_2_3_4_5_6.7_8_9_A_B_C_D_E_F", &.{.float_literal}); |
| 2011 | testTokenize("0x0p0", &[_]Token.Id{.FloatLiteral}); | 2016 | testTokenize("0x0p0", &.{.float_literal}); |
| 2012 | testTokenize("0x0.0p0", &[_]Token.Id{.FloatLiteral}); | 2017 | testTokenize("0x0.0p0", &.{.float_literal}); |
| 2013 | testTokenize("0xff.ffp10", &[_]Token.Id{.FloatLiteral}); | 2018 | testTokenize("0xff.ffp10", &.{.float_literal}); |
| 2014 | testTokenize("0xff.ffP10", &[_]Token.Id{.FloatLiteral}); | 2019 | testTokenize("0xff.ffP10", &.{.float_literal}); |
| 2015 | testTokenize("0xff.p10", &[_]Token.Id{.FloatLiteral}); | 2020 | testTokenize("0xff.p10", &.{.float_literal}); |
| 2016 | testTokenize("0xffp10", &[_]Token.Id{.FloatLiteral}); | 2021 | testTokenize("0xffp10", &.{.float_literal}); |
| 2017 | testTokenize("0xff_ff.ff_ffp1_0_0_0", &[_]Token.Id{.FloatLiteral}); | 2022 | testTokenize("0xff_ff.ff_ffp1_0_0_0", &.{.float_literal}); |
| 2018 | testTokenize("0xf_f_f_f.f_f_f_fp+1_000", &[_]Token.Id{.FloatLiteral}); | 2023 | testTokenize("0xf_f_f_f.f_f_f_fp+1_000", &.{.float_literal}); |
| 2019 | testTokenize("0xf_f_f_f.f_f_f_fp-1_00_0", &[_]Token.Id{.FloatLiteral}); | 2024 | testTokenize("0xf_f_f_f.f_f_f_fp-1_00_0", &.{.float_literal}); |
| 2020 | 2025 | ||
| 2021 | testTokenize("0x1e", &[_]Token.Id{.IntegerLiteral}); | 2026 | testTokenize("0x1e", &.{.integer_literal}); |
| 2022 | testTokenize("0x1e0", &[_]Token.Id{.IntegerLiteral}); | 2027 | testTokenize("0x1e0", &.{.integer_literal}); |
| 2023 | testTokenize("0x1p", &[_]Token.Id{.Invalid}); | 2028 | testTokenize("0x1p", &.{.invalid}); |
| 2024 | testTokenize("0xfp0z1", &[_]Token.Id{ .Invalid, .Identifier }); | 2029 | testTokenize("0xfp0z1", &.{ .invalid, .identifier }); |
| 2025 | testTokenize("0xff.ffpff", &[_]Token.Id{ .Invalid, .Identifier }); | 2030 | testTokenize("0xff.ffpff", &.{ .invalid, .identifier }); |
| 2026 | testTokenize("0x0.p", &[_]Token.Id{.Invalid}); | 2031 | testTokenize("0x0.p", &.{.invalid}); |
| 2027 | testTokenize("0x0.z", &[_]Token.Id{ .Invalid, .Identifier }); | 2032 | testTokenize("0x0.z", &.{ .invalid, .identifier }); |
| 2028 | testTokenize("0x0._", &[_]Token.Id{ .Invalid, .Identifier }); | 2033 | testTokenize("0x0._", &.{ .invalid, .identifier }); |
| 2029 | testTokenize("0x0_.0", &[_]Token.Id{ .Invalid, .Period, .IntegerLiteral }); | 2034 | testTokenize("0x0_.0", &.{ .invalid, .period, .integer_literal }); |
| 2030 | testTokenize("0x0_.0.0", &[_]Token.Id{ .Invalid, .Period, .FloatLiteral }); | 2035 | testTokenize("0x0_.0.0", &.{ .invalid, .period, .float_literal }); |
| 2031 | testTokenize("0x0._0", &[_]Token.Id{ .Invalid, .Identifier }); | 2036 | testTokenize("0x0._0", &.{ .invalid, .identifier }); |
| 2032 | testTokenize("0x0.0_", &[_]Token.Id{.Invalid}); | 2037 | testTokenize("0x0.0_", &.{.invalid}); |
| 2033 | testTokenize("0x0_p0", &[_]Token.Id{ .Invalid, .Identifier }); | 2038 | testTokenize("0x0_p0", &.{ .invalid, .identifier }); |
| 2034 | testTokenize("0x0_.p0", &[_]Token.Id{ .Invalid, .Period, .Identifier }); | 2039 | testTokenize("0x0_.p0", &.{ .invalid, .period, .identifier }); |
| 2035 | testTokenize("0x0._p0", &[_]Token.Id{ .Invalid, .Identifier }); | 2040 | testTokenize("0x0._p0", &.{ .invalid, .identifier }); |
| 2036 | testTokenize("0x0.0_p0", &[_]Token.Id{ .Invalid, .Identifier }); | 2041 | testTokenize("0x0.0_p0", &.{ .invalid, .identifier }); |
| 2037 | testTokenize("0x0._0p0", &[_]Token.Id{ .Invalid, .Identifier }); | 2042 | testTokenize("0x0._0p0", &.{ .invalid, .identifier }); |
| 2038 | testTokenize("0x0.0p_0", &[_]Token.Id{ .Invalid, .Identifier }); | 2043 | testTokenize("0x0.0p_0", &.{ .invalid, .identifier }); |
| 2039 | testTokenize("0x0.0p+_0", &[_]Token.Id{ .Invalid, .Identifier }); | 2044 | testTokenize("0x0.0p+_0", &.{ .invalid, .identifier }); |
| 2040 | testTokenize("0x0.0p-_0", &[_]Token.Id{ .Invalid, .Identifier }); | 2045 | testTokenize("0x0.0p-_0", &.{ .invalid, .identifier }); |
| 2041 | testTokenize("0x0.0p0_", &[_]Token.Id{ .Invalid, .Eof }); | 2046 | testTokenize("0x0.0p0_", &.{ .invalid, .eof }); |
| 2042 | } | 2047 | } |
| 2043 | 2048 | ||
| 2044 | fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void { | 2049 | fn testTokenize(source: []const u8, expected_tokens: []const Token.Tag) void { |
| 2045 | var tokenizer = Tokenizer.init(source); | 2050 | var tokenizer = Tokenizer.init(source); |
| 2046 | for (expected_tokens) |expected_token_id| { | 2051 | for (expected_tokens) |expected_token_id| { |
| 2047 | const token = tokenizer.next(); | 2052 | const token = tokenizer.next(); |
| 2048 | if (token.id != expected_token_id) { | 2053 | if (token.tag != expected_token_id) { |
| 2049 | std.debug.panic("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.id) }); | 2054 | std.debug.panic("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.tag) }); |
| 2050 | } | 2055 | } |
| 2051 | } | 2056 | } |
| 2052 | const last_token = tokenizer.next(); | 2057 | const last_token = tokenizer.next(); |
| 2053 | std.testing.expect(last_token.id == .Eof); | 2058 | std.testing.expect(last_token.tag == .eof); |
| 2059 | std.testing.expect(last_token.loc.start == source.len); | ||
| 2054 | } | 2060 | } |
src/BuiltinFn.zig created+844| ... | @@ -0,0 +1,844 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub const Tag = enum { | ||
| 4 | add_with_overflow, | ||
| 5 | align_cast, | ||
| 6 | align_of, | ||
| 7 | as, | ||
| 8 | async_call, | ||
| 9 | atomic_load, | ||
| 10 | atomic_rmw, | ||
| 11 | atomic_store, | ||
| 12 | bit_cast, | ||
| 13 | bit_offset_of, | ||
| 14 | bool_to_int, | ||
| 15 | bit_size_of, | ||
| 16 | breakpoint, | ||
| 17 | mul_add, | ||
| 18 | byte_swap, | ||
| 19 | bit_reverse, | ||
| 20 | byte_offset_of, | ||
| 21 | call, | ||
| 22 | c_define, | ||
| 23 | c_import, | ||
| 24 | c_include, | ||
| 25 | clz, | ||
| 26 | cmpxchg_strong, | ||
| 27 | cmpxchg_weak, | ||
| 28 | compile_error, | ||
| 29 | compile_log, | ||
| 30 | ctz, | ||
| 31 | c_undef, | ||
| 32 | div_exact, | ||
| 33 | div_floor, | ||
| 34 | div_trunc, | ||
| 35 | embed_file, | ||
| 36 | enum_to_int, | ||
| 37 | error_name, | ||
| 38 | error_return_trace, | ||
| 39 | error_to_int, | ||
| 40 | err_set_cast, | ||
| 41 | @"export", | ||
| 42 | fence, | ||
| 43 | field, | ||
| 44 | field_parent_ptr, | ||
| 45 | float_cast, | ||
| 46 | float_to_int, | ||
| 47 | frame, | ||
| 48 | Frame, | ||
| 49 | frame_address, | ||
| 50 | frame_size, | ||
| 51 | has_decl, | ||
| 52 | has_field, | ||
| 53 | import, | ||
| 54 | int_cast, | ||
| 55 | int_to_enum, | ||
| 56 | int_to_error, | ||
| 57 | int_to_float, | ||
| 58 | int_to_ptr, | ||
| 59 | memcpy, | ||
| 60 | memset, | ||
| 61 | wasm_memory_size, | ||
| 62 | wasm_memory_grow, | ||
| 63 | mod, | ||
| 64 | mul_with_overflow, | ||
| 65 | panic, | ||
| 66 | pop_count, | ||
| 67 | ptr_cast, | ||
| 68 | ptr_to_int, | ||
| 69 | rem, | ||
| 70 | return_address, | ||
| 71 | set_align_stack, | ||
| 72 | set_cold, | ||
| 73 | set_eval_branch_quota, | ||
| 74 | set_float_mode, | ||
| 75 | set_runtime_safety, | ||
| 76 | shl_exact, | ||
| 77 | shl_with_overflow, | ||
| 78 | shr_exact, | ||
| 79 | shuffle, | ||
| 80 | size_of, | ||
| 81 | splat, | ||
| 82 | reduce, | ||
| 83 | src, | ||
| 84 | sqrt, | ||
| 85 | sin, | ||
| 86 | cos, | ||
| 87 | exp, | ||
| 88 | exp2, | ||
| 89 | log, | ||
| 90 | log2, | ||
| 91 | log10, | ||
| 92 | fabs, | ||
| 93 | floor, | ||
| 94 | ceil, | ||
| 95 | trunc, | ||
| 96 | round, | ||
| 97 | sub_with_overflow, | ||
| 98 | tag_name, | ||
| 99 | This, | ||
| 100 | truncate, | ||
| 101 | Type, | ||
| 102 | type_info, | ||
| 103 | type_name, | ||
| 104 | TypeOf, | ||
| 105 | union_init, | ||
| 106 | }; | ||
| 107 | |||
| 108 | tag: Tag, | ||
| 109 | |||
| 110 | /// `true` if the builtin call can take advantage of a result location pointer. | ||
| 111 | needs_mem_loc: bool = false, | ||
| 112 | /// `true` if the builtin call can be the left-hand side of an expression (assigned to). | ||
| 113 | allows_lvalue: bool = false, | ||
| 114 | /// The number of parameters to this builtin function. `null` means variable number | ||
| 115 | /// of parameters. | ||
| 116 | param_count: ?u8, | ||
| 117 | |||
| 118 | pub const list = list: { | ||
| 119 | @setEvalBranchQuota(3000); | ||
| 120 | break :list std.ComptimeStringMap(@This(), .{ | ||
| 121 | .{ | ||
| 122 | "@addWithOverflow", | ||
| 123 | .{ | ||
| 124 | .tag = .add_with_overflow, | ||
| 125 | .param_count = 4, | ||
| 126 | }, | ||
| 127 | }, | ||
| 128 | .{ | ||
| 129 | "@alignCast", | ||
| 130 | .{ | ||
| 131 | .tag = .align_cast, | ||
| 132 | .param_count = 1, | ||
| 133 | }, | ||
| 134 | }, | ||
| 135 | .{ | ||
| 136 | "@alignOf", | ||
| 137 | .{ | ||
| 138 | .tag = .align_of, | ||
| 139 | .param_count = 1, | ||
| 140 | }, | ||
| 141 | }, | ||
| 142 | .{ | ||
| 143 | "@as", | ||
| 144 | .{ | ||
| 145 | .tag = .as, | ||
| 146 | .needs_mem_loc = true, | ||
| 147 | .param_count = 2, | ||
| 148 | }, | ||
| 149 | }, | ||
| 150 | .{ | ||
| 151 | "@asyncCall", | ||
| 152 | .{ | ||
| 153 | .tag = .async_call, | ||
| 154 | .param_count = null, | ||
| 155 | }, | ||
| 156 | }, | ||
| 157 | .{ | ||
| 158 | "@atomicLoad", | ||
| 159 | .{ | ||
| 160 | .tag = .atomic_load, | ||
| 161 | .param_count = 3, | ||
| 162 | }, | ||
| 163 | }, | ||
| 164 | .{ | ||
| 165 | "@atomicRmw", | ||
| 166 | .{ | ||
| 167 | .tag = .atomic_rmw, | ||
| 168 | .param_count = 5, | ||
| 169 | }, | ||
| 170 | }, | ||
| 171 | .{ | ||
| 172 | "@atomicStore", | ||
| 173 | .{ | ||
| 174 | .tag = .atomic_store, | ||
| 175 | .param_count = 4, | ||
| 176 | }, | ||
| 177 | }, | ||
| 178 | .{ | ||
| 179 | "@bitCast", | ||
| 180 | .{ | ||
| 181 | .tag = .bit_cast, | ||
| 182 | .needs_mem_loc = true, | ||
| 183 | .param_count = 2, | ||
| 184 | }, | ||
| 185 | }, | ||
| 186 | .{ | ||
| 187 | "@bitOffsetOf", | ||
| 188 | .{ | ||
| 189 | .tag = .bit_offset_of, | ||
| 190 | .param_count = 2, | ||
| 191 | }, | ||
| 192 | }, | ||
| 193 | .{ | ||
| 194 | "@boolToInt", | ||
| 195 | .{ | ||
| 196 | .tag = .bool_to_int, | ||
| 197 | .param_count = 1, | ||
| 198 | }, | ||
| 199 | }, | ||
| 200 | .{ | ||
| 201 | "@bitSizeOf", | ||
| 202 | .{ | ||
| 203 | .tag = .bit_size_of, | ||
| 204 | .param_count = 1, | ||
| 205 | }, | ||
| 206 | }, | ||
| 207 | .{ | ||
| 208 | "@breakpoint", | ||
| 209 | .{ | ||
| 210 | .tag = .breakpoint, | ||
| 211 | .param_count = 0, | ||
| 212 | }, | ||
| 213 | }, | ||
| 214 | .{ | ||
| 215 | "@mulAdd", | ||
| 216 | .{ | ||
| 217 | .tag = .mul_add, | ||
| 218 | .param_count = 4, | ||
| 219 | }, | ||
| 220 | }, | ||
| 221 | .{ | ||
| 222 | "@byteSwap", | ||
| 223 | .{ | ||
| 224 | .tag = .byte_swap, | ||
| 225 | .param_count = 2, | ||
| 226 | }, | ||
| 227 | }, | ||
| 228 | .{ | ||
| 229 | "@bitReverse", | ||
| 230 | .{ | ||
| 231 | .tag = .bit_reverse, | ||
| 232 | .param_count = 2, | ||
| 233 | }, | ||
| 234 | }, | ||
| 235 | .{ | ||
| 236 | "@byteOffsetOf", | ||
| 237 | .{ | ||
| 238 | .tag = .byte_offset_of, | ||
| 239 | .param_count = 2, | ||
| 240 | }, | ||
| 241 | }, | ||
| 242 | .{ | ||
| 243 | "@call", | ||
| 244 | .{ | ||
| 245 | .tag = .call, | ||
| 246 | .needs_mem_loc = true, | ||
| 247 | .param_count = 3, | ||
| 248 | }, | ||
| 249 | }, | ||
| 250 | .{ | ||
| 251 | "@cDefine", | ||
| 252 | .{ | ||
| 253 | .tag = .c_define, | ||
| 254 | .param_count = 2, | ||
| 255 | }, | ||
| 256 | }, | ||
| 257 | .{ | ||
| 258 | "@cImport", | ||
| 259 | .{ | ||
| 260 | .tag = .c_import, | ||
| 261 | .param_count = 1, | ||
| 262 | }, | ||
| 263 | }, | ||
| 264 | .{ | ||
| 265 | "@cInclude", | ||
| 266 | .{ | ||
| 267 | .tag = .c_include, | ||
| 268 | .param_count = 1, | ||
| 269 | }, | ||
| 270 | }, | ||
| 271 | .{ | ||
| 272 | "@clz", | ||
| 273 | .{ | ||
| 274 | .tag = .clz, | ||
| 275 | .param_count = 2, | ||
| 276 | }, | ||
| 277 | }, | ||
| 278 | .{ | ||
| 279 | "@cmpxchgStrong", | ||
| 280 | .{ | ||
| 281 | .tag = .cmpxchg_strong, | ||
| 282 | .param_count = 6, | ||
| 283 | }, | ||
| 284 | }, | ||
| 285 | .{ | ||
| 286 | "@cmpxchgWeak", | ||
| 287 | .{ | ||
| 288 | .tag = .cmpxchg_weak, | ||
| 289 | .param_count = 6, | ||
| 290 | }, | ||
| 291 | }, | ||
| 292 | .{ | ||
| 293 | "@compileError", | ||
| 294 | .{ | ||
| 295 | .tag = .compile_error, | ||
| 296 | .param_count = 1, | ||
| 297 | }, | ||
| 298 | }, | ||
| 299 | .{ | ||
| 300 | "@compileLog", | ||
| 301 | .{ | ||
| 302 | .tag = .compile_log, | ||
| 303 | .param_count = null, | ||
| 304 | }, | ||
| 305 | }, | ||
| 306 | .{ | ||
| 307 | "@ctz", | ||
| 308 | .{ | ||
| 309 | .tag = .ctz, | ||
| 310 | .param_count = 2, | ||
| 311 | }, | ||
| 312 | }, | ||
| 313 | .{ | ||
| 314 | "@cUndef", | ||
| 315 | .{ | ||
| 316 | .tag = .c_undef, | ||
| 317 | .param_count = 1, | ||
| 318 | }, | ||
| 319 | }, | ||
| 320 | .{ | ||
| 321 | "@divExact", | ||
| 322 | .{ | ||
| 323 | .tag = .div_exact, | ||
| 324 | .param_count = 2, | ||
| 325 | }, | ||
| 326 | }, | ||
| 327 | .{ | ||
| 328 | "@divFloor", | ||
| 329 | .{ | ||
| 330 | .tag = .div_floor, | ||
| 331 | .param_count = 2, | ||
| 332 | }, | ||
| 333 | }, | ||
| 334 | .{ | ||
| 335 | "@divTrunc", | ||
| 336 | .{ | ||
| 337 | .tag = .div_trunc, | ||
| 338 | .param_count = 2, | ||
| 339 | }, | ||
| 340 | }, | ||
| 341 | .{ | ||
| 342 | "@embedFile", | ||
| 343 | .{ | ||
| 344 | .tag = .embed_file, | ||
| 345 | .param_count = 1, | ||
| 346 | }, | ||
| 347 | }, | ||
| 348 | .{ | ||
| 349 | "@enumToInt", | ||
| 350 | .{ | ||
| 351 | .tag = .enum_to_int, | ||
| 352 | .param_count = 1, | ||
| 353 | }, | ||
| 354 | }, | ||
| 355 | .{ | ||
| 356 | "@errorName", | ||
| 357 | .{ | ||
| 358 | .tag = .error_name, | ||
| 359 | .param_count = 1, | ||
| 360 | }, | ||
| 361 | }, | ||
| 362 | .{ | ||
| 363 | "@errorReturnTrace", | ||
| 364 | .{ | ||
| 365 | .tag = .error_return_trace, | ||
| 366 | .param_count = 0, | ||
| 367 | }, | ||
| 368 | }, | ||
| 369 | .{ | ||
| 370 | "@errorToInt", | ||
| 371 | .{ | ||
| 372 | .tag = .error_to_int, | ||
| 373 | .param_count = 1, | ||
| 374 | }, | ||
| 375 | }, | ||
| 376 | .{ | ||
| 377 | "@errSetCast", | ||
| 378 | .{ | ||
| 379 | .tag = .err_set_cast, | ||
| 380 | .param_count = 2, | ||
| 381 | }, | ||
| 382 | }, | ||
| 383 | .{ | ||
| 384 | "@export", | ||
| 385 | .{ | ||
| 386 | .tag = .@"export", | ||
| 387 | .param_count = 2, | ||
| 388 | }, | ||
| 389 | }, | ||
| 390 | .{ | ||
| 391 | "@fence", | ||
| 392 | .{ | ||
| 393 | .tag = .fence, | ||
| 394 | .param_count = 0, | ||
| 395 | }, | ||
| 396 | }, | ||
| 397 | .{ | ||
| 398 | "@field", | ||
| 399 | .{ | ||
| 400 | .tag = .field, | ||
| 401 | .needs_mem_loc = true, | ||
| 402 | .param_count = 2, | ||
| 403 | .allows_lvalue = true, | ||
| 404 | }, | ||
| 405 | }, | ||
| 406 | .{ | ||
| 407 | "@fieldParentPtr", | ||
| 408 | .{ | ||
| 409 | .tag = .field_parent_ptr, | ||
| 410 | .param_count = 3, | ||
| 411 | }, | ||
| 412 | }, | ||
| 413 | .{ | ||
| 414 | "@floatCast", | ||
| 415 | .{ | ||
| 416 | .tag = .float_cast, | ||
| 417 | .param_count = 1, | ||
| 418 | }, | ||
| 419 | }, | ||
| 420 | .{ | ||
| 421 | "@floatToInt", | ||
| 422 | .{ | ||
| 423 | .tag = .float_to_int, | ||
| 424 | .param_count = 1, | ||
| 425 | }, | ||
| 426 | }, | ||
| 427 | .{ | ||
| 428 | "@frame", | ||
| 429 | .{ | ||
| 430 | .tag = .frame, | ||
| 431 | .param_count = 0, | ||
| 432 | }, | ||
| 433 | }, | ||
| 434 | .{ | ||
| 435 | "@Frame", | ||
| 436 | .{ | ||
| 437 | .tag = .Frame, | ||
| 438 | .param_count = 1, | ||
| 439 | }, | ||
| 440 | }, | ||
| 441 | .{ | ||
| 442 | "@frameAddress", | ||
| 443 | .{ | ||
| 444 | .tag = .frame_address, | ||
| 445 | .param_count = 0, | ||
| 446 | }, | ||
| 447 | }, | ||
| 448 | .{ | ||
| 449 | "@frameSize", | ||
| 450 | .{ | ||
| 451 | .tag = .frame_size, | ||
| 452 | .param_count = 1, | ||
| 453 | }, | ||
| 454 | }, | ||
| 455 | .{ | ||
| 456 | "@hasDecl", | ||
| 457 | .{ | ||
| 458 | .tag = .has_decl, | ||
| 459 | .param_count = 2, | ||
| 460 | }, | ||
| 461 | }, | ||
| 462 | .{ | ||
| 463 | "@hasField", | ||
| 464 | .{ | ||
| 465 | .tag = .has_field, | ||
| 466 | .param_count = 2, | ||
| 467 | }, | ||
| 468 | }, | ||
| 469 | .{ | ||
| 470 | "@import", | ||
| 471 | .{ | ||
| 472 | .tag = .import, | ||
| 473 | .param_count = 1, | ||
| 474 | }, | ||
| 475 | }, | ||
| 476 | .{ | ||
| 477 | "@intCast", | ||
| 478 | .{ | ||
| 479 | .tag = .int_cast, | ||
| 480 | .param_count = 1, | ||
| 481 | }, | ||
| 482 | }, | ||
| 483 | .{ | ||
| 484 | "@intToEnum", | ||
| 485 | .{ | ||
| 486 | .tag = .int_to_enum, | ||
| 487 | .param_count = 1, | ||
| 488 | }, | ||
| 489 | }, | ||
| 490 | .{ | ||
| 491 | "@intToError", | ||
| 492 | .{ | ||
| 493 | .tag = .int_to_error, | ||
| 494 | .param_count = 1, | ||
| 495 | }, | ||
| 496 | }, | ||
| 497 | .{ | ||
| 498 | "@intToFloat", | ||
| 499 | .{ | ||
| 500 | .tag = .int_to_float, | ||
| 501 | .param_count = 1, | ||
| 502 | }, | ||
| 503 | }, | ||
| 504 | .{ | ||
| 505 | "@intToPtr", | ||
| 506 | .{ | ||
| 507 | .tag = .int_to_ptr, | ||
| 508 | .param_count = 2, | ||
| 509 | }, | ||
| 510 | }, | ||
| 511 | .{ | ||
| 512 | "@memcpy", | ||
| 513 | .{ | ||
| 514 | .tag = .memcpy, | ||
| 515 | .param_count = 3, | ||
| 516 | }, | ||
| 517 | }, | ||
| 518 | .{ | ||
| 519 | "@memset", | ||
| 520 | .{ | ||
| 521 | .tag = .memset, | ||
| 522 | .param_count = 3, | ||
| 523 | }, | ||
| 524 | }, | ||
| 525 | .{ | ||
| 526 | "@wasmMemorySize", | ||
| 527 | .{ | ||
| 528 | .tag = .wasm_memory_size, | ||
| 529 | .param_count = 1, | ||
| 530 | }, | ||
| 531 | }, | ||
| 532 | .{ | ||
| 533 | "@wasmMemoryGrow", | ||
| 534 | .{ | ||
| 535 | .tag = .wasm_memory_grow, | ||
| 536 | .param_count = 2, | ||
| 537 | }, | ||
| 538 | }, | ||
| 539 | .{ | ||
| 540 | "@mod", | ||
| 541 | .{ | ||
| 542 | .tag = .mod, | ||
| 543 | .param_count = 2, | ||
| 544 | }, | ||
| 545 | }, | ||
| 546 | .{ | ||
| 547 | "@mulWithOverflow", | ||
| 548 | .{ | ||
| 549 | .tag = .mul_with_overflow, | ||
| 550 | .param_count = 4, | ||
| 551 | }, | ||
| 552 | }, | ||
| 553 | .{ | ||
| 554 | "@panic", | ||
| 555 | .{ | ||
| 556 | .tag = .panic, | ||
| 557 | .param_count = 1, | ||
| 558 | }, | ||
| 559 | }, | ||
| 560 | .{ | ||
| 561 | "@popCount", | ||
| 562 | .{ | ||
| 563 | .tag = .pop_count, | ||
| 564 | .param_count = 2, | ||
| 565 | }, | ||
| 566 | }, | ||
| 567 | .{ | ||
| 568 | "@ptrCast", | ||
| 569 | .{ | ||
| 570 | .tag = .ptr_cast, | ||
| 571 | .param_count = 2, | ||
| 572 | }, | ||
| 573 | }, | ||
| 574 | .{ | ||
| 575 | "@ptrToInt", | ||
| 576 | .{ | ||
| 577 | .tag = .ptr_to_int, | ||
| 578 | .param_count = 1, | ||
| 579 | }, | ||
| 580 | }, | ||
| 581 | .{ | ||
| 582 | "@rem", | ||
| 583 | .{ | ||
| 584 | .tag = .rem, | ||
| 585 | .param_count = 2, | ||
| 586 | }, | ||
| 587 | }, | ||
| 588 | .{ | ||
| 589 | "@returnAddress", | ||
| 590 | .{ | ||
| 591 | .tag = .return_address, | ||
| 592 | .param_count = 0, | ||
| 593 | }, | ||
| 594 | }, | ||
| 595 | .{ | ||
| 596 | "@setAlignStack", | ||
| 597 | .{ | ||
| 598 | .tag = .set_align_stack, | ||
| 599 | .param_count = 1, | ||
| 600 | }, | ||
| 601 | }, | ||
| 602 | .{ | ||
| 603 | "@setCold", | ||
| 604 | .{ | ||
| 605 | .tag = .set_cold, | ||
| 606 | .param_count = 1, | ||
| 607 | }, | ||
| 608 | }, | ||
| 609 | .{ | ||
| 610 | "@setEvalBranchQuota", | ||
| 611 | .{ | ||
| 612 | .tag = .set_eval_branch_quota, | ||
| 613 | .param_count = 1, | ||
| 614 | }, | ||
| 615 | }, | ||
| 616 | .{ | ||
| 617 | "@setFloatMode", | ||
| 618 | .{ | ||
| 619 | .tag = .set_float_mode, | ||
| 620 | .param_count = 1, | ||
| 621 | }, | ||
| 622 | }, | ||
| 623 | .{ | ||
| 624 | "@setRuntimeSafety", | ||
| 625 | .{ | ||
| 626 | .tag = .set_runtime_safety, | ||
| 627 | .param_count = 1, | ||
| 628 | }, | ||
| 629 | }, | ||
| 630 | .{ | ||
| 631 | "@shlExact", | ||
| 632 | .{ | ||
| 633 | .tag = .shl_exact, | ||
| 634 | .param_count = 2, | ||
| 635 | }, | ||
| 636 | }, | ||
| 637 | .{ | ||
| 638 | "@shlWithOverflow", | ||
| 639 | .{ | ||
| 640 | .tag = .shl_with_overflow, | ||
| 641 | .param_count = 4, | ||
| 642 | }, | ||
| 643 | }, | ||
| 644 | .{ | ||
| 645 | "@shrExact", | ||
| 646 | .{ | ||
| 647 | .tag = .shr_exact, | ||
| 648 | .param_count = 2, | ||
| 649 | }, | ||
| 650 | }, | ||
| 651 | .{ | ||
| 652 | "@shuffle", | ||
| 653 | .{ | ||
| 654 | .tag = .shuffle, | ||
| 655 | .param_count = 4, | ||
| 656 | }, | ||
| 657 | }, | ||
| 658 | .{ | ||
| 659 | "@sizeOf", | ||
| 660 | .{ | ||
| 661 | .tag = .size_of, | ||
| 662 | .param_count = 1, | ||
| 663 | }, | ||
| 664 | }, | ||
| 665 | .{ | ||
| 666 | "@splat", | ||
| 667 | .{ | ||
| 668 | .tag = .splat, | ||
| 669 | .needs_mem_loc = true, | ||
| 670 | .param_count = 2, | ||
| 671 | }, | ||
| 672 | }, | ||
| 673 | .{ | ||
| 674 | "@reduce", | ||
| 675 | .{ | ||
| 676 | .tag = .reduce, | ||
| 677 | .param_count = 2, | ||
| 678 | }, | ||
| 679 | }, | ||
| 680 | .{ | ||
| 681 | "@src", | ||
| 682 | .{ | ||
| 683 | .tag = .src, | ||
| 684 | .needs_mem_loc = true, | ||
| 685 | .param_count = 0, | ||
| 686 | }, | ||
| 687 | }, | ||
| 688 | .{ | ||
| 689 | "@sqrt", | ||
| 690 | .{ | ||
| 691 | .tag = .sqrt, | ||
| 692 | .param_count = 1, | ||
| 693 | }, | ||
| 694 | }, | ||
| 695 | .{ | ||
| 696 | "@sin", | ||
| 697 | .{ | ||
| 698 | .tag = .sin, | ||
| 699 | .param_count = 1, | ||
| 700 | }, | ||
| 701 | }, | ||
| 702 | .{ | ||
| 703 | "@cos", | ||
| 704 | .{ | ||
| 705 | .tag = .cos, | ||
| 706 | .param_count = 1, | ||
| 707 | }, | ||
| 708 | }, | ||
| 709 | .{ | ||
| 710 | "@exp", | ||
| 711 | .{ | ||
| 712 | .tag = .exp, | ||
| 713 | .param_count = 1, | ||
| 714 | }, | ||
| 715 | }, | ||
| 716 | .{ | ||
| 717 | "@exp2", | ||
| 718 | .{ | ||
| 719 | .tag = .exp2, | ||
| 720 | .param_count = 1, | ||
| 721 | }, | ||
| 722 | }, | ||
| 723 | .{ | ||
| 724 | "@log", | ||
| 725 | .{ | ||
| 726 | .tag = .log, | ||
| 727 | .param_count = 1, | ||
| 728 | }, | ||
| 729 | }, | ||
| 730 | .{ | ||
| 731 | "@log2", | ||
| 732 | .{ | ||
| 733 | .tag = .log2, | ||
| 734 | .param_count = 1, | ||
| 735 | }, | ||
| 736 | }, | ||
| 737 | .{ | ||
| 738 | "@log10", | ||
| 739 | .{ | ||
| 740 | .tag = .log10, | ||
| 741 | .param_count = 1, | ||
| 742 | }, | ||
| 743 | }, | ||
| 744 | .{ | ||
| 745 | "@fabs", | ||
| 746 | .{ | ||
| 747 | .tag = .fabs, | ||
| 748 | .param_count = 1, | ||
| 749 | }, | ||
| 750 | }, | ||
| 751 | .{ | ||
| 752 | "@floor", | ||
| 753 | .{ | ||
| 754 | .tag = .floor, | ||
| 755 | .param_count = 1, | ||
| 756 | }, | ||
| 757 | }, | ||
| 758 | .{ | ||
| 759 | "@ceil", | ||
| 760 | .{ | ||
| 761 | .tag = .ceil, | ||
| 762 | .param_count = 1, | ||
| 763 | }, | ||
| 764 | }, | ||
| 765 | .{ | ||
| 766 | "@trunc", | ||
| 767 | .{ | ||
| 768 | .tag = .trunc, | ||
| 769 | .param_count = 1, | ||
| 770 | }, | ||
| 771 | }, | ||
| 772 | .{ | ||
| 773 | "@round", | ||
| 774 | .{ | ||
| 775 | .tag = .round, | ||
| 776 | .param_count = 1, | ||
| 777 | }, | ||
| 778 | }, | ||
| 779 | .{ | ||
| 780 | "@subWithOverflow", | ||
| 781 | .{ | ||
| 782 | .tag = .sub_with_overflow, | ||
| 783 | .param_count = 4, | ||
| 784 | }, | ||
| 785 | }, | ||
| 786 | .{ | ||
| 787 | "@tagName", | ||
| 788 | .{ | ||
| 789 | .tag = .tag_name, | ||
| 790 | .param_count = 1, | ||
| 791 | }, | ||
| 792 | }, | ||
| 793 | .{ | ||
| 794 | "@This", | ||
| 795 | .{ | ||
| 796 | .tag = .This, | ||
| 797 | .param_count = 0, | ||
| 798 | }, | ||
| 799 | }, | ||
| 800 | .{ | ||
| 801 | "@truncate", | ||
| 802 | .{ | ||
| 803 | .tag = .truncate, | ||
| 804 | .param_count = 2, | ||
| 805 | }, | ||
| 806 | }, | ||
| 807 | .{ | ||
| 808 | "@Type", | ||
| 809 | .{ | ||
| 810 | .tag = .Type, | ||
| 811 | .param_count = 1, | ||
| 812 | }, | ||
| 813 | }, | ||
| 814 | .{ | ||
| 815 | "@typeInfo", | ||
| 816 | .{ | ||
| 817 | .tag = .type_info, | ||
| 818 | .param_count = 1, | ||
| 819 | }, | ||
| 820 | }, | ||
| 821 | .{ | ||
| 822 | "@typeName", | ||
| 823 | .{ | ||
| 824 | .tag = .type_name, | ||
| 825 | .param_count = 1, | ||
| 826 | }, | ||
| 827 | }, | ||
| 828 | .{ | ||
| 829 | "@TypeOf", | ||
| 830 | .{ | ||
| 831 | .tag = .TypeOf, | ||
| 832 | .param_count = null, | ||
| 833 | }, | ||
| 834 | }, | ||
| 835 | .{ | ||
| 836 | "@unionInit", | ||
| 837 | .{ | ||
| 838 | .tag = .union_init, | ||
| 839 | .needs_mem_loc = true, | ||
| 840 | .param_count = 3, | ||
| 841 | }, | ||
| 842 | }, | ||
| 843 | }); | ||
| 844 | }; | ||
src/Compilation.zig+10-9| ... | @@ -921,7 +921,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -921,7 +921,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 921 | // TODO this is duped so it can be freed in Container.deinit | 921 | // TODO this is duped so it can be freed in Container.deinit |
| 922 | .sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path), | 922 | .sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path), |
| 923 | .source = .{ .unloaded = {} }, | 923 | .source = .{ .unloaded = {} }, |
| 924 | .contents = .{ .not_available = {} }, | 924 | .tree = undefined, |
| 925 | .status = .never_loaded, | 925 | .status = .never_loaded, |
| 926 | .pkg = root_pkg, | 926 | .pkg = root_pkg, |
| 927 | .root_container = .{ | 927 | .root_container = .{ |
| ... | @@ -1334,7 +1334,7 @@ pub fn update(self: *Compilation) !void { | ... | @@ -1334,7 +1334,7 @@ pub fn update(self: *Compilation) !void { |
| 1334 | self.c_object_work_queue.writeItemAssumeCapacity(entry.key); | 1334 | self.c_object_work_queue.writeItemAssumeCapacity(entry.key); |
| 1335 | } | 1335 | } |
| 1336 | 1336 | ||
| 1337 | const use_stage1 = build_options.is_stage1 and self.bin_file.options.use_llvm; | 1337 | const use_stage1 = build_options.omit_stage2 or build_options.is_stage1 and self.bin_file.options.use_llvm; |
| 1338 | if (!use_stage1) { | 1338 | if (!use_stage1) { |
| 1339 | if (self.bin_file.options.module) |module| { | 1339 | if (self.bin_file.options.module) |module| { |
| 1340 | module.compile_log_text.shrinkAndFree(module.gpa, 0); | 1340 | module.compile_log_text.shrinkAndFree(module.gpa, 0); |
| ... | @@ -1884,7 +1884,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { | ... | @@ -1884,7 +1884,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { |
| 1884 | const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"}); | 1884 | const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"}); |
| 1885 | const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path); | 1885 | const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path); |
| 1886 | var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{}; | 1886 | var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{}; |
| 1887 | const tree = translate_c.translate( | 1887 | var tree = translate_c.translate( |
| 1888 | comp.gpa, | 1888 | comp.gpa, |
| 1889 | new_argv.ptr, | 1889 | new_argv.ptr, |
| 1890 | new_argv.ptr + new_argv.len, | 1890 | new_argv.ptr + new_argv.len, |
| ... | @@ -1903,7 +1903,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { | ... | @@ -1903,7 +1903,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { |
| 1903 | }; | 1903 | }; |
| 1904 | }, | 1904 | }, |
| 1905 | }; | 1905 | }; |
| 1906 | defer tree.deinit(); | 1906 | defer tree.deinit(comp.gpa); |
| 1907 | 1907 | ||
| 1908 | if (comp.verbose_cimport) { | 1908 | if (comp.verbose_cimport) { |
| 1909 | log.info("C import .d file: {s}", .{out_dep_path}); | 1909 | log.info("C import .d file: {s}", .{out_dep_path}); |
| ... | @@ -1921,9 +1921,10 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { | ... | @@ -1921,9 +1921,10 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { |
| 1921 | var out_zig_file = try o_dir.createFile(cimport_zig_basename, .{}); | 1921 | var out_zig_file = try o_dir.createFile(cimport_zig_basename, .{}); |
| 1922 | defer out_zig_file.close(); | 1922 | defer out_zig_file.close(); |
| 1923 | 1923 | ||
| 1924 | var bos = std.io.bufferedWriter(out_zig_file.writer()); | 1924 | const formatted = try tree.render(comp.gpa); |
| 1925 | _ = try std.zig.render(comp.gpa, bos.writer(), tree); | 1925 | defer comp.gpa.free(formatted); |
| 1926 | try bos.flush(); | 1926 | |
| 1927 | try out_zig_file.writeAll(formatted); | ||
| 1927 | 1928 | ||
| 1928 | man.writeManifest() catch |err| { | 1929 | man.writeManifest() catch |err| { |
| 1929 | log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)}); | 1930 | log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)}); |
| ... | @@ -1936,7 +1937,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { | ... | @@ -1936,7 +1937,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { |
| 1936 | "o", &digest, cimport_zig_basename, | 1937 | "o", &digest, cimport_zig_basename, |
| 1937 | }); | 1938 | }); |
| 1938 | if (comp.verbose_cimport) { | 1939 | if (comp.verbose_cimport) { |
| 1939 | log.info("C import output: {s}\n", .{out_zig_path}); | 1940 | log.info("C import output: {s}", .{out_zig_path}); |
| 1940 | } | 1941 | } |
| 1941 | return CImportResult{ | 1942 | return CImportResult{ |
| 1942 | .out_zig_path = out_zig_path, | 1943 | .out_zig_path = out_zig_path, |
| ... | @@ -3000,7 +3001,7 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void { | ... | @@ -3000,7 +3001,7 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void { |
| 3000 | for (errors.list) |full_err_msg| { | 3001 | for (errors.list) |full_err_msg| { |
| 3001 | switch (full_err_msg) { | 3002 | switch (full_err_msg) { |
| 3002 | .src => |src| { | 3003 | .src => |src| { |
| 3003 | log.err("{s}:{d}:{d}: {s}\n", .{ | 3004 | log.err("{s}:{d}:{d}: {s}", .{ |
| 3004 | src.src_path, | 3005 | src.src_path, |
| 3005 | src.line + 1, | 3006 | src.line + 1, |
| 3006 | src.column + 1, | 3007 | src.column + 1, |
src/Module.zig+977-637| ... | @@ -244,9 +244,9 @@ pub const Decl = struct { | ... | @@ -244,9 +244,9 @@ pub const Decl = struct { |
| 244 | } | 244 | } |
| 245 | 245 | ||
| 246 | pub fn src(self: Decl) usize { | 246 | pub fn src(self: Decl) usize { |
| 247 | const tree = self.container.file_scope.contents.tree; | 247 | const tree = &self.container.file_scope.tree; |
| 248 | const decl_node = tree.root_node.decls()[self.src_index]; | 248 | const decl_node = tree.rootDecls()[self.src_index]; |
| 249 | return tree.token_locs[decl_node.firstToken()].start; | 249 | return tree.tokens.items(.start)[tree.firstToken(decl_node)]; |
| 250 | } | 250 | } |
| 251 | 251 | ||
| 252 | pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash { | 252 | pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash { |
| ... | @@ -428,14 +428,14 @@ pub const Scope = struct { | ... | @@ -428,14 +428,14 @@ pub const Scope = struct { |
| 428 | } | 428 | } |
| 429 | 429 | ||
| 430 | /// Asserts the scope is a child of a File and has an AST tree and returns the tree. | 430 | /// Asserts the scope is a child of a File and has an AST tree and returns the tree. |
| 431 | pub fn tree(self: *Scope) *ast.Tree { | 431 | pub fn tree(self: *Scope) *const ast.Tree { |
| 432 | switch (self.tag) { | 432 | switch (self.tag) { |
| 433 | .file => return self.cast(File).?.contents.tree, | 433 | .file => return &self.cast(File).?.tree, |
| 434 | .block => return self.cast(Block).?.src_decl.container.file_scope.contents.tree, | 434 | .block => return &self.cast(Block).?.src_decl.container.file_scope.tree, |
| 435 | .gen_zir => return self.cast(GenZIR).?.decl.container.file_scope.contents.tree, | 435 | .gen_zir => return &self.cast(GenZIR).?.decl.container.file_scope.tree, |
| 436 | .local_val => return self.cast(LocalVal).?.gen_zir.decl.container.file_scope.contents.tree, | 436 | .local_val => return &self.cast(LocalVal).?.gen_zir.decl.container.file_scope.tree, |
| 437 | .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.contents.tree, | 437 | .local_ptr => return &self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.tree, |
| 438 | .container => return self.cast(Container).?.file_scope.contents.tree, | 438 | .container => return &self.cast(Container).?.file_scope.tree, |
| 439 | } | 439 | } |
| 440 | } | 440 | } |
| 441 | 441 | ||
| ... | @@ -540,6 +540,12 @@ pub const Scope = struct { | ... | @@ -540,6 +540,12 @@ pub const Scope = struct { |
| 540 | pub const File = struct { | 540 | pub const File = struct { |
| 541 | pub const base_tag: Tag = .file; | 541 | pub const base_tag: Tag = .file; |
| 542 | base: Scope = Scope{ .tag = base_tag }, | 542 | base: Scope = Scope{ .tag = base_tag }, |
| 543 | status: enum { | ||
| 544 | never_loaded, | ||
| 545 | unloaded_success, | ||
| 546 | unloaded_parse_failure, | ||
| 547 | loaded_success, | ||
| 548 | }, | ||
| 543 | 549 | ||
| 544 | /// Relative to the owning package's root_src_dir. | 550 | /// Relative to the owning package's root_src_dir. |
| 545 | /// Reference to external memory, not owned by File. | 551 | /// Reference to external memory, not owned by File. |
| ... | @@ -548,16 +554,8 @@ pub const Scope = struct { | ... | @@ -548,16 +554,8 @@ pub const Scope = struct { |
| 548 | unloaded: void, | 554 | unloaded: void, |
| 549 | bytes: [:0]const u8, | 555 | bytes: [:0]const u8, |
| 550 | }, | 556 | }, |
| 551 | contents: union { | 557 | /// Whether this is populated or not depends on `status`. |
| 552 | not_available: void, | 558 | tree: ast.Tree, |
| 553 | tree: *ast.Tree, | ||
| 554 | }, | ||
| 555 | status: enum { | ||
| 556 | never_loaded, | ||
| 557 | unloaded_success, | ||
| 558 | unloaded_parse_failure, | ||
| 559 | loaded_success, | ||
| 560 | }, | ||
| 561 | /// Package that this file is a part of, managed externally. | 559 | /// Package that this file is a part of, managed externally. |
| 562 | pkg: *Package, | 560 | pkg: *Package, |
| 563 | 561 | ||
| ... | @@ -571,7 +569,7 @@ pub const Scope = struct { | ... | @@ -571,7 +569,7 @@ pub const Scope = struct { |
| 571 | => {}, | 569 | => {}, |
| 572 | 570 | ||
| 573 | .loaded_success => { | 571 | .loaded_success => { |
| 574 | self.contents.tree.deinit(); | 572 | self.tree.deinit(gpa); |
| 575 | self.status = .unloaded_success; | 573 | self.status = .unloaded_success; |
| 576 | }, | 574 | }, |
| 577 | } | 575 | } |
| ... | @@ -926,7 +924,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { | ... | @@ -926,7 +924,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 926 | .complete => return, | 924 | .complete => return, |
| 927 | 925 | ||
| 928 | .outdated => blk: { | 926 | .outdated => blk: { |
| 929 | log.debug("re-analyzing {s}\n", .{decl.name}); | 927 | log.debug("re-analyzing {s}", .{decl.name}); |
| 930 | 928 | ||
| 931 | // The exports this Decl performs will be re-discovered, so we remove them here | 929 | // The exports this Decl performs will be re-discovered, so we remove them here |
| 932 | // prior to re-analysis. | 930 | // prior to re-analysis. |
| ... | @@ -950,7 +948,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { | ... | @@ -950,7 +948,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 950 | .unreferenced => false, | 948 | .unreferenced => false, |
| 951 | }; | 949 | }; |
| 952 | 950 | ||
| 953 | const type_changed = mod.astGenAndAnalyzeDecl(decl) catch |err| switch (err) { | 951 | const type_changed = mod.astgenAndSemaDecl(decl) catch |err| switch (err) { |
| 954 | error.OutOfMemory => return error.OutOfMemory, | 952 | error.OutOfMemory => return error.OutOfMemory, |
| 955 | error.AnalysisFail => return error.AnalysisFail, | 953 | error.AnalysisFail => return error.AnalysisFail, |
| 956 | else => { | 954 | else => { |
| ... | @@ -992,141 +990,72 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { | ... | @@ -992,141 +990,72 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 992 | } | 990 | } |
| 993 | } | 991 | } |
| 994 | 992 | ||
| 995 | fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { | 993 | /// Returns `true` if the Decl type changed. |
| 994 | /// Returns `true` if this is the first time analyzing the Decl. | ||
| 995 | /// Returns `false` otherwise. | ||
| 996 | fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { | ||
| 996 | const tracy = trace(@src()); | 997 | const tracy = trace(@src()); |
| 997 | defer tracy.end(); | 998 | defer tracy.end(); |
| 998 | 999 | ||
| 999 | const tree = try self.getAstTree(decl.container.file_scope); | 1000 | const tree = try mod.getAstTree(decl.container.file_scope); |
| 1000 | const ast_node = tree.root_node.decls()[decl.src_index]; | 1001 | const node_tags = tree.nodes.items(.tag); |
| 1001 | switch (ast_node.tag) { | 1002 | const node_datas = tree.nodes.items(.data); |
| 1002 | .FnProto => { | 1003 | const decl_node = tree.rootDecls()[decl.src_index]; |
| 1003 | const fn_proto = ast_node.castTag(.FnProto).?; | 1004 | switch (node_tags[decl_node]) { |
| 1005 | .fn_decl => { | ||
| 1006 | const fn_proto = node_datas[decl_node].lhs; | ||
| 1007 | const body = node_datas[decl_node].rhs; | ||
| 1008 | switch (node_tags[fn_proto]) { | ||
| 1009 | .fn_proto_simple => { | ||
| 1010 | var params: [1]ast.Node.Index = undefined; | ||
| 1011 | return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProtoSimple(&params, fn_proto)); | ||
| 1012 | }, | ||
| 1013 | .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProtoMulti(fn_proto)), | ||
| 1014 | .fn_proto_one => { | ||
| 1015 | var params: [1]ast.Node.Index = undefined; | ||
| 1016 | return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProtoOne(&params, fn_proto)); | ||
| 1017 | }, | ||
| 1018 | .fn_proto => return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProto(fn_proto)), | ||
| 1019 | else => unreachable, | ||
| 1020 | } | ||
| 1021 | }, | ||
| 1022 | .fn_proto_simple => { | ||
| 1023 | var params: [1]ast.Node.Index = undefined; | ||
| 1024 | return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProtoSimple(&params, decl_node)); | ||
| 1025 | }, | ||
| 1026 | .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProtoMulti(decl_node)), | ||
| 1027 | .fn_proto_one => { | ||
| 1028 | var params: [1]ast.Node.Index = undefined; | ||
| 1029 | return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProtoOne(&params, decl_node)); | ||
| 1030 | }, | ||
| 1031 | .fn_proto => return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProto(decl_node)), | ||
| 1004 | 1032 | ||
| 1033 | .global_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.globalVarDecl(decl_node)), | ||
| 1034 | .local_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.localVarDecl(decl_node)), | ||
| 1035 | .simple_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.simpleVarDecl(decl_node)), | ||
| 1036 | .aligned_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.alignedVarDecl(decl_node)), | ||
| 1037 | |||
| 1038 | .@"comptime" => { | ||
| 1005 | decl.analysis = .in_progress; | 1039 | decl.analysis = .in_progress; |
| 1006 | 1040 | ||
| 1007 | // This arena allocator's memory is discarded at the end of this function. It is used | 1041 | // A comptime decl does not store any value so we can just deinit this arena after analysis is done. |
| 1008 | // to determine the type of the function, and hence the type of the decl, which is needed | 1042 | var analysis_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 1009 | // to complete the Decl analysis. | 1043 | defer analysis_arena.deinit(); |
| 1010 | var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | 1044 | var gen_scope: Scope.GenZIR = .{ |
| 1011 | defer fn_type_scope_arena.deinit(); | ||
| 1012 | var fn_type_scope: Scope.GenZIR = .{ | ||
| 1013 | .decl = decl, | 1045 | .decl = decl, |
| 1014 | .arena = &fn_type_scope_arena.allocator, | 1046 | .arena = &analysis_arena.allocator, |
| 1015 | .parent = &decl.container.base, | 1047 | .parent = &decl.container.base, |
| 1016 | .force_comptime = true, | 1048 | .force_comptime = true, |
| 1017 | }; | 1049 | }; |
| 1018 | defer fn_type_scope.instructions.deinit(self.gpa); | 1050 | defer gen_scope.instructions.deinit(mod.gpa); |
| 1019 | |||
| 1020 | decl.is_pub = fn_proto.getVisibToken() != null; | ||
| 1021 | |||
| 1022 | const param_decls = fn_proto.params(); | ||
| 1023 | const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len); | ||
| 1024 | 1051 | ||
| 1025 | const fn_src = tree.token_locs[fn_proto.fn_token].start; | 1052 | const block_expr = node_datas[decl_node].lhs; |
| 1026 | const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{ | 1053 | _ = try astgen.comptimeExpr(mod, &gen_scope.base, .none, block_expr); |
| 1027 | .ty = Type.initTag(.type), | 1054 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { |
| 1028 | .val = Value.initTag(.type_type), | 1055 | zir.dumpZir(mod.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {}; |
| 1029 | }); | ||
| 1030 | const type_type_rl: astgen.ResultLoc = .{ .ty = type_type }; | ||
| 1031 | for (param_decls) |param_decl, i| { | ||
| 1032 | const param_type_node = switch (param_decl.param_type) { | ||
| 1033 | .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}), | ||
| 1034 | .type_expr => |node| node, | ||
| 1035 | }; | ||
| 1036 | param_types[i] = try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node); | ||
| 1037 | } | ||
| 1038 | if (fn_proto.getVarArgsToken()) |var_args_token| { | ||
| 1039 | return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{}); | ||
| 1040 | } | ||
| 1041 | if (fn_proto.getLibName()) |lib_name| blk: { | ||
| 1042 | const lib_name_str = mem.trim(u8, tree.tokenSlice(lib_name.firstToken()), "\""); // TODO: call identifierTokenString | ||
| 1043 | log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str}); | ||
| 1044 | const target = self.comp.getTarget(); | ||
| 1045 | if (target_util.is_libc_lib_name(target, lib_name_str)) { | ||
| 1046 | if (!self.comp.bin_file.options.link_libc) { | ||
| 1047 | return self.failNode( | ||
| 1048 | &fn_type_scope.base, | ||
| 1049 | lib_name, | ||
| 1050 | "dependency on libc must be explicitly specified in the build command", | ||
| 1051 | .{}, | ||
| 1052 | ); | ||
| 1053 | } | ||
| 1054 | break :blk; | ||
| 1055 | } | ||
| 1056 | if (target_util.is_libcpp_lib_name(target, lib_name_str)) { | ||
| 1057 | if (!self.comp.bin_file.options.link_libcpp) { | ||
| 1058 | return self.failNode( | ||
| 1059 | &fn_type_scope.base, | ||
| 1060 | lib_name, | ||
| 1061 | "dependency on libc++ must be explicitly specified in the build command", | ||
| 1062 | .{}, | ||
| 1063 | ); | ||
| 1064 | } | ||
| 1065 | break :blk; | ||
| 1066 | } | ||
| 1067 | if (!target.isWasm() and !self.comp.bin_file.options.pic) { | ||
| 1068 | return self.failNode( | ||
| 1069 | &fn_type_scope.base, | ||
| 1070 | lib_name, | ||
| 1071 | "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", | ||
| 1072 | .{ lib_name, lib_name }, | ||
| 1073 | ); | ||
| 1074 | } | ||
| 1075 | self.comp.stage1AddLinkLib(lib_name_str) catch |err| { | ||
| 1076 | return self.failNode( | ||
| 1077 | &fn_type_scope.base, | ||
| 1078 | lib_name, | ||
| 1079 | "unable to add link lib '{s}': {s}", | ||
| 1080 | .{ lib_name, @errorName(err) }, | ||
| 1081 | ); | ||
| 1082 | }; | ||
| 1083 | } | ||
| 1084 | if (fn_proto.getAlignExpr()) |align_expr| { | ||
| 1085 | return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{}); | ||
| 1086 | } | ||
| 1087 | if (fn_proto.getSectionExpr()) |sect_expr| { | ||
| 1088 | return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{}); | ||
| 1089 | } | 1056 | } |
| 1090 | 1057 | ||
| 1091 | const enum_literal_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{ | 1058 | var inst_table = Scope.Block.InstTable.init(mod.gpa); |
| 1092 | .ty = Type.initTag(.type), | ||
| 1093 | .val = Value.initTag(.enum_literal_type), | ||
| 1094 | }); | ||
| 1095 | const enum_literal_type_rl: astgen.ResultLoc = .{ .ty = enum_literal_type }; | ||
| 1096 | const cc = if (fn_proto.getCallconvExpr()) |callconv_expr| | ||
| 1097 | try astgen.expr(self, &fn_type_scope.base, enum_literal_type_rl, callconv_expr) | ||
| 1098 | else | ||
| 1099 | try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{ | ||
| 1100 | .ty = Type.initTag(.enum_literal), | ||
| 1101 | .val = try Value.Tag.enum_literal.create( | ||
| 1102 | &fn_type_scope_arena.allocator, | ||
| 1103 | try fn_type_scope_arena.allocator.dupe(u8, "Unspecified"), | ||
| 1104 | ), | ||
| 1105 | }); | ||
| 1106 | |||
| 1107 | const return_type_expr = switch (fn_proto.return_type) { | ||
| 1108 | .Explicit => |node| node, | ||
| 1109 | .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}), | ||
| 1110 | .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}), | ||
| 1111 | }; | ||
| 1112 | |||
| 1113 | const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr); | ||
| 1114 | const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{ | ||
| 1115 | .return_type = return_type_inst, | ||
| 1116 | .param_types = param_types, | ||
| 1117 | .cc = cc, | ||
| 1118 | }, .{}); | ||
| 1119 | |||
| 1120 | if (std.builtin.mode == .Debug and self.comp.verbose_ir) { | ||
| 1121 | zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {}; | ||
| 1122 | } | ||
| 1123 | |||
| 1124 | // We need the memory for the Type to go into the arena for the Decl | ||
| 1125 | var decl_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 1126 | errdefer decl_arena.deinit(); | ||
| 1127 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | ||
| 1128 | |||
| 1129 | var inst_table = Scope.Block.InstTable.init(self.gpa); | ||
| 1130 | defer inst_table.deinit(); | 1059 | defer inst_table.deinit(); |
| 1131 | 1060 | ||
| 1132 | var branch_quota: u32 = default_eval_branch_quota; | 1061 | var branch_quota: u32 = default_eval_branch_quota; |
| ... | @@ -1138,424 +1067,627 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { | ... | @@ -1138,424 +1067,627 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1138 | .owner_decl = decl, | 1067 | .owner_decl = decl, |
| 1139 | .src_decl = decl, | 1068 | .src_decl = decl, |
| 1140 | .instructions = .{}, | 1069 | .instructions = .{}, |
| 1141 | .arena = &decl_arena.allocator, | 1070 | .arena = &analysis_arena.allocator, |
| 1142 | .inlining = null, | 1071 | .inlining = null, |
| 1143 | .is_comptime = false, | 1072 | .is_comptime = true, |
| 1144 | .branch_quota = &branch_quota, | 1073 | .branch_quota = &branch_quota, |
| 1145 | }; | 1074 | }; |
| 1146 | defer block_scope.instructions.deinit(self.gpa); | 1075 | defer block_scope.instructions.deinit(mod.gpa); |
| 1147 | 1076 | ||
| 1148 | const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{ | 1077 | _ = try zir_sema.analyzeBody(mod, &block_scope, .{ |
| 1149 | .instructions = fn_type_scope.instructions.items, | 1078 | .instructions = gen_scope.instructions.items, |
| 1150 | }); | 1079 | }); |
| 1151 | const body_node = fn_proto.getBodyNode() orelse { | ||
| 1152 | // Extern function. | ||
| 1153 | var type_changed = true; | ||
| 1154 | if (decl.typedValueManaged()) |tvm| { | ||
| 1155 | type_changed = !tvm.typed_value.ty.eql(fn_type); | ||
| 1156 | 1080 | ||
| 1157 | tvm.deinit(self.gpa); | 1081 | decl.analysis = .complete; |
| 1158 | } | 1082 | decl.generation = mod.generation; |
| 1159 | const fn_val = try Value.Tag.extern_fn.create(&decl_arena.allocator, decl); | 1083 | return true; |
| 1084 | }, | ||
| 1085 | .@"usingnamespace" => @panic("TODO usingnamespace decl"), | ||
| 1086 | else => unreachable, | ||
| 1087 | } | ||
| 1088 | } | ||
| 1160 | 1089 | ||
| 1161 | decl_arena_state.* = decl_arena.state; | 1090 | fn astgenAndSemaFn( |
| 1162 | decl.typed_value = .{ | 1091 | mod: *Module, |
| 1163 | .most_recent = .{ | 1092 | decl: *Decl, |
| 1164 | .typed_value = .{ .ty = fn_type, .val = fn_val }, | 1093 | tree: ast.Tree, |
| 1165 | .arena = decl_arena_state, | 1094 | body_node: ast.Node.Index, |
| 1166 | }, | 1095 | fn_proto: ast.full.FnProto, |
| 1167 | }; | 1096 | ) !bool { |
| 1168 | decl.analysis = .complete; | 1097 | const tracy = trace(@src()); |
| 1169 | decl.generation = self.generation; | 1098 | defer tracy.end(); |
| 1099 | |||
| 1100 | decl.analysis = .in_progress; | ||
| 1101 | |||
| 1102 | const token_starts = tree.tokens.items(.start); | ||
| 1103 | const token_tags = tree.tokens.items(.tag); | ||
| 1104 | |||
| 1105 | // This arena allocator's memory is discarded at the end of this function. It is used | ||
| 1106 | // to determine the type of the function, and hence the type of the decl, which is needed | ||
| 1107 | // to complete the Decl analysis. | ||
| 1108 | var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); | ||
| 1109 | defer fn_type_scope_arena.deinit(); | ||
| 1110 | var fn_type_scope: Scope.GenZIR = .{ | ||
| 1111 | .decl = decl, | ||
| 1112 | .arena = &fn_type_scope_arena.allocator, | ||
| 1113 | .parent = &decl.container.base, | ||
| 1114 | .force_comptime = true, | ||
| 1115 | }; | ||
| 1116 | defer fn_type_scope.instructions.deinit(mod.gpa); | ||
| 1170 | 1117 | ||
| 1171 | try self.comp.bin_file.allocateDeclIndexes(decl); | 1118 | decl.is_pub = fn_proto.visib_token != null; |
| 1172 | try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); | ||
| 1173 | 1119 | ||
| 1174 | if (type_changed and self.emit_h != null) { | 1120 | // The AST params array does not contain anytype and ... parameters. |
| 1175 | try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); | 1121 | // We must iterate to count how many param types to allocate. |
| 1122 | const param_count = blk: { | ||
| 1123 | var count: usize = 0; | ||
| 1124 | var it = fn_proto.iterate(tree); | ||
| 1125 | while (it.next()) |_| { | ||
| 1126 | count += 1; | ||
| 1127 | } | ||
| 1128 | break :blk count; | ||
| 1129 | }; | ||
| 1130 | const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_count); | ||
| 1131 | const fn_src = token_starts[fn_proto.ast.fn_token]; | ||
| 1132 | const type_type = try astgen.addZIRInstConst(mod, &fn_type_scope.base, fn_src, .{ | ||
| 1133 | .ty = Type.initTag(.type), | ||
| 1134 | .val = Value.initTag(.type_type), | ||
| 1135 | }); | ||
| 1136 | const type_type_rl: astgen.ResultLoc = .{ .ty = type_type }; | ||
| 1137 | |||
| 1138 | { | ||
| 1139 | var param_type_i: usize = 0; | ||
| 1140 | var it = fn_proto.iterate(tree); | ||
| 1141 | while (it.next()) |param| : (param_type_i += 1) { | ||
| 1142 | if (param.anytype_ellipsis3) |token| { | ||
| 1143 | switch (token_tags[token]) { | ||
| 1144 | .keyword_anytype => return mod.failTok( | ||
| 1145 | &fn_type_scope.base, | ||
| 1146 | token, | ||
| 1147 | "TODO implement anytype parameter", | ||
| 1148 | .{}, | ||
| 1149 | ), | ||
| 1150 | .ellipsis3 => return mod.failTok( | ||
| 1151 | &fn_type_scope.base, | ||
| 1152 | token, | ||
| 1153 | "TODO implement var args", | ||
| 1154 | .{}, | ||
| 1155 | ), | ||
| 1156 | else => unreachable, | ||
| 1176 | } | 1157 | } |
| 1158 | } | ||
| 1159 | const param_type_node = param.type_expr; | ||
| 1160 | assert(param_type_node != 0); | ||
| 1161 | param_types[param_type_i] = | ||
| 1162 | try astgen.expr(mod, &fn_type_scope.base, type_type_rl, param_type_node); | ||
| 1163 | } | ||
| 1164 | assert(param_type_i == param_count); | ||
| 1165 | } | ||
| 1166 | if (fn_proto.lib_name) |lib_name_token| blk: { | ||
| 1167 | // TODO call std.zig.parseStringLiteral | ||
| 1168 | const lib_name_str = mem.trim(u8, tree.tokenSlice(lib_name_token), "\""); | ||
| 1169 | log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str}); | ||
| 1170 | const target = mod.comp.getTarget(); | ||
| 1171 | if (target_util.is_libc_lib_name(target, lib_name_str)) { | ||
| 1172 | if (!mod.comp.bin_file.options.link_libc) { | ||
| 1173 | return mod.failTok( | ||
| 1174 | &fn_type_scope.base, | ||
| 1175 | lib_name_token, | ||
| 1176 | "dependency on libc must be explicitly specified in the build command", | ||
| 1177 | .{}, | ||
| 1178 | ); | ||
| 1179 | } | ||
| 1180 | break :blk; | ||
| 1181 | } | ||
| 1182 | if (target_util.is_libcpp_lib_name(target, lib_name_str)) { | ||
| 1183 | if (!mod.comp.bin_file.options.link_libcpp) { | ||
| 1184 | return mod.failTok( | ||
| 1185 | &fn_type_scope.base, | ||
| 1186 | lib_name_token, | ||
| 1187 | "dependency on libc++ must be explicitly specified in the build command", | ||
| 1188 | .{}, | ||
| 1189 | ); | ||
| 1190 | } | ||
| 1191 | break :blk; | ||
| 1192 | } | ||
| 1193 | if (!target.isWasm() and !mod.comp.bin_file.options.pic) { | ||
| 1194 | return mod.failTok( | ||
| 1195 | &fn_type_scope.base, | ||
| 1196 | lib_name_token, | ||
| 1197 | "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", | ||
| 1198 | .{ lib_name_str, lib_name_str }, | ||
| 1199 | ); | ||
| 1200 | } | ||
| 1201 | mod.comp.stage1AddLinkLib(lib_name_str) catch |err| { | ||
| 1202 | return mod.failTok( | ||
| 1203 | &fn_type_scope.base, | ||
| 1204 | lib_name_token, | ||
| 1205 | "unable to add link lib '{s}': {s}", | ||
| 1206 | .{ lib_name_str, @errorName(err) }, | ||
| 1207 | ); | ||
| 1208 | }; | ||
| 1209 | } | ||
| 1210 | if (fn_proto.ast.align_expr != 0) { | ||
| 1211 | return mod.failNode( | ||
| 1212 | &fn_type_scope.base, | ||
| 1213 | fn_proto.ast.align_expr, | ||
| 1214 | "TODO implement function align expression", | ||
| 1215 | .{}, | ||
| 1216 | ); | ||
| 1217 | } | ||
| 1218 | if (fn_proto.ast.section_expr != 0) { | ||
| 1219 | return mod.failNode( | ||
| 1220 | &fn_type_scope.base, | ||
| 1221 | fn_proto.ast.section_expr, | ||
| 1222 | "TODO implement function section expression", | ||
| 1223 | .{}, | ||
| 1224 | ); | ||
| 1225 | } | ||
| 1177 | 1226 | ||
| 1178 | return type_changed; | 1227 | const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; |
| 1179 | }; | 1228 | if (token_tags[maybe_bang] == .bang) { |
| 1229 | return mod.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{}); | ||
| 1230 | } | ||
| 1231 | const return_type_inst = try astgen.expr( | ||
| 1232 | mod, | ||
| 1233 | &fn_type_scope.base, | ||
| 1234 | type_type_rl, | ||
| 1235 | fn_proto.ast.return_type, | ||
| 1236 | ); | ||
| 1237 | const fn_type_inst = if (fn_proto.ast.callconv_expr != 0) cc: { | ||
| 1238 | // TODO instead of enum literal type, this needs to be the | ||
| 1239 | // std.builtin.CallingConvention enum. We need to implement importing other files | ||
| 1240 | // and enums in order to fix this. | ||
| 1241 | const src = token_starts[tree.firstToken(fn_proto.ast.callconv_expr)]; | ||
| 1242 | const enum_lit_ty = try astgen.addZIRInstConst(mod, &fn_type_scope.base, src, .{ | ||
| 1243 | .ty = Type.initTag(.type), | ||
| 1244 | .val = Value.initTag(.enum_literal_type), | ||
| 1245 | }); | ||
| 1246 | const cc = try astgen.comptimeExpr(mod, &fn_type_scope.base, .{ | ||
| 1247 | .ty = enum_lit_ty, | ||
| 1248 | }, fn_proto.ast.callconv_expr); | ||
| 1249 | break :cc try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type_cc, .{ | ||
| 1250 | .return_type = return_type_inst, | ||
| 1251 | .param_types = param_types, | ||
| 1252 | .cc = cc, | ||
| 1253 | }); | ||
| 1254 | } else | ||
| 1255 | try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type, .{ | ||
| 1256 | .return_type = return_type_inst, | ||
| 1257 | .param_types = param_types, | ||
| 1258 | }); | ||
| 1180 | 1259 | ||
| 1181 | const new_func = try decl_arena.allocator.create(Fn); | 1260 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { |
| 1182 | const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); | 1261 | zir.dumpZir(mod.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {}; |
| 1262 | } | ||
| 1183 | 1263 | ||
| 1184 | const fn_zir: zir.Body = blk: { | 1264 | // We need the memory for the Type to go into the arena for the Decl |
| 1185 | // We put the ZIR inside the Decl arena. | 1265 | var decl_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 1186 | var gen_scope: Scope.GenZIR = .{ | 1266 | errdefer decl_arena.deinit(); |
| 1187 | .decl = decl, | 1267 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 1188 | .arena = &decl_arena.allocator, | ||
| 1189 | .parent = &decl.container.base, | ||
| 1190 | .force_comptime = false, | ||
| 1191 | }; | ||
| 1192 | defer gen_scope.instructions.deinit(self.gpa); | ||
| 1193 | |||
| 1194 | // We need an instruction for each parameter, and they must be first in the body. | ||
| 1195 | try gen_scope.instructions.resize(self.gpa, fn_proto.params_len); | ||
| 1196 | var params_scope = &gen_scope.base; | ||
| 1197 | for (fn_proto.params()) |param, i| { | ||
| 1198 | const name_token = param.name_token.?; | ||
| 1199 | const src = tree.token_locs[name_token].start; | ||
| 1200 | const param_name = try self.identifierTokenString(&gen_scope.base, name_token); | ||
| 1201 | const arg = try decl_arena.allocator.create(zir.Inst.Arg); | ||
| 1202 | arg.* = .{ | ||
| 1203 | .base = .{ | ||
| 1204 | .tag = .arg, | ||
| 1205 | .src = src, | ||
| 1206 | }, | ||
| 1207 | .positionals = .{ | ||
| 1208 | .name = param_name, | ||
| 1209 | }, | ||
| 1210 | .kw_args = .{}, | ||
| 1211 | }; | ||
| 1212 | gen_scope.instructions.items[i] = &arg.base; | ||
| 1213 | const sub_scope = try decl_arena.allocator.create(Scope.LocalVal); | ||
| 1214 | sub_scope.* = .{ | ||
| 1215 | .parent = params_scope, | ||
| 1216 | .gen_zir = &gen_scope, | ||
| 1217 | .name = param_name, | ||
| 1218 | .inst = &arg.base, | ||
| 1219 | }; | ||
| 1220 | params_scope = &sub_scope.base; | ||
| 1221 | } | ||
| 1222 | 1268 | ||
| 1223 | const body_block = body_node.cast(ast.Node.Block).?; | 1269 | var inst_table = Scope.Block.InstTable.init(mod.gpa); |
| 1270 | defer inst_table.deinit(); | ||
| 1224 | 1271 | ||
| 1225 | try astgen.blockExpr(self, params_scope, body_block); | 1272 | var branch_quota: u32 = default_eval_branch_quota; |
| 1226 | 1273 | ||
| 1227 | if (gen_scope.instructions.items.len == 0 or | 1274 | var block_scope: Scope.Block = .{ |
| 1228 | !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) | 1275 | .parent = null, |
| 1229 | { | 1276 | .inst_table = &inst_table, |
| 1230 | const src = tree.token_locs[body_block.rbrace].start; | 1277 | .func = null, |
| 1231 | _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .return_void); | 1278 | .owner_decl = decl, |
| 1232 | } | 1279 | .src_decl = decl, |
| 1280 | .instructions = .{}, | ||
| 1281 | .arena = &decl_arena.allocator, | ||
| 1282 | .inlining = null, | ||
| 1283 | .is_comptime = false, | ||
| 1284 | .branch_quota = &branch_quota, | ||
| 1285 | }; | ||
| 1286 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 1233 | 1287 | ||
| 1234 | if (std.builtin.mode == .Debug and self.comp.verbose_ir) { | 1288 | const fn_type = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, fn_type_inst, .{ |
| 1235 | zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {}; | 1289 | .instructions = fn_type_scope.instructions.items, |
| 1236 | } | 1290 | }); |
| 1291 | if (body_node == 0) { | ||
| 1292 | // Extern function. | ||
| 1293 | var type_changed = true; | ||
| 1294 | if (decl.typedValueManaged()) |tvm| { | ||
| 1295 | type_changed = !tvm.typed_value.ty.eql(fn_type); | ||
| 1237 | 1296 | ||
| 1238 | break :blk .{ | 1297 | tvm.deinit(mod.gpa); |
| 1239 | .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), | 1298 | } |
| 1240 | }; | 1299 | const fn_val = try Value.Tag.extern_fn.create(&decl_arena.allocator, decl); |
| 1241 | }; | ||
| 1242 | 1300 | ||
| 1243 | const is_inline = fn_type.fnCallingConvention() == .Inline; | 1301 | decl_arena_state.* = decl_arena.state; |
| 1244 | const anal_state = ([2]Fn.Analysis{ .queued, .inline_only })[@boolToInt(is_inline)]; | 1302 | decl.typed_value = .{ |
| 1303 | .most_recent = .{ | ||
| 1304 | .typed_value = .{ .ty = fn_type, .val = fn_val }, | ||
| 1305 | .arena = decl_arena_state, | ||
| 1306 | }, | ||
| 1307 | }; | ||
| 1308 | decl.analysis = .complete; | ||
| 1309 | decl.generation = mod.generation; | ||
| 1245 | 1310 | ||
| 1246 | new_func.* = .{ | 1311 | try mod.comp.bin_file.allocateDeclIndexes(decl); |
| 1247 | .state = anal_state, | 1312 | try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl }); |
| 1248 | .zir = fn_zir, | ||
| 1249 | .body = undefined, | ||
| 1250 | .owner_decl = decl, | ||
| 1251 | }; | ||
| 1252 | fn_payload.* = .{ | ||
| 1253 | .base = .{ .tag = .function }, | ||
| 1254 | .data = new_func, | ||
| 1255 | }; | ||
| 1256 | 1313 | ||
| 1257 | var prev_type_has_bits = false; | 1314 | if (type_changed and mod.emit_h != null) { |
| 1258 | var prev_is_inline = false; | 1315 | try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); |
| 1259 | var type_changed = true; | 1316 | } |
| 1260 | 1317 | ||
| 1261 | if (decl.typedValueManaged()) |tvm| { | 1318 | return type_changed; |
| 1262 | prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); | 1319 | } |
| 1263 | type_changed = !tvm.typed_value.ty.eql(fn_type); | ||
| 1264 | if (tvm.typed_value.val.castTag(.function)) |payload| { | ||
| 1265 | const prev_func = payload.data; | ||
| 1266 | prev_is_inline = prev_func.state == .inline_only; | ||
| 1267 | } | ||
| 1268 | 1320 | ||
| 1269 | tvm.deinit(self.gpa); | 1321 | const new_func = try decl_arena.allocator.create(Fn); |
| 1270 | } | 1322 | const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); |
| 1271 | 1323 | ||
| 1272 | decl_arena_state.* = decl_arena.state; | 1324 | const fn_zir: zir.Body = blk: { |
| 1273 | decl.typed_value = .{ | 1325 | // We put the ZIR inside the Decl arena. |
| 1274 | .most_recent = .{ | 1326 | var gen_scope: Scope.GenZIR = .{ |
| 1275 | .typed_value = .{ | 1327 | .decl = decl, |
| 1276 | .ty = fn_type, | 1328 | .arena = &decl_arena.allocator, |
| 1277 | .val = Value.initPayload(&fn_payload.base), | 1329 | .parent = &decl.container.base, |
| 1278 | }, | 1330 | .force_comptime = false, |
| 1279 | .arena = decl_arena_state, | 1331 | }; |
| 1332 | defer gen_scope.instructions.deinit(mod.gpa); | ||
| 1333 | |||
| 1334 | // We need an instruction for each parameter, and they must be first in the body. | ||
| 1335 | try gen_scope.instructions.resize(mod.gpa, param_count); | ||
| 1336 | var params_scope = &gen_scope.base; | ||
| 1337 | var i: usize = 0; | ||
| 1338 | var it = fn_proto.iterate(tree); | ||
| 1339 | while (it.next()) |param| : (i += 1) { | ||
| 1340 | const name_token = param.name_token.?; | ||
| 1341 | const src = token_starts[name_token]; | ||
| 1342 | const param_name = try mod.identifierTokenString(&gen_scope.base, name_token); | ||
| 1343 | const arg = try decl_arena.allocator.create(zir.Inst.Arg); | ||
| 1344 | arg.* = .{ | ||
| 1345 | .base = .{ | ||
| 1346 | .tag = .arg, | ||
| 1347 | .src = src, | ||
| 1348 | }, | ||
| 1349 | .positionals = .{ | ||
| 1350 | .name = param_name, | ||
| 1280 | }, | 1351 | }, |
| 1352 | .kw_args = .{}, | ||
| 1281 | }; | 1353 | }; |
| 1282 | decl.analysis = .complete; | 1354 | gen_scope.instructions.items[i] = &arg.base; |
| 1283 | decl.generation = self.generation; | 1355 | const sub_scope = try decl_arena.allocator.create(Scope.LocalVal); |
| 1284 | 1356 | sub_scope.* = .{ | |
| 1285 | if (!is_inline and fn_type.hasCodeGenBits()) { | 1357 | .parent = params_scope, |
| 1286 | // We don't fully codegen the decl until later, but we do need to reserve a global | 1358 | .gen_zir = &gen_scope, |
| 1287 | // offset table index for it. This allows us to codegen decls out of dependency order, | 1359 | .name = param_name, |
| 1288 | // increasing how many computations can be done in parallel. | 1360 | .inst = &arg.base, |
| 1289 | try self.comp.bin_file.allocateDeclIndexes(decl); | 1361 | }; |
| 1290 | try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); | 1362 | params_scope = &sub_scope.base; |
| 1291 | if (type_changed and self.emit_h != null) { | 1363 | } |
| 1292 | try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); | ||
| 1293 | } | ||
| 1294 | } else if (!prev_is_inline and prev_type_has_bits) { | ||
| 1295 | self.comp.bin_file.freeDecl(decl); | ||
| 1296 | } | ||
| 1297 | 1364 | ||
| 1298 | if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { | 1365 | _ = try astgen.expr(mod, params_scope, .none, body_node); |
| 1299 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | ||
| 1300 | if (is_inline) { | ||
| 1301 | return self.failTok( | ||
| 1302 | &block_scope.base, | ||
| 1303 | maybe_export_token, | ||
| 1304 | "export of inline function", | ||
| 1305 | .{}, | ||
| 1306 | ); | ||
| 1307 | } | ||
| 1308 | const export_src = tree.token_locs[maybe_export_token].start; | ||
| 1309 | const name_loc = tree.token_locs[fn_proto.getNameToken().?]; | ||
| 1310 | const name = tree.tokenSliceLoc(name_loc); | ||
| 1311 | // The scope needs to have the decl in it. | ||
| 1312 | try self.analyzeExport(&block_scope.base, export_src, name, decl); | ||
| 1313 | } | ||
| 1314 | } | ||
| 1315 | return type_changed or is_inline != prev_is_inline; | ||
| 1316 | }, | ||
| 1317 | .VarDecl => { | ||
| 1318 | const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node); | ||
| 1319 | 1366 | ||
| 1320 | decl.analysis = .in_progress; | 1367 | if (gen_scope.instructions.items.len == 0 or |
| 1368 | !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) | ||
| 1369 | { | ||
| 1370 | const src = token_starts[tree.lastToken(body_node)]; | ||
| 1371 | _ = try astgen.addZIRNoOp(mod, &gen_scope.base, src, .return_void); | ||
| 1372 | } | ||
| 1321 | 1373 | ||
| 1322 | // We need the memory for the Type to go into the arena for the Decl | 1374 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { |
| 1323 | var decl_arena = std.heap.ArenaAllocator.init(self.gpa); | 1375 | zir.dumpZir(mod.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {}; |
| 1324 | errdefer decl_arena.deinit(); | 1376 | } |
| 1325 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | ||
| 1326 | 1377 | ||
| 1327 | var decl_inst_table = Scope.Block.InstTable.init(self.gpa); | 1378 | break :blk .{ |
| 1328 | defer decl_inst_table.deinit(); | 1379 | .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), |
| 1380 | }; | ||
| 1381 | }; | ||
| 1329 | 1382 | ||
| 1330 | var branch_quota: u32 = default_eval_branch_quota; | 1383 | const is_inline = fn_type.fnCallingConvention() == .Inline; |
| 1384 | const anal_state: Fn.Analysis = if (is_inline) .inline_only else .queued; | ||
| 1331 | 1385 | ||
| 1332 | var block_scope: Scope.Block = .{ | 1386 | new_func.* = .{ |
| 1333 | .parent = null, | 1387 | .state = anal_state, |
| 1334 | .inst_table = &decl_inst_table, | 1388 | .zir = fn_zir, |
| 1335 | .func = null, | 1389 | .body = undefined, |
| 1336 | .owner_decl = decl, | 1390 | .owner_decl = decl, |
| 1337 | .src_decl = decl, | 1391 | }; |
| 1338 | .instructions = .{}, | 1392 | fn_payload.* = .{ |
| 1339 | .arena = &decl_arena.allocator, | 1393 | .base = .{ .tag = .function }, |
| 1340 | .inlining = null, | 1394 | .data = new_func, |
| 1341 | .is_comptime = true, | 1395 | }; |
| 1342 | .branch_quota = &branch_quota, | ||
| 1343 | }; | ||
| 1344 | defer block_scope.instructions.deinit(self.gpa); | ||
| 1345 | |||
| 1346 | decl.is_pub = var_decl.getVisibToken() != null; | ||
| 1347 | const is_extern = blk: { | ||
| 1348 | const maybe_extern_token = var_decl.getExternExportToken() orelse | ||
| 1349 | break :blk false; | ||
| 1350 | if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false; | ||
| 1351 | if (var_decl.getInitNode()) |some| { | ||
| 1352 | return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{}); | ||
| 1353 | } | ||
| 1354 | break :blk true; | ||
| 1355 | }; | ||
| 1356 | if (var_decl.getLibName()) |lib_name| { | ||
| 1357 | assert(is_extern); | ||
| 1358 | return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{}); | ||
| 1359 | } | ||
| 1360 | const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var; | ||
| 1361 | const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: { | ||
| 1362 | if (!is_mutable) { | ||
| 1363 | return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{}); | ||
| 1364 | } | ||
| 1365 | break :blk true; | ||
| 1366 | } else false; | ||
| 1367 | assert(var_decl.getComptimeToken() == null); | ||
| 1368 | if (var_decl.getAlignNode()) |align_expr| { | ||
| 1369 | return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{}); | ||
| 1370 | } | ||
| 1371 | if (var_decl.getSectionNode()) |sect_expr| { | ||
| 1372 | return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{}); | ||
| 1373 | } | ||
| 1374 | 1396 | ||
| 1375 | const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: { | 1397 | var prev_type_has_bits = false; |
| 1376 | var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | 1398 | var prev_is_inline = false; |
| 1377 | defer gen_scope_arena.deinit(); | 1399 | var type_changed = true; |
| 1378 | var gen_scope: Scope.GenZIR = .{ | ||
| 1379 | .decl = decl, | ||
| 1380 | .arena = &gen_scope_arena.allocator, | ||
| 1381 | .parent = &decl.container.base, | ||
| 1382 | .force_comptime = false, | ||
| 1383 | }; | ||
| 1384 | defer gen_scope.instructions.deinit(self.gpa); | ||
| 1385 | 1400 | ||
| 1386 | const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: { | 1401 | if (decl.typedValueManaged()) |tvm| { |
| 1387 | const src = tree.token_locs[type_node.firstToken()].start; | 1402 | prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); |
| 1388 | const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{ | 1403 | type_changed = !tvm.typed_value.ty.eql(fn_type); |
| 1389 | .ty = Type.initTag(.type), | 1404 | if (tvm.typed_value.val.castTag(.function)) |payload| { |
| 1390 | .val = Value.initTag(.type_type), | 1405 | const prev_func = payload.data; |
| 1391 | }); | 1406 | prev_is_inline = prev_func.state == .inline_only; |
| 1392 | const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node); | 1407 | } |
| 1393 | break :rl .{ .ty = var_type }; | ||
| 1394 | } else .none; | ||
| 1395 | 1408 | ||
| 1396 | const init_inst = try astgen.comptimeExpr(self, &gen_scope.base, init_result_loc, init_node); | 1409 | tvm.deinit(mod.gpa); |
| 1397 | if (std.builtin.mode == .Debug and self.comp.verbose_ir) { | 1410 | } |
| 1398 | zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {}; | ||
| 1399 | } | ||
| 1400 | 1411 | ||
| 1401 | var var_inst_table = Scope.Block.InstTable.init(self.gpa); | 1412 | decl_arena_state.* = decl_arena.state; |
| 1402 | defer var_inst_table.deinit(); | 1413 | decl.typed_value = .{ |
| 1403 | 1414 | .most_recent = .{ | |
| 1404 | var branch_quota_vi: u32 = default_eval_branch_quota; | 1415 | .typed_value = .{ |
| 1405 | var inner_block: Scope.Block = .{ | 1416 | .ty = fn_type, |
| 1406 | .parent = null, | 1417 | .val = Value.initPayload(&fn_payload.base), |
| 1407 | .inst_table = &var_inst_table, | 1418 | }, |
| 1408 | .func = null, | 1419 | .arena = decl_arena_state, |
| 1409 | .owner_decl = decl, | 1420 | }, |
| 1410 | .src_decl = decl, | 1421 | }; |
| 1411 | .instructions = .{}, | 1422 | decl.analysis = .complete; |
| 1412 | .arena = &gen_scope_arena.allocator, | 1423 | decl.generation = mod.generation; |
| 1413 | .inlining = null, | 1424 | |
| 1414 | .is_comptime = true, | 1425 | if (!is_inline and fn_type.hasCodeGenBits()) { |
| 1415 | .branch_quota = &branch_quota_vi, | 1426 | // We don't fully codegen the decl until later, but we do need to reserve a global |
| 1416 | }; | 1427 | // offset table index for it. This allows us to codegen decls out of dependency order, |
| 1417 | defer inner_block.instructions.deinit(self.gpa); | 1428 | // increasing how many computations can be done in parallel. |
| 1418 | try zir_sema.analyzeBody(self, &inner_block, .{ | 1429 | try mod.comp.bin_file.allocateDeclIndexes(decl); |
| 1419 | .instructions = gen_scope.instructions.items, | 1430 | try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl }); |
| 1420 | }); | 1431 | if (type_changed and mod.emit_h != null) { |
| 1432 | try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); | ||
| 1433 | } | ||
| 1434 | } else if (!prev_is_inline and prev_type_has_bits) { | ||
| 1435 | mod.comp.bin_file.freeDecl(decl); | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | if (fn_proto.extern_export_token) |maybe_export_token| { | ||
| 1439 | if (token_tags[maybe_export_token] == .keyword_export) { | ||
| 1440 | if (is_inline) { | ||
| 1441 | return mod.failTok( | ||
| 1442 | &block_scope.base, | ||
| 1443 | maybe_export_token, | ||
| 1444 | "export of inline function", | ||
| 1445 | .{}, | ||
| 1446 | ); | ||
| 1447 | } | ||
| 1448 | const export_src = token_starts[maybe_export_token]; | ||
| 1449 | const name = tree.tokenSlice(fn_proto.name_token.?); // TODO identifierTokenString | ||
| 1450 | // The scope needs to have the decl in it. | ||
| 1451 | try mod.analyzeExport(&block_scope.base, export_src, name, decl); | ||
| 1452 | } | ||
| 1453 | } | ||
| 1454 | return type_changed or is_inline != prev_is_inline; | ||
| 1455 | } | ||
| 1421 | 1456 | ||
| 1422 | // The result location guarantees the type coercion. | 1457 | fn astgenAndSemaVarDecl( |
| 1423 | const analyzed_init_inst = var_inst_table.get(init_inst).?; | 1458 | mod: *Module, |
| 1424 | // The is_comptime in the Scope.Block guarantees the result is comptime-known. | 1459 | decl: *Decl, |
| 1425 | const val = analyzed_init_inst.value().?; | 1460 | tree: ast.Tree, |
| 1461 | var_decl: ast.full.VarDecl, | ||
| 1462 | ) !bool { | ||
| 1463 | const tracy = trace(@src()); | ||
| 1464 | defer tracy.end(); | ||
| 1426 | 1465 | ||
| 1427 | const ty = try analyzed_init_inst.ty.copy(block_scope.arena); | 1466 | decl.analysis = .in_progress; |
| 1428 | break :vi .{ | ||
| 1429 | .ty = ty, | ||
| 1430 | .val = try val.copy(block_scope.arena), | ||
| 1431 | }; | ||
| 1432 | } else if (!is_extern) { | ||
| 1433 | return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{}); | ||
| 1434 | } else if (var_decl.getTypeNode()) |type_node| vi: { | ||
| 1435 | // Temporary arena for the zir instructions. | ||
| 1436 | var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 1437 | defer type_scope_arena.deinit(); | ||
| 1438 | var type_scope: Scope.GenZIR = .{ | ||
| 1439 | .decl = decl, | ||
| 1440 | .arena = &type_scope_arena.allocator, | ||
| 1441 | .parent = &decl.container.base, | ||
| 1442 | .force_comptime = true, | ||
| 1443 | }; | ||
| 1444 | defer type_scope.instructions.deinit(self.gpa); | ||
| 1445 | 1467 | ||
| 1446 | const var_type = try astgen.typeExpr(self, &type_scope.base, type_node); | 1468 | const token_starts = tree.tokens.items(.start); |
| 1447 | if (std.builtin.mode == .Debug and self.comp.verbose_ir) { | 1469 | const token_tags = tree.tokens.items(.tag); |
| 1448 | zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {}; | ||
| 1449 | } | ||
| 1450 | 1470 | ||
| 1451 | const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{ | 1471 | // We need the memory for the Type to go into the arena for the Decl |
| 1452 | .instructions = type_scope.instructions.items, | 1472 | var decl_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 1453 | }); | 1473 | errdefer decl_arena.deinit(); |
| 1454 | break :vi .{ | 1474 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 1455 | .ty = ty, | ||
| 1456 | .val = null, | ||
| 1457 | }; | ||
| 1458 | } else { | ||
| 1459 | return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{}); | ||
| 1460 | }; | ||
| 1461 | 1475 | ||
| 1462 | if (is_mutable and !var_info.ty.isValidVarType(is_extern)) { | 1476 | var decl_inst_table = Scope.Block.InstTable.init(mod.gpa); |
| 1463 | return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty}); | 1477 | defer decl_inst_table.deinit(); |
| 1464 | } | ||
| 1465 | 1478 | ||
| 1466 | var type_changed = true; | 1479 | var branch_quota: u32 = default_eval_branch_quota; |
| 1467 | if (decl.typedValueManaged()) |tvm| { | ||
| 1468 | type_changed = !tvm.typed_value.ty.eql(var_info.ty); | ||
| 1469 | 1480 | ||
| 1470 | tvm.deinit(self.gpa); | 1481 | var block_scope: Scope.Block = .{ |
| 1471 | } | 1482 | .parent = null, |
| 1483 | .inst_table = &decl_inst_table, | ||
| 1484 | .func = null, | ||
| 1485 | .owner_decl = decl, | ||
| 1486 | .src_decl = decl, | ||
| 1487 | .instructions = .{}, | ||
| 1488 | .arena = &decl_arena.allocator, | ||
| 1489 | .inlining = null, | ||
| 1490 | .is_comptime = true, | ||
| 1491 | .branch_quota = &branch_quota, | ||
| 1492 | }; | ||
| 1493 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 1494 | |||
| 1495 | decl.is_pub = var_decl.visib_token != null; | ||
| 1496 | const is_extern = blk: { | ||
| 1497 | const maybe_extern_token = var_decl.extern_export_token orelse break :blk false; | ||
| 1498 | if (token_tags[maybe_extern_token] != .keyword_extern) break :blk false; | ||
| 1499 | if (var_decl.ast.init_node != 0) { | ||
| 1500 | return mod.failNode( | ||
| 1501 | &block_scope.base, | ||
| 1502 | var_decl.ast.init_node, | ||
| 1503 | "extern variables have no initializers", | ||
| 1504 | .{}, | ||
| 1505 | ); | ||
| 1506 | } | ||
| 1507 | break :blk true; | ||
| 1508 | }; | ||
| 1509 | if (var_decl.lib_name) |lib_name| { | ||
| 1510 | assert(is_extern); | ||
| 1511 | return mod.failTok(&block_scope.base, lib_name, "TODO implement function library name", .{}); | ||
| 1512 | } | ||
| 1513 | const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var; | ||
| 1514 | const is_threadlocal = if (var_decl.threadlocal_token) |some| blk: { | ||
| 1515 | if (!is_mutable) { | ||
| 1516 | return mod.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{}); | ||
| 1517 | } | ||
| 1518 | break :blk true; | ||
| 1519 | } else false; | ||
| 1520 | assert(var_decl.comptime_token == null); | ||
| 1521 | if (var_decl.ast.align_node != 0) { | ||
| 1522 | return mod.failNode( | ||
| 1523 | &block_scope.base, | ||
| 1524 | var_decl.ast.align_node, | ||
| 1525 | "TODO implement function align expression", | ||
| 1526 | .{}, | ||
| 1527 | ); | ||
| 1528 | } | ||
| 1529 | if (var_decl.ast.section_node != 0) { | ||
| 1530 | return mod.failNode( | ||
| 1531 | &block_scope.base, | ||
| 1532 | var_decl.ast.section_node, | ||
| 1533 | "TODO implement function section expression", | ||
| 1534 | .{}, | ||
| 1535 | ); | ||
| 1536 | } | ||
| 1472 | 1537 | ||
| 1473 | const new_variable = try decl_arena.allocator.create(Var); | 1538 | const var_info: struct { ty: Type, val: ?Value } = if (var_decl.ast.init_node != 0) vi: { |
| 1474 | new_variable.* = .{ | 1539 | var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 1475 | .owner_decl = decl, | 1540 | defer gen_scope_arena.deinit(); |
| 1476 | .init = var_info.val orelse undefined, | 1541 | var gen_scope: Scope.GenZIR = .{ |
| 1477 | .is_extern = is_extern, | 1542 | .decl = decl, |
| 1478 | .is_mutable = is_mutable, | 1543 | .arena = &gen_scope_arena.allocator, |
| 1479 | .is_threadlocal = is_threadlocal, | 1544 | .parent = &decl.container.base, |
| 1480 | }; | 1545 | .force_comptime = true, |
| 1481 | const var_val = try Value.Tag.variable.create(&decl_arena.allocator, new_variable); | 1546 | }; |
| 1482 | 1547 | defer gen_scope.instructions.deinit(mod.gpa); | |
| 1483 | decl_arena_state.* = decl_arena.state; | ||
| 1484 | decl.typed_value = .{ | ||
| 1485 | .most_recent = .{ | ||
| 1486 | .typed_value = .{ | ||
| 1487 | .ty = var_info.ty, | ||
| 1488 | .val = var_val, | ||
| 1489 | }, | ||
| 1490 | .arena = decl_arena_state, | ||
| 1491 | }, | ||
| 1492 | }; | ||
| 1493 | decl.analysis = .complete; | ||
| 1494 | decl.generation = self.generation; | ||
| 1495 | |||
| 1496 | if (var_decl.getExternExportToken()) |maybe_export_token| { | ||
| 1497 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | ||
| 1498 | const export_src = tree.token_locs[maybe_export_token].start; | ||
| 1499 | const name_loc = tree.token_locs[var_decl.name_token]; | ||
| 1500 | const name = tree.tokenSliceLoc(name_loc); | ||
| 1501 | // The scope needs to have the decl in it. | ||
| 1502 | try self.analyzeExport(&block_scope.base, export_src, name, decl); | ||
| 1503 | } | ||
| 1504 | } | ||
| 1505 | return type_changed; | ||
| 1506 | }, | ||
| 1507 | .Comptime => { | ||
| 1508 | const comptime_decl = @fieldParentPtr(ast.Node.Comptime, "base", ast_node); | ||
| 1509 | 1548 | ||
| 1510 | decl.analysis = .in_progress; | 1549 | const init_result_loc: astgen.ResultLoc = if (var_decl.ast.type_node != 0) rl: { |
| 1550 | const type_node = var_decl.ast.type_node; | ||
| 1551 | const src = token_starts[tree.firstToken(type_node)]; | ||
| 1552 | const type_type = try astgen.addZIRInstConst(mod, &gen_scope.base, src, .{ | ||
| 1553 | .ty = Type.initTag(.type), | ||
| 1554 | .val = Value.initTag(.type_type), | ||
| 1555 | }); | ||
| 1556 | const var_type = try astgen.expr(mod, &gen_scope.base, .{ .ty = type_type }, type_node); | ||
| 1557 | break :rl .{ .ty = var_type }; | ||
| 1558 | } else .none; | ||
| 1559 | |||
| 1560 | const init_inst = try astgen.comptimeExpr( | ||
| 1561 | mod, | ||
| 1562 | &gen_scope.base, | ||
| 1563 | init_result_loc, | ||
| 1564 | var_decl.ast.init_node, | ||
| 1565 | ); | ||
| 1566 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { | ||
| 1567 | zir.dumpZir(mod.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {}; | ||
| 1568 | } | ||
| 1511 | 1569 | ||
| 1512 | // A comptime decl does not store any value so we can just deinit | 1570 | var var_inst_table = Scope.Block.InstTable.init(mod.gpa); |
| 1513 | // this arena after analysis is done. | 1571 | defer var_inst_table.deinit(); |
| 1514 | var analysis_arena = std.heap.ArenaAllocator.init(self.gpa); | 1572 | |
| 1515 | defer analysis_arena.deinit(); | 1573 | var branch_quota_vi: u32 = default_eval_branch_quota; |
| 1516 | var gen_scope: Scope.GenZIR = .{ | 1574 | var inner_block: Scope.Block = .{ |
| 1517 | .decl = decl, | 1575 | .parent = null, |
| 1518 | .arena = &analysis_arena.allocator, | 1576 | .inst_table = &var_inst_table, |
| 1519 | .parent = &decl.container.base, | 1577 | .func = null, |
| 1520 | .force_comptime = true, | 1578 | .owner_decl = decl, |
| 1521 | }; | 1579 | .src_decl = decl, |
| 1522 | defer gen_scope.instructions.deinit(self.gpa); | 1580 | .instructions = .{}, |
| 1581 | .arena = &gen_scope_arena.allocator, | ||
| 1582 | .inlining = null, | ||
| 1583 | .is_comptime = true, | ||
| 1584 | .branch_quota = &branch_quota_vi, | ||
| 1585 | }; | ||
| 1586 | defer inner_block.instructions.deinit(mod.gpa); | ||
| 1587 | try zir_sema.analyzeBody(mod, &inner_block, .{ | ||
| 1588 | .instructions = gen_scope.instructions.items, | ||
| 1589 | }); | ||
| 1523 | 1590 | ||
| 1524 | _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr); | 1591 | // The result location guarantees the type coercion. |
| 1525 | if (std.builtin.mode == .Debug and self.comp.verbose_ir) { | 1592 | const analyzed_init_inst = var_inst_table.get(init_inst).?; |
| 1526 | zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {}; | 1593 | // The is_comptime in the Scope.Block guarantees the result is comptime-known. |
| 1527 | } | 1594 | const val = analyzed_init_inst.value().?; |
| 1528 | 1595 | ||
| 1529 | var inst_table = Scope.Block.InstTable.init(self.gpa); | 1596 | const ty = try analyzed_init_inst.ty.copy(block_scope.arena); |
| 1530 | defer inst_table.deinit(); | 1597 | break :vi .{ |
| 1598 | .ty = ty, | ||
| 1599 | .val = try val.copy(block_scope.arena), | ||
| 1600 | }; | ||
| 1601 | } else if (!is_extern) { | ||
| 1602 | return mod.failTok( | ||
| 1603 | &block_scope.base, | ||
| 1604 | var_decl.ast.mut_token, | ||
| 1605 | "variables must be initialized", | ||
| 1606 | .{}, | ||
| 1607 | ); | ||
| 1608 | } else if (var_decl.ast.type_node != 0) vi: { | ||
| 1609 | const type_node = var_decl.ast.type_node; | ||
| 1610 | // Temporary arena for the zir instructions. | ||
| 1611 | var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa); | ||
| 1612 | defer type_scope_arena.deinit(); | ||
| 1613 | var type_scope: Scope.GenZIR = .{ | ||
| 1614 | .decl = decl, | ||
| 1615 | .arena = &type_scope_arena.allocator, | ||
| 1616 | .parent = &decl.container.base, | ||
| 1617 | .force_comptime = true, | ||
| 1618 | }; | ||
| 1619 | defer type_scope.instructions.deinit(mod.gpa); | ||
| 1531 | 1620 | ||
| 1532 | var branch_quota: u32 = default_eval_branch_quota; | 1621 | const var_type = try astgen.typeExpr(mod, &type_scope.base, type_node); |
| 1622 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { | ||
| 1623 | zir.dumpZir(mod.gpa, "var_type", decl.name, type_scope.instructions.items) catch {}; | ||
| 1624 | } | ||
| 1533 | 1625 | ||
| 1534 | var block_scope: Scope.Block = .{ | 1626 | const ty = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, var_type, .{ |
| 1535 | .parent = null, | 1627 | .instructions = type_scope.instructions.items, |
| 1536 | .inst_table = &inst_table, | 1628 | }); |
| 1537 | .func = null, | 1629 | break :vi .{ |
| 1538 | .owner_decl = decl, | 1630 | .ty = ty, |
| 1539 | .src_decl = decl, | 1631 | .val = null, |
| 1540 | .instructions = .{}, | 1632 | }; |
| 1541 | .arena = &analysis_arena.allocator, | 1633 | } else { |
| 1542 | .inlining = null, | 1634 | return mod.failTok( |
| 1543 | .is_comptime = true, | 1635 | &block_scope.base, |
| 1544 | .branch_quota = &branch_quota, | 1636 | var_decl.ast.mut_token, |
| 1545 | }; | 1637 | "unable to infer variable type", |
| 1546 | defer block_scope.instructions.deinit(self.gpa); | 1638 | .{}, |
| 1639 | ); | ||
| 1640 | }; | ||
| 1547 | 1641 | ||
| 1548 | _ = try zir_sema.analyzeBody(self, &block_scope, .{ | 1642 | if (is_mutable and !var_info.ty.isValidVarType(is_extern)) { |
| 1549 | .instructions = gen_scope.instructions.items, | 1643 | return mod.failTok( |
| 1550 | }); | 1644 | &block_scope.base, |
| 1645 | var_decl.ast.mut_token, | ||
| 1646 | "variable of type '{}' must be const", | ||
| 1647 | .{var_info.ty}, | ||
| 1648 | ); | ||
| 1649 | } | ||
| 1551 | 1650 | ||
| 1552 | decl.analysis = .complete; | 1651 | var type_changed = true; |
| 1553 | decl.generation = self.generation; | 1652 | if (decl.typedValueManaged()) |tvm| { |
| 1554 | return true; | 1653 | type_changed = !tvm.typed_value.ty.eql(var_info.ty); |
| 1654 | |||
| 1655 | tvm.deinit(mod.gpa); | ||
| 1656 | } | ||
| 1657 | |||
| 1658 | const new_variable = try decl_arena.allocator.create(Var); | ||
| 1659 | new_variable.* = .{ | ||
| 1660 | .owner_decl = decl, | ||
| 1661 | .init = var_info.val orelse undefined, | ||
| 1662 | .is_extern = is_extern, | ||
| 1663 | .is_mutable = is_mutable, | ||
| 1664 | .is_threadlocal = is_threadlocal, | ||
| 1665 | }; | ||
| 1666 | const var_val = try Value.Tag.variable.create(&decl_arena.allocator, new_variable); | ||
| 1667 | |||
| 1668 | decl_arena_state.* = decl_arena.state; | ||
| 1669 | decl.typed_value = .{ | ||
| 1670 | .most_recent = .{ | ||
| 1671 | .typed_value = .{ | ||
| 1672 | .ty = var_info.ty, | ||
| 1673 | .val = var_val, | ||
| 1674 | }, | ||
| 1675 | .arena = decl_arena_state, | ||
| 1555 | }, | 1676 | }, |
| 1556 | .Use => @panic("TODO usingnamespace decl"), | 1677 | }; |
| 1557 | else => unreachable, | 1678 | decl.analysis = .complete; |
| 1679 | decl.generation = mod.generation; | ||
| 1680 | |||
| 1681 | if (var_decl.extern_export_token) |maybe_export_token| { | ||
| 1682 | if (token_tags[maybe_export_token] == .keyword_export) { | ||
| 1683 | const export_src = token_starts[maybe_export_token]; | ||
| 1684 | const name_token = var_decl.ast.mut_token + 1; | ||
| 1685 | const name = tree.tokenSlice(name_token); // TODO identifierTokenString | ||
| 1686 | // The scope needs to have the decl in it. | ||
| 1687 | try mod.analyzeExport(&block_scope.base, export_src, name, decl); | ||
| 1688 | } | ||
| 1558 | } | 1689 | } |
| 1690 | return type_changed; | ||
| 1559 | } | 1691 | } |
| 1560 | 1692 | ||
| 1561 | fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void { | 1693 | fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void { |
| ... | @@ -1566,7 +1698,7 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void | ... | @@ -1566,7 +1698,7 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void |
| 1566 | dependee.dependants.putAssumeCapacity(depender, {}); | 1698 | dependee.dependants.putAssumeCapacity(depender, {}); |
| 1567 | } | 1699 | } |
| 1568 | 1700 | ||
| 1569 | pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { | 1701 | pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*const ast.Tree { |
| 1570 | const tracy = trace(@src()); | 1702 | const tracy = trace(@src()); |
| 1571 | defer tracy.end(); | 1703 | defer tracy.end(); |
| 1572 | 1704 | ||
| ... | @@ -1577,8 +1709,10 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { | ... | @@ -1577,8 +1709,10 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { |
| 1577 | const source = try root_scope.getSource(self); | 1709 | const source = try root_scope.getSource(self); |
| 1578 | 1710 | ||
| 1579 | var keep_tree = false; | 1711 | var keep_tree = false; |
| 1580 | const tree = try std.zig.parse(self.gpa, source); | 1712 | root_scope.tree = try std.zig.parse(self.gpa, source); |
| 1581 | defer if (!keep_tree) tree.deinit(); | 1713 | defer if (!keep_tree) root_scope.tree.deinit(self.gpa); |
| 1714 | |||
| 1715 | const tree = &root_scope.tree; | ||
| 1582 | 1716 | ||
| 1583 | if (tree.errors.len != 0) { | 1717 | if (tree.errors.len != 0) { |
| 1584 | const parse_err = tree.errors[0]; | 1718 | const parse_err = tree.errors[0]; |
| ... | @@ -1586,12 +1720,12 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { | ... | @@ -1586,12 +1720,12 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { |
| 1586 | var msg = std.ArrayList(u8).init(self.gpa); | 1720 | var msg = std.ArrayList(u8).init(self.gpa); |
| 1587 | defer msg.deinit(); | 1721 | defer msg.deinit(); |
| 1588 | 1722 | ||
| 1589 | try parse_err.render(tree.token_ids, msg.writer()); | 1723 | try tree.renderError(parse_err, msg.writer()); |
| 1590 | const err_msg = try self.gpa.create(ErrorMsg); | 1724 | const err_msg = try self.gpa.create(ErrorMsg); |
| 1591 | err_msg.* = .{ | 1725 | err_msg.* = .{ |
| 1592 | .src_loc = .{ | 1726 | .src_loc = .{ |
| 1593 | .file_scope = root_scope, | 1727 | .file_scope = root_scope, |
| 1594 | .byte_offset = tree.token_locs[parse_err.loc()].start, | 1728 | .byte_offset = tree.tokens.items(.start)[parse_err.token], |
| 1595 | }, | 1729 | }, |
| 1596 | .msg = msg.toOwnedSlice(), | 1730 | .msg = msg.toOwnedSlice(), |
| 1597 | }; | 1731 | }; |
| ... | @@ -1602,7 +1736,6 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { | ... | @@ -1602,7 +1736,6 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { |
| 1602 | } | 1736 | } |
| 1603 | 1737 | ||
| 1604 | root_scope.status = .loaded_success; | 1738 | root_scope.status = .loaded_success; |
| 1605 | root_scope.contents = .{ .tree = tree }; | ||
| 1606 | keep_tree = true; | 1739 | keep_tree = true; |
| 1607 | 1740 | ||
| 1608 | return tree; | 1741 | return tree; |
| ... | @@ -1610,151 +1743,353 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { | ... | @@ -1610,151 +1743,353 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { |
| 1610 | 1743 | ||
| 1611 | .unloaded_parse_failure => return error.AnalysisFail, | 1744 | .unloaded_parse_failure => return error.AnalysisFail, |
| 1612 | 1745 | ||
| 1613 | .loaded_success => return root_scope.contents.tree, | 1746 | .loaded_success => return &root_scope.tree, |
| 1614 | } | 1747 | } |
| 1615 | } | 1748 | } |
| 1616 | 1749 | ||
| 1617 | pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void { | 1750 | pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { |
| 1618 | const tracy = trace(@src()); | 1751 | const tracy = trace(@src()); |
| 1619 | defer tracy.end(); | 1752 | defer tracy.end(); |
| 1620 | 1753 | ||
| 1621 | // We may be analyzing it for the first time, or this may be | 1754 | // We may be analyzing it for the first time, or this may be |
| 1622 | // an incremental update. This code handles both cases. | 1755 | // an incremental update. This code handles both cases. |
| 1623 | const tree = try self.getAstTree(container_scope.file_scope); | 1756 | const tree = try mod.getAstTree(container_scope.file_scope); |
| 1624 | const decls = tree.root_node.decls(); | 1757 | const node_tags = tree.nodes.items(.tag); |
| 1758 | const node_datas = tree.nodes.items(.data); | ||
| 1759 | const decls = tree.rootDecls(); | ||
| 1625 | 1760 | ||
| 1626 | try self.comp.work_queue.ensureUnusedCapacity(decls.len); | 1761 | try mod.comp.work_queue.ensureUnusedCapacity(decls.len); |
| 1627 | try container_scope.decls.ensureCapacity(self.gpa, decls.len); | 1762 | try container_scope.decls.ensureCapacity(mod.gpa, decls.len); |
| 1628 | 1763 | ||
| 1629 | // Keep track of the decls that we expect to see in this file so that | 1764 | // Keep track of the decls that we expect to see in this file so that |
| 1630 | // we know which ones have been deleted. | 1765 | // we know which ones have been deleted. |
| 1631 | var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa); | 1766 | var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(mod.gpa); |
| 1632 | defer deleted_decls.deinit(); | 1767 | defer deleted_decls.deinit(); |
| 1633 | try deleted_decls.ensureCapacity(container_scope.decls.items().len); | 1768 | try deleted_decls.ensureCapacity(container_scope.decls.items().len); |
| 1634 | for (container_scope.decls.items()) |entry| { | 1769 | for (container_scope.decls.items()) |entry| { |
| 1635 | deleted_decls.putAssumeCapacityNoClobber(entry.key, {}); | 1770 | deleted_decls.putAssumeCapacityNoClobber(entry.key, {}); |
| 1636 | } | 1771 | } |
| 1637 | 1772 | ||
| 1638 | for (decls) |src_decl, decl_i| { | 1773 | for (decls) |decl_node, decl_i| switch (node_tags[decl_node]) { |
| 1639 | if (src_decl.cast(ast.Node.FnProto)) |fn_proto| { | 1774 | .fn_decl => { |
| 1640 | // We will create a Decl for it regardless of analysis status. | 1775 | const fn_proto = node_datas[decl_node].lhs; |
| 1641 | const name_tok = fn_proto.getNameToken() orelse { | 1776 | const body = node_datas[decl_node].rhs; |
| 1642 | @panic("TODO missing function name"); | 1777 | switch (node_tags[fn_proto]) { |
| 1643 | }; | 1778 | .fn_proto_simple => { |
| 1644 | 1779 | var params: [1]ast.Node.Index = undefined; | |
| 1645 | const name_loc = tree.token_locs[name_tok]; | 1780 | try mod.semaContainerFn( |
| 1646 | const name = tree.tokenSliceLoc(name_loc); | 1781 | container_scope, |
| 1647 | const name_hash = container_scope.fullyQualifiedNameHash(name); | 1782 | &deleted_decls, |
| 1648 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); | 1783 | decl_node, |
| 1649 | if (self.decl_table.get(name_hash)) |decl| { | 1784 | decl_i, |
| 1650 | // Update the AST Node index of the decl, even if its contents are unchanged, it may | 1785 | tree.*, |
| 1651 | // have been re-ordered. | 1786 | body, |
| 1652 | decl.src_index = decl_i; | 1787 | tree.fnProtoSimple(&params, fn_proto), |
| 1653 | if (deleted_decls.swapRemove(decl) == null) { | 1788 | ); |
| 1654 | decl.analysis = .sema_failure; | 1789 | }, |
| 1655 | const msg = try ErrorMsg.create(self.gpa, .{ | 1790 | .fn_proto_multi => try mod.semaContainerFn( |
| 1656 | .file_scope = container_scope.file_scope, | 1791 | container_scope, |
| 1657 | .byte_offset = tree.token_locs[name_tok].start, | 1792 | &deleted_decls, |
| 1658 | }, "redefinition of '{s}'", .{decl.name}); | 1793 | decl_node, |
| 1659 | errdefer msg.destroy(self.gpa); | 1794 | decl_i, |
| 1660 | try self.failed_decls.putNoClobber(self.gpa, decl, msg); | 1795 | tree.*, |
| 1661 | } else { | 1796 | body, |
| 1662 | if (!srcHashEql(decl.contents_hash, contents_hash)) { | 1797 | tree.fnProtoMulti(fn_proto), |
| 1663 | try self.markOutdatedDecl(decl); | 1798 | ), |
| 1664 | decl.contents_hash = contents_hash; | 1799 | .fn_proto_one => { |
| 1665 | } else switch (self.comp.bin_file.tag) { | 1800 | var params: [1]ast.Node.Index = undefined; |
| 1666 | .coff => { | 1801 | try mod.semaContainerFn( |
| 1667 | // TODO Implement for COFF | 1802 | container_scope, |
| 1668 | }, | 1803 | &deleted_decls, |
| 1669 | .elf => if (decl.fn_link.elf.len != 0) { | 1804 | decl_node, |
| 1670 | // TODO Look into detecting when this would be unnecessary by storing enough state | 1805 | decl_i, |
| 1671 | // in `Decl` to notice that the line number did not change. | 1806 | tree.*, |
| 1672 | self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); | 1807 | body, |
| 1673 | }, | 1808 | tree.fnProtoOne(&params, fn_proto), |
| 1674 | .macho => if (decl.fn_link.macho.len != 0) { | 1809 | ); |
| 1675 | // TODO Look into detecting when this would be unnecessary by storing enough state | 1810 | }, |
| 1676 | // in `Decl` to notice that the line number did not change. | 1811 | .fn_proto => try mod.semaContainerFn( |
| 1677 | self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); | 1812 | container_scope, |
| 1678 | }, | 1813 | &deleted_decls, |
| 1679 | .c, .wasm, .spirv => {}, | 1814 | decl_node, |
| 1680 | } | 1815 | decl_i, |
| 1681 | } | 1816 | tree.*, |
| 1682 | } else { | 1817 | body, |
| 1683 | const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | 1818 | tree.fnProto(fn_proto), |
| 1684 | container_scope.decls.putAssumeCapacity(new_decl, {}); | 1819 | ), |
| 1685 | if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { | 1820 | else => unreachable, |
| 1686 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | ||
| 1687 | self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | ||
| 1688 | } | ||
| 1689 | } | ||
| 1690 | } | ||
| 1691 | } else if (src_decl.castTag(.VarDecl)) |var_decl| { | ||
| 1692 | const name_loc = tree.token_locs[var_decl.name_token]; | ||
| 1693 | const name = tree.tokenSliceLoc(name_loc); | ||
| 1694 | const name_hash = container_scope.fullyQualifiedNameHash(name); | ||
| 1695 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); | ||
| 1696 | if (self.decl_table.get(name_hash)) |decl| { | ||
| 1697 | // Update the AST Node index of the decl, even if its contents are unchanged, it may | ||
| 1698 | // have been re-ordered. | ||
| 1699 | decl.src_index = decl_i; | ||
| 1700 | if (deleted_decls.swapRemove(decl) == null) { | ||
| 1701 | decl.analysis = .sema_failure; | ||
| 1702 | const err_msg = try ErrorMsg.create(self.gpa, .{ | ||
| 1703 | .file_scope = container_scope.file_scope, | ||
| 1704 | .byte_offset = name_loc.start, | ||
| 1705 | }, "redefinition of '{s}'", .{decl.name}); | ||
| 1706 | errdefer err_msg.destroy(self.gpa); | ||
| 1707 | try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); | ||
| 1708 | } else if (!srcHashEql(decl.contents_hash, contents_hash)) { | ||
| 1709 | try self.markOutdatedDecl(decl); | ||
| 1710 | decl.contents_hash = contents_hash; | ||
| 1711 | } | ||
| 1712 | } else { | ||
| 1713 | const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | ||
| 1714 | container_scope.decls.putAssumeCapacity(new_decl, {}); | ||
| 1715 | if (var_decl.getExternExportToken()) |maybe_export_token| { | ||
| 1716 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | ||
| 1717 | self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | ||
| 1718 | } | ||
| 1719 | } | ||
| 1720 | } | 1821 | } |
| 1721 | } else if (src_decl.castTag(.Comptime)) |comptime_node| { | 1822 | }, |
| 1722 | const name_index = self.getNextAnonNameIndex(); | 1823 | .fn_proto_simple => { |
| 1723 | const name = try std.fmt.allocPrint(self.gpa, "__comptime_{d}", .{name_index}); | 1824 | var params: [1]ast.Node.Index = undefined; |
| 1724 | defer self.gpa.free(name); | 1825 | try mod.semaContainerFn( |
| 1826 | container_scope, | ||
| 1827 | &deleted_decls, | ||
| 1828 | decl_node, | ||
| 1829 | decl_i, | ||
| 1830 | tree.*, | ||
| 1831 | 0, | ||
| 1832 | tree.fnProtoSimple(&params, decl_node), | ||
| 1833 | ); | ||
| 1834 | }, | ||
| 1835 | .fn_proto_multi => try mod.semaContainerFn( | ||
| 1836 | container_scope, | ||
| 1837 | &deleted_decls, | ||
| 1838 | decl_node, | ||
| 1839 | decl_i, | ||
| 1840 | tree.*, | ||
| 1841 | 0, | ||
| 1842 | tree.fnProtoMulti(decl_node), | ||
| 1843 | ), | ||
| 1844 | .fn_proto_one => { | ||
| 1845 | var params: [1]ast.Node.Index = undefined; | ||
| 1846 | try mod.semaContainerFn( | ||
| 1847 | container_scope, | ||
| 1848 | &deleted_decls, | ||
| 1849 | decl_node, | ||
| 1850 | decl_i, | ||
| 1851 | tree.*, | ||
| 1852 | 0, | ||
| 1853 | tree.fnProtoOne(&params, decl_node), | ||
| 1854 | ); | ||
| 1855 | }, | ||
| 1856 | .fn_proto => try mod.semaContainerFn( | ||
| 1857 | container_scope, | ||
| 1858 | &deleted_decls, | ||
| 1859 | decl_node, | ||
| 1860 | decl_i, | ||
| 1861 | tree.*, | ||
| 1862 | 0, | ||
| 1863 | tree.fnProto(decl_node), | ||
| 1864 | ), | ||
| 1865 | |||
| 1866 | .global_var_decl => try mod.semaContainerVar( | ||
| 1867 | container_scope, | ||
| 1868 | &deleted_decls, | ||
| 1869 | decl_node, | ||
| 1870 | decl_i, | ||
| 1871 | tree.*, | ||
| 1872 | tree.globalVarDecl(decl_node), | ||
| 1873 | ), | ||
| 1874 | .local_var_decl => try mod.semaContainerVar( | ||
| 1875 | container_scope, | ||
| 1876 | &deleted_decls, | ||
| 1877 | decl_node, | ||
| 1878 | decl_i, | ||
| 1879 | tree.*, | ||
| 1880 | tree.localVarDecl(decl_node), | ||
| 1881 | ), | ||
| 1882 | .simple_var_decl => try mod.semaContainerVar( | ||
| 1883 | container_scope, | ||
| 1884 | &deleted_decls, | ||
| 1885 | decl_node, | ||
| 1886 | decl_i, | ||
| 1887 | tree.*, | ||
| 1888 | tree.simpleVarDecl(decl_node), | ||
| 1889 | ), | ||
| 1890 | .aligned_var_decl => try mod.semaContainerVar( | ||
| 1891 | container_scope, | ||
| 1892 | &deleted_decls, | ||
| 1893 | decl_node, | ||
| 1894 | decl_i, | ||
| 1895 | tree.*, | ||
| 1896 | tree.alignedVarDecl(decl_node), | ||
| 1897 | ), | ||
| 1898 | |||
| 1899 | .@"comptime" => { | ||
| 1900 | const name_index = mod.getNextAnonNameIndex(); | ||
| 1901 | const name = try std.fmt.allocPrint(mod.gpa, "__comptime_{d}", .{name_index}); | ||
| 1902 | defer mod.gpa.free(name); | ||
| 1725 | 1903 | ||
| 1726 | const name_hash = container_scope.fullyQualifiedNameHash(name); | 1904 | const name_hash = container_scope.fullyQualifiedNameHash(name); |
| 1727 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); | 1905 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); |
| 1728 | 1906 | ||
| 1729 | const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | 1907 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); |
| 1730 | container_scope.decls.putAssumeCapacity(new_decl, {}); | 1908 | container_scope.decls.putAssumeCapacity(new_decl, {}); |
| 1731 | self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | 1909 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| 1732 | } else if (src_decl.castTag(.ContainerField)) |container_field| { | 1910 | }, |
| 1733 | log.err("TODO: analyze container field", .{}); | 1911 | |
| 1734 | } else if (src_decl.castTag(.TestDecl)) |test_decl| { | 1912 | .container_field_init => try mod.semaContainerField( |
| 1913 | container_scope, | ||
| 1914 | &deleted_decls, | ||
| 1915 | decl_node, | ||
| 1916 | decl_i, | ||
| 1917 | tree.*, | ||
| 1918 | tree.containerFieldInit(decl_node), | ||
| 1919 | ), | ||
| 1920 | .container_field_align => try mod.semaContainerField( | ||
| 1921 | container_scope, | ||
| 1922 | &deleted_decls, | ||
| 1923 | decl_node, | ||
| 1924 | decl_i, | ||
| 1925 | tree.*, | ||
| 1926 | tree.containerFieldAlign(decl_node), | ||
| 1927 | ), | ||
| 1928 | .container_field => try mod.semaContainerField( | ||
| 1929 | container_scope, | ||
| 1930 | &deleted_decls, | ||
| 1931 | decl_node, | ||
| 1932 | decl_i, | ||
| 1933 | tree.*, | ||
| 1934 | tree.containerField(decl_node), | ||
| 1935 | ), | ||
| 1936 | |||
| 1937 | .test_decl => { | ||
| 1735 | log.err("TODO: analyze test decl", .{}); | 1938 | log.err("TODO: analyze test decl", .{}); |
| 1736 | } else if (src_decl.castTag(.Use)) |use_decl| { | 1939 | }, |
| 1940 | .@"usingnamespace" => { | ||
| 1737 | log.err("TODO: analyze usingnamespace decl", .{}); | 1941 | log.err("TODO: analyze usingnamespace decl", .{}); |
| 1738 | } else { | 1942 | }, |
| 1739 | unreachable; | 1943 | else => unreachable, |
| 1740 | } | 1944 | }; |
| 1741 | } | ||
| 1742 | // Handle explicitly deleted decls from the source code. Not to be confused | 1945 | // Handle explicitly deleted decls from the source code. Not to be confused |
| 1743 | // with when we delete decls because they are no longer referenced. | 1946 | // with when we delete decls because they are no longer referenced. |
| 1744 | for (deleted_decls.items()) |entry| { | 1947 | for (deleted_decls.items()) |entry| { |
| 1745 | log.debug("noticed '{s}' deleted from source\n", .{entry.key.name}); | 1948 | log.debug("noticed '{s}' deleted from source", .{entry.key.name}); |
| 1746 | try self.deleteDecl(entry.key); | 1949 | try mod.deleteDecl(entry.key); |
| 1950 | } | ||
| 1951 | } | ||
| 1952 | |||
| 1953 | fn semaContainerFn( | ||
| 1954 | mod: *Module, | ||
| 1955 | container_scope: *Scope.Container, | ||
| 1956 | deleted_decls: *std.AutoArrayHashMap(*Decl, void), | ||
| 1957 | decl_node: ast.Node.Index, | ||
| 1958 | decl_i: usize, | ||
| 1959 | tree: ast.Tree, | ||
| 1960 | body_node: ast.Node.Index, | ||
| 1961 | fn_proto: ast.full.FnProto, | ||
| 1962 | ) !void { | ||
| 1963 | const tracy = trace(@src()); | ||
| 1964 | defer tracy.end(); | ||
| 1965 | |||
| 1966 | const token_starts = tree.tokens.items(.start); | ||
| 1967 | const token_tags = tree.tokens.items(.tag); | ||
| 1968 | |||
| 1969 | // We will create a Decl for it regardless of analysis status. | ||
| 1970 | const name_tok = fn_proto.name_token orelse { | ||
| 1971 | @panic("TODO missing function name"); | ||
| 1972 | }; | ||
| 1973 | const name = tree.tokenSlice(name_tok); // TODO use identifierTokenString | ||
| 1974 | const name_hash = container_scope.fullyQualifiedNameHash(name); | ||
| 1975 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); | ||
| 1976 | if (mod.decl_table.get(name_hash)) |decl| { | ||
| 1977 | // Update the AST Node index of the decl, even if its contents are unchanged, it may | ||
| 1978 | // have been re-ordered. | ||
| 1979 | decl.src_index = decl_i; | ||
| 1980 | if (deleted_decls.swapRemove(decl) == null) { | ||
| 1981 | decl.analysis = .sema_failure; | ||
| 1982 | const msg = try ErrorMsg.create(mod.gpa, .{ | ||
| 1983 | .file_scope = container_scope.file_scope, | ||
| 1984 | .byte_offset = token_starts[name_tok], | ||
| 1985 | }, "redefinition of '{s}'", .{decl.name}); | ||
| 1986 | errdefer msg.destroy(mod.gpa); | ||
| 1987 | try mod.failed_decls.putNoClobber(mod.gpa, decl, msg); | ||
| 1988 | } else { | ||
| 1989 | if (!srcHashEql(decl.contents_hash, contents_hash)) { | ||
| 1990 | try mod.markOutdatedDecl(decl); | ||
| 1991 | decl.contents_hash = contents_hash; | ||
| 1992 | } else switch (mod.comp.bin_file.tag) { | ||
| 1993 | .coff => { | ||
| 1994 | // TODO Implement for COFF | ||
| 1995 | }, | ||
| 1996 | .elf => if (decl.fn_link.elf.len != 0) { | ||
| 1997 | // TODO Look into detecting when this would be unnecessary by storing enough state | ||
| 1998 | // in `Decl` to notice that the line number did not change. | ||
| 1999 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); | ||
| 2000 | }, | ||
| 2001 | .macho => if (decl.fn_link.macho.len != 0) { | ||
| 2002 | // TODO Look into detecting when this would be unnecessary by storing enough state | ||
| 2003 | // in `Decl` to notice that the line number did not change. | ||
| 2004 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); | ||
| 2005 | }, | ||
| 2006 | .c, .wasm, .spirv => {}, | ||
| 2007 | } | ||
| 2008 | } | ||
| 2009 | } else { | ||
| 2010 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | ||
| 2011 | container_scope.decls.putAssumeCapacity(new_decl, {}); | ||
| 2012 | if (fn_proto.extern_export_token) |maybe_export_token| { | ||
| 2013 | if (token_tags[maybe_export_token] == .keyword_export) { | ||
| 2014 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | ||
| 2015 | } | ||
| 2016 | } | ||
| 2017 | } | ||
| 2018 | } | ||
| 2019 | |||
| 2020 | fn semaContainerVar( | ||
| 2021 | mod: *Module, | ||
| 2022 | container_scope: *Scope.Container, | ||
| 2023 | deleted_decls: *std.AutoArrayHashMap(*Decl, void), | ||
| 2024 | decl_node: ast.Node.Index, | ||
| 2025 | decl_i: usize, | ||
| 2026 | tree: ast.Tree, | ||
| 2027 | var_decl: ast.full.VarDecl, | ||
| 2028 | ) !void { | ||
| 2029 | const tracy = trace(@src()); | ||
| 2030 | defer tracy.end(); | ||
| 2031 | |||
| 2032 | const token_starts = tree.tokens.items(.start); | ||
| 2033 | const token_tags = tree.tokens.items(.tag); | ||
| 2034 | |||
| 2035 | const name_token = var_decl.ast.mut_token + 1; | ||
| 2036 | const name_src = token_starts[name_token]; | ||
| 2037 | const name = tree.tokenSlice(name_token); // TODO identifierTokenString | ||
| 2038 | const name_hash = container_scope.fullyQualifiedNameHash(name); | ||
| 2039 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); | ||
| 2040 | if (mod.decl_table.get(name_hash)) |decl| { | ||
| 2041 | // Update the AST Node index of the decl, even if its contents are unchanged, it may | ||
| 2042 | // have been re-ordered. | ||
| 2043 | decl.src_index = decl_i; | ||
| 2044 | if (deleted_decls.swapRemove(decl) == null) { | ||
| 2045 | decl.analysis = .sema_failure; | ||
| 2046 | const err_msg = try ErrorMsg.create(mod.gpa, .{ | ||
| 2047 | .file_scope = container_scope.file_scope, | ||
| 2048 | .byte_offset = name_src, | ||
| 2049 | }, "redefinition of '{s}'", .{decl.name}); | ||
| 2050 | errdefer err_msg.destroy(mod.gpa); | ||
| 2051 | try mod.failed_decls.putNoClobber(mod.gpa, decl, err_msg); | ||
| 2052 | } else if (!srcHashEql(decl.contents_hash, contents_hash)) { | ||
| 2053 | try mod.markOutdatedDecl(decl); | ||
| 2054 | decl.contents_hash = contents_hash; | ||
| 2055 | } | ||
| 2056 | } else { | ||
| 2057 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | ||
| 2058 | container_scope.decls.putAssumeCapacity(new_decl, {}); | ||
| 2059 | if (var_decl.extern_export_token) |maybe_export_token| { | ||
| 2060 | if (token_tags[maybe_export_token] == .keyword_export) { | ||
| 2061 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | ||
| 2062 | } | ||
| 2063 | } | ||
| 1747 | } | 2064 | } |
| 1748 | } | 2065 | } |
| 1749 | 2066 | ||
| 2067 | fn semaContainerField( | ||
| 2068 | mod: *Module, | ||
| 2069 | container_scope: *Scope.Container, | ||
| 2070 | deleted_decls: *std.AutoArrayHashMap(*Decl, void), | ||
| 2071 | decl_node: ast.Node.Index, | ||
| 2072 | decl_i: usize, | ||
| 2073 | tree: ast.Tree, | ||
| 2074 | field: ast.full.ContainerField, | ||
| 2075 | ) !void { | ||
| 2076 | const tracy = trace(@src()); | ||
| 2077 | defer tracy.end(); | ||
| 2078 | |||
| 2079 | log.err("TODO: analyze container field", .{}); | ||
| 2080 | } | ||
| 2081 | |||
| 1750 | pub fn deleteDecl(self: *Module, decl: *Decl) !void { | 2082 | pub fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 2083 | const tracy = trace(@src()); | ||
| 2084 | defer tracy.end(); | ||
| 2085 | |||
| 1751 | try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len); | 2086 | try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len); |
| 1752 | 2087 | ||
| 1753 | // Remove from the namespace it resides in. In the case of an anonymous Decl it will | 2088 | // Remove from the namespace it resides in. In the case of an anonymous Decl it will |
| 1754 | // not be present in the set, and this does nothing. | 2089 | // not be present in the set, and this does nothing. |
| 1755 | decl.container.removeDecl(decl); | 2090 | decl.container.removeDecl(decl); |
| 1756 | 2091 | ||
| 1757 | log.debug("deleting decl '{s}'\n", .{decl.name}); | 2092 | log.debug("deleting decl '{s}'", .{decl.name}); |
| 1758 | const name_hash = decl.fullyQualifiedNameHash(); | 2093 | const name_hash = decl.fullyQualifiedNameHash(); |
| 1759 | self.decl_table.removeAssertDiscard(name_hash); | 2094 | self.decl_table.removeAssertDiscard(name_hash); |
| 1760 | // Remove itself from its dependencies, because we are about to destroy the decl pointer. | 2095 | // Remove itself from its dependencies, because we are about to destroy the decl pointer. |
| ... | @@ -1856,18 +2191,18 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { | ... | @@ -1856,18 +2191,18 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { |
| 1856 | defer inner_block.instructions.deinit(self.gpa); | 2191 | defer inner_block.instructions.deinit(self.gpa); |
| 1857 | 2192 | ||
| 1858 | func.state = .in_progress; | 2193 | func.state = .in_progress; |
| 1859 | log.debug("set {s} to in_progress\n", .{decl.name}); | 2194 | log.debug("set {s} to in_progress", .{decl.name}); |
| 1860 | 2195 | ||
| 1861 | try zir_sema.analyzeBody(self, &inner_block, func.zir); | 2196 | try zir_sema.analyzeBody(self, &inner_block, func.zir); |
| 1862 | 2197 | ||
| 1863 | const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items); | 2198 | const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items); |
| 1864 | func.state = .success; | 2199 | func.state = .success; |
| 1865 | func.body = .{ .instructions = instructions }; | 2200 | func.body = .{ .instructions = instructions }; |
| 1866 | log.debug("set {s} to success\n", .{decl.name}); | 2201 | log.debug("set {s} to success", .{decl.name}); |
| 1867 | } | 2202 | } |
| 1868 | 2203 | ||
| 1869 | fn markOutdatedDecl(self: *Module, decl: *Decl) !void { | 2204 | fn markOutdatedDecl(self: *Module, decl: *Decl) !void { |
| 1870 | log.debug("mark {s} outdated\n", .{decl.name}); | 2205 | log.debug("mark {s} outdated", .{decl.name}); |
| 1871 | try self.comp.work_queue.writeItem(.{ .analyze_decl = decl }); | 2206 | try self.comp.work_queue.writeItem(.{ .analyze_decl = decl }); |
| 1872 | if (self.failed_decls.swapRemove(decl)) |entry| { | 2207 | if (self.failed_decls.swapRemove(decl)) |entry| { |
| 1873 | entry.value.destroy(self.gpa); | 2208 | entry.value.destroy(self.gpa); |
| ... | @@ -2395,15 +2730,16 @@ pub fn createContainerDecl( | ... | @@ -2395,15 +2730,16 @@ pub fn createContainerDecl( |
| 2395 | fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 { | 2730 | fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 { |
| 2396 | // TODO add namespaces, generic function signatrues | 2731 | // TODO add namespaces, generic function signatrues |
| 2397 | const tree = scope.tree(); | 2732 | const tree = scope.tree(); |
| 2398 | const base_name = switch (tree.token_ids[base_token]) { | 2733 | const token_tags = tree.tokens.items(.tag); |
| 2399 | .Keyword_struct => "struct", | 2734 | const base_name = switch (token_tags[base_token]) { |
| 2400 | .Keyword_enum => "enum", | 2735 | .keyword_struct => "struct", |
| 2401 | .Keyword_union => "union", | 2736 | .keyword_enum => "enum", |
| 2402 | .Keyword_opaque => "opaque", | 2737 | .keyword_union => "union", |
| 2738 | .keyword_opaque => "opaque", | ||
| 2403 | else => unreachable, | 2739 | else => unreachable, |
| 2404 | }; | 2740 | }; |
| 2405 | const loc = tree.tokenLocationLoc(0, tree.token_locs[base_token]); | 2741 | const loc = tree.tokenLocation(0, base_token); |
| 2406 | return std.fmt.allocPrint(self.gpa, "{s}:{}:{}", .{ base_name, loc.line, loc.column }); | 2742 | return std.fmt.allocPrint(self.gpa, "{s}:{d}:{d}", .{ base_name, loc.line, loc.column }); |
| 2407 | } | 2743 | } |
| 2408 | 2744 | ||
| 2409 | fn getNextAnonNameIndex(self: *Module) usize { | 2745 | fn getNextAnonNameIndex(self: *Module) usize { |
| ... | @@ -2639,7 +2975,7 @@ pub fn analyzeImport(self: *Module, scope: *Scope, src: usize, target_string: [] | ... | @@ -2639,7 +2975,7 @@ pub fn analyzeImport(self: *Module, scope: *Scope, src: usize, target_string: [] |
| 2639 | file_scope.* = .{ | 2975 | file_scope.* = .{ |
| 2640 | .sub_file_path = resolved_path, | 2976 | .sub_file_path = resolved_path, |
| 2641 | .source = .{ .unloaded = {} }, | 2977 | .source = .{ .unloaded = {} }, |
| 2642 | .contents = .{ .not_available = {} }, | 2978 | .tree = undefined, |
| 2643 | .status = .never_loaded, | 2979 | .status = .never_loaded, |
| 2644 | .pkg = found_pkg orelse cur_pkg, | 2980 | .pkg = found_pkg orelse cur_pkg, |
| 2645 | .root_container = .{ | 2981 | .root_container = .{ |
| ... | @@ -3149,18 +3485,19 @@ pub fn failTok( | ... | @@ -3149,18 +3485,19 @@ pub fn failTok( |
| 3149 | comptime format: []const u8, | 3485 | comptime format: []const u8, |
| 3150 | args: anytype, | 3486 | args: anytype, |
| 3151 | ) InnerError { | 3487 | ) InnerError { |
| 3152 | const src = scope.tree().token_locs[token_index].start; | 3488 | const src = scope.tree().tokens.items(.start)[token_index]; |
| 3153 | return self.fail(scope, src, format, args); | 3489 | return self.fail(scope, src, format, args); |
| 3154 | } | 3490 | } |
| 3155 | 3491 | ||
| 3156 | pub fn failNode( | 3492 | pub fn failNode( |
| 3157 | self: *Module, | 3493 | self: *Module, |
| 3158 | scope: *Scope, | 3494 | scope: *Scope, |
| 3159 | ast_node: *ast.Node, | 3495 | ast_node: ast.Node.Index, |
| 3160 | comptime format: []const u8, | 3496 | comptime format: []const u8, |
| 3161 | args: anytype, | 3497 | args: anytype, |
| 3162 | ) InnerError { | 3498 | ) InnerError { |
| 3163 | const src = scope.tree().token_locs[ast_node.firstToken()].start; | 3499 | const tree = scope.tree(); |
| 3500 | const src = tree.tokens.items(.start)[tree.firstToken(ast_node)]; | ||
| 3164 | return self.fail(scope, src, format, args); | 3501 | return self.fail(scope, src, format, args); |
| 3165 | } | 3502 | } |
| 3166 | 3503 | ||
| ... | @@ -3594,6 +3931,9 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void | ... | @@ -3594,6 +3931,9 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void |
| 3594 | /// Identifier token -> String (allocated in scope.arena()) | 3931 | /// Identifier token -> String (allocated in scope.arena()) |
| 3595 | pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { | 3932 | pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { |
| 3596 | const tree = scope.tree(); | 3933 | const tree = scope.tree(); |
| 3934 | const token_tags = tree.tokens.items(.tag); | ||
| 3935 | const token_starts = tree.tokens.items(.start); | ||
| 3936 | assert(token_tags[token] == .identifier); | ||
| 3597 | 3937 | ||
| 3598 | const ident_name = tree.tokenSlice(token); | 3938 | const ident_name = tree.tokenSlice(token); |
| 3599 | if (mem.startsWith(u8, ident_name, "@")) { | 3939 | if (mem.startsWith(u8, ident_name, "@")) { |
| ... | @@ -3602,7 +3942,7 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) | ... | @@ -3602,7 +3942,7 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) |
| 3602 | return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) { | 3942 | return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) { |
| 3603 | error.InvalidCharacter => { | 3943 | error.InvalidCharacter => { |
| 3604 | const bad_byte = raw_string[bad_index]; | 3944 | const bad_byte = raw_string[bad_index]; |
| 3605 | const src = tree.token_locs[token].start; | 3945 | const src = token_starts[token]; |
| 3606 | return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte}); | 3946 | return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte}); |
| 3607 | }, | 3947 | }, |
| 3608 | else => |e| return e, | 3948 | else => |e| return e, |
src/astgen.zig+1908-1427| ... | @@ -1,16 +1,18 @@ | ... | @@ -1,16 +1,18 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const mem = std.mem; | 2 | const mem = std.mem; |
| 3 | const Allocator = std.mem.Allocator; | 3 | const Allocator = std.mem.Allocator; |
| 4 | const assert = std.debug.assert; | ||
| 5 | |||
| 4 | const Value = @import("value.zig").Value; | 6 | const Value = @import("value.zig").Value; |
| 5 | const Type = @import("type.zig").Type; | 7 | const Type = @import("type.zig").Type; |
| 6 | const TypedValue = @import("TypedValue.zig"); | 8 | const TypedValue = @import("TypedValue.zig"); |
| 7 | const assert = std.debug.assert; | ||
| 8 | const zir = @import("zir.zig"); | 9 | const zir = @import("zir.zig"); |
| 9 | const Module = @import("Module.zig"); | 10 | const Module = @import("Module.zig"); |
| 10 | const ast = std.zig.ast; | 11 | const ast = std.zig.ast; |
| 11 | const trace = @import("tracy.zig").trace; | 12 | const trace = @import("tracy.zig").trace; |
| 12 | const Scope = Module.Scope; | 13 | const Scope = Module.Scope; |
| 13 | const InnerError = Module.InnerError; | 14 | const InnerError = Module.InnerError; |
| 15 | const BuiltinFn = @import("BuiltinFn.zig"); | ||
| 14 | 16 | ||
| 15 | pub const ResultLoc = union(enum) { | 17 | pub const ResultLoc = union(enum) { |
| 16 | /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the | 18 | /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the |
| ... | @@ -55,8 +57,11 @@ pub const ResultLoc = union(enum) { | ... | @@ -55,8 +57,11 @@ pub const ResultLoc = union(enum) { |
| 55 | }; | 57 | }; |
| 56 | }; | 58 | }; |
| 57 | 59 | ||
| 58 | pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst { | 60 | pub fn typeExpr(mod: *Module, scope: *Scope, type_node: ast.Node.Index) InnerError!*zir.Inst { |
| 59 | const type_src = scope.tree().token_locs[type_node.firstToken()].start; | 61 | const tree = scope.tree(); |
| 62 | const token_starts = tree.tokens.items(.start); | ||
| 63 | |||
| 64 | const type_src = token_starts[tree.firstToken(type_node)]; | ||
| 60 | const type_type = try addZIRInstConst(mod, scope, type_src, .{ | 65 | const type_type = try addZIRInstConst(mod, scope, type_src, .{ |
| 61 | .ty = Type.initTag(.type), | 66 | .ty = Type.initTag(.type), |
| 62 | .val = Value.initTag(.type_type), | 67 | .val = Value.initTag(.type_type), |
| ... | @@ -65,134 +70,191 @@ pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*z | ... | @@ -65,134 +70,191 @@ pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*z |
| 65 | return expr(mod, scope, type_rl, type_node); | 70 | return expr(mod, scope, type_rl, type_node); |
| 66 | } | 71 | } |
| 67 | 72 | ||
| 68 | fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { | 73 | fn lvalExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst { |
| 69 | switch (node.tag) { | 74 | const tree = scope.tree(); |
| 70 | .Root => unreachable, | 75 | const node_tags = tree.nodes.items(.tag); |
| 71 | .Use => unreachable, | 76 | const main_tokens = tree.nodes.items(.main_token); |
| 72 | .TestDecl => unreachable, | 77 | switch (node_tags[node]) { |
| 73 | .DocComment => unreachable, | 78 | .root => unreachable, |
| 74 | .VarDecl => unreachable, | 79 | .@"usingnamespace" => unreachable, |
| 75 | .SwitchCase => unreachable, | 80 | .test_decl => unreachable, |
| 76 | .SwitchElse => unreachable, | 81 | .global_var_decl => unreachable, |
| 77 | .Else => unreachable, | 82 | .local_var_decl => unreachable, |
| 78 | .Payload => unreachable, | 83 | .simple_var_decl => unreachable, |
| 79 | .PointerPayload => unreachable, | 84 | .aligned_var_decl => unreachable, |
| 80 | .PointerIndexPayload => unreachable, | 85 | .switch_case => unreachable, |
| 81 | .ErrorTag => unreachable, | 86 | .switch_case_one => unreachable, |
| 82 | .FieldInitializer => unreachable, | 87 | .container_field_init => unreachable, |
| 83 | .ContainerField => unreachable, | 88 | .container_field_align => unreachable, |
| 84 | 89 | .container_field => unreachable, | |
| 85 | .Assign, | 90 | .asm_output => unreachable, |
| 86 | .AssignBitAnd, | 91 | .asm_input => unreachable, |
| 87 | .AssignBitOr, | 92 | |
| 88 | .AssignBitShiftLeft, | 93 | .assign, |
| 89 | .AssignBitShiftRight, | 94 | .assign_bit_and, |
| 90 | .AssignBitXor, | 95 | .assign_bit_or, |
| 91 | .AssignDiv, | 96 | .assign_bit_shift_left, |
| 92 | .AssignSub, | 97 | .assign_bit_shift_right, |
| 93 | .AssignSubWrap, | 98 | .assign_bit_xor, |
| 94 | .AssignMod, | 99 | .assign_div, |
| 95 | .AssignAdd, | 100 | .assign_sub, |
| 96 | .AssignAddWrap, | 101 | .assign_sub_wrap, |
| 97 | .AssignMul, | 102 | .assign_mod, |
| 98 | .AssignMulWrap, | 103 | .assign_add, |
| 99 | .Add, | 104 | .assign_add_wrap, |
| 100 | .AddWrap, | 105 | .assign_mul, |
| 101 | .Sub, | 106 | .assign_mul_wrap, |
| 102 | .SubWrap, | 107 | .add, |
| 103 | .Mul, | 108 | .add_wrap, |
| 104 | .MulWrap, | 109 | .sub, |
| 105 | .Div, | 110 | .sub_wrap, |
| 106 | .Mod, | 111 | .mul, |
| 107 | .BitAnd, | 112 | .mul_wrap, |
| 108 | .BitOr, | 113 | .div, |
| 109 | .BitShiftLeft, | 114 | .mod, |
| 110 | .BitShiftRight, | 115 | .bit_and, |
| 111 | .BitXor, | 116 | .bit_or, |
| 112 | .BangEqual, | 117 | .bit_shift_left, |
| 113 | .EqualEqual, | 118 | .bit_shift_right, |
| 114 | .GreaterThan, | 119 | .bit_xor, |
| 115 | .GreaterOrEqual, | 120 | .bang_equal, |
| 116 | .LessThan, | 121 | .equal_equal, |
| 117 | .LessOrEqual, | 122 | .greater_than, |
| 118 | .ArrayCat, | 123 | .greater_or_equal, |
| 119 | .ArrayMult, | 124 | .less_than, |
| 120 | .BoolAnd, | 125 | .less_or_equal, |
| 121 | .BoolOr, | 126 | .array_cat, |
| 122 | .Asm, | 127 | .array_mult, |
| 123 | .StringLiteral, | 128 | .bool_and, |
| 124 | .IntegerLiteral, | 129 | .bool_or, |
| 125 | .Call, | 130 | .@"asm", |
| 126 | .Unreachable, | 131 | .asm_simple, |
| 127 | .Return, | 132 | .string_literal, |
| 128 | .If, | 133 | .integer_literal, |
| 129 | .While, | 134 | .call, |
| 130 | .BoolNot, | 135 | .call_comma, |
| 131 | .AddressOf, | 136 | .async_call, |
| 132 | .FloatLiteral, | 137 | .async_call_comma, |
| 133 | .UndefinedLiteral, | 138 | .call_one, |
| 134 | .BoolLiteral, | 139 | .call_one_comma, |
| 135 | .NullLiteral, | 140 | .async_call_one, |
| 136 | .OptionalType, | 141 | .async_call_one_comma, |
| 137 | .Block, | 142 | .unreachable_literal, |
| 138 | .LabeledBlock, | 143 | .@"return", |
| 139 | .Break, | 144 | .@"if", |
| 140 | .PtrType, | 145 | .if_simple, |
| 141 | .ArrayType, | 146 | .@"while", |
| 142 | .ArrayTypeSentinel, | 147 | .while_simple, |
| 143 | .EnumLiteral, | 148 | .while_cont, |
| 144 | .MultilineStringLiteral, | 149 | .bool_not, |
| 145 | .CharLiteral, | 150 | .address_of, |
| 146 | .Defer, | 151 | .float_literal, |
| 147 | .Catch, | 152 | .undefined_literal, |
| 148 | .ErrorUnion, | 153 | .true_literal, |
| 149 | .MergeErrorSets, | 154 | .false_literal, |
| 150 | .Range, | 155 | .null_literal, |
| 151 | .Await, | 156 | .optional_type, |
| 152 | .BitNot, | 157 | .block, |
| 153 | .Negation, | 158 | .block_semicolon, |
| 154 | .NegationWrap, | 159 | .block_two, |
| 155 | .Resume, | 160 | .block_two_semicolon, |
| 156 | .Try, | 161 | .@"break", |
| 157 | .SliceType, | 162 | .ptr_type_aligned, |
| 158 | .Slice, | 163 | .ptr_type_sentinel, |
| 159 | .ArrayInitializer, | 164 | .ptr_type, |
| 160 | .ArrayInitializerDot, | 165 | .ptr_type_bit_range, |
| 161 | .StructInitializer, | 166 | .array_type, |
| 162 | .StructInitializerDot, | 167 | .array_type_sentinel, |
| 163 | .Switch, | 168 | .enum_literal, |
| 164 | .For, | 169 | .multiline_string_literal, |
| 165 | .Suspend, | 170 | .char_literal, |
| 166 | .Continue, | 171 | .@"defer", |
| 167 | .AnyType, | 172 | .@"errdefer", |
| 168 | .ErrorType, | 173 | .@"catch", |
| 169 | .FnProto, | 174 | .error_union, |
| 170 | .AnyFrameType, | 175 | .merge_error_sets, |
| 171 | .ErrorSetDecl, | 176 | .switch_range, |
| 172 | .ContainerDecl, | 177 | .@"await", |
| 173 | .Comptime, | 178 | .bit_not, |
| 174 | .Nosuspend, | 179 | .negation, |
| 180 | .negation_wrap, | ||
| 181 | .@"resume", | ||
| 182 | .@"try", | ||
| 183 | .slice, | ||
| 184 | .slice_open, | ||
| 185 | .slice_sentinel, | ||
| 186 | .array_init_one, | ||
| 187 | .array_init_one_comma, | ||
| 188 | .array_init_dot_two, | ||
| 189 | .array_init_dot_two_comma, | ||
| 190 | .array_init_dot, | ||
| 191 | .array_init_dot_comma, | ||
| 192 | .array_init, | ||
| 193 | .array_init_comma, | ||
| 194 | .struct_init_one, | ||
| 195 | .struct_init_one_comma, | ||
| 196 | .struct_init_dot_two, | ||
| 197 | .struct_init_dot_two_comma, | ||
| 198 | .struct_init_dot, | ||
| 199 | .struct_init_dot_comma, | ||
| 200 | .struct_init, | ||
| 201 | .struct_init_comma, | ||
| 202 | .@"switch", | ||
| 203 | .switch_comma, | ||
| 204 | .@"for", | ||
| 205 | .for_simple, | ||
| 206 | .@"suspend", | ||
| 207 | .@"continue", | ||
| 208 | .@"anytype", | ||
| 209 | .fn_proto_simple, | ||
| 210 | .fn_proto_multi, | ||
| 211 | .fn_proto_one, | ||
| 212 | .fn_proto, | ||
| 213 | .fn_decl, | ||
| 214 | .anyframe_type, | ||
| 215 | .anyframe_literal, | ||
| 216 | .error_set_decl, | ||
| 217 | .container_decl, | ||
| 218 | .container_decl_trailing, | ||
| 219 | .container_decl_two, | ||
| 220 | .container_decl_two_trailing, | ||
| 221 | .container_decl_arg, | ||
| 222 | .container_decl_arg_trailing, | ||
| 223 | .tagged_union, | ||
| 224 | .tagged_union_trailing, | ||
| 225 | .tagged_union_two, | ||
| 226 | .tagged_union_two_trailing, | ||
| 227 | .tagged_union_enum_tag, | ||
| 228 | .tagged_union_enum_tag_trailing, | ||
| 229 | .@"comptime", | ||
| 230 | .@"nosuspend", | ||
| 231 | .error_value, | ||
| 175 | => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}), | 232 | => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}), |
| 176 | 233 | ||
| 177 | // @field can be assigned to | 234 | .builtin_call, |
| 178 | .BuiltinCall => { | 235 | .builtin_call_comma, |
| 179 | const call = node.castTag(.BuiltinCall).?; | 236 | .builtin_call_two, |
| 180 | const tree = scope.tree(); | 237 | .builtin_call_two_comma, |
| 181 | const builtin_name = tree.tokenSlice(call.builtin_token); | 238 | => { |
| 182 | 239 | const builtin_token = main_tokens[node]; | |
| 183 | if (!mem.eql(u8, builtin_name, "@field")) { | 240 | const builtin_name = tree.tokenSlice(builtin_token); |
| 184 | return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}); | 241 | // If the builtin is an invalid name, we don't cause an error here; instead |
| 242 | // let it pass, and the error will be "invalid builtin function" later. | ||
| 243 | if (BuiltinFn.list.get(builtin_name)) |info| { | ||
| 244 | if (!info.allows_lvalue) { | ||
| 245 | return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}); | ||
| 246 | } | ||
| 185 | } | 247 | } |
| 186 | }, | 248 | }, |
| 187 | 249 | ||
| 188 | // can be assigned to | 250 | // These can be assigned to. |
| 189 | .UnwrapOptional, | 251 | .unwrap_optional, |
| 190 | .Deref, | 252 | .deref, |
| 191 | .Period, | 253 | .field_access, |
| 192 | .ArrayAccess, | 254 | .array_access, |
| 193 | .Identifier, | 255 | .identifier, |
| 194 | .GroupedExpression, | 256 | .grouped_expression, |
| 195 | .OrElse, | 257 | .@"orelse", |
| 196 | => {}, | 258 | => {}, |
| 197 | } | 259 | } |
| 198 | return expr(mod, scope, .ref, node); | 260 | return expr(mod, scope, .ref, node); |
| ... | @@ -202,154 +264,403 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { | ... | @@ -202,154 +264,403 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { |
| 202 | /// When `rl` is discard, ptr, inferred_ptr, bitcasted_ptr, or inferred_ptr, the | 264 | /// When `rl` is discard, ptr, inferred_ptr, bitcasted_ptr, or inferred_ptr, the |
| 203 | /// result instruction can be used to inspect whether it is isNoReturn() but that is it, | 265 | /// result instruction can be used to inspect whether it is isNoReturn() but that is it, |
| 204 | /// it must otherwise not be used. | 266 | /// it must otherwise not be used. |
| 205 | pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst { | 267 | pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst { |
| 206 | switch (node.tag) { | 268 | const tree = scope.tree(); |
| 207 | .Root => unreachable, // Top-level declaration. | 269 | const main_tokens = tree.nodes.items(.main_token); |
| 208 | .Use => unreachable, // Top-level declaration. | 270 | const token_tags = tree.tokens.items(.tag); |
| 209 | .TestDecl => unreachable, // Top-level declaration. | 271 | const node_datas = tree.nodes.items(.data); |
| 210 | .DocComment => unreachable, // Top-level declaration. | 272 | const node_tags = tree.nodes.items(.tag); |
| 211 | .VarDecl => unreachable, // Handled in `blockExpr`. | 273 | const token_starts = tree.tokens.items(.start); |
| 212 | .SwitchCase => unreachable, // Handled in `switchExpr`. | 274 | |
| 213 | .SwitchElse => unreachable, // Handled in `switchExpr`. | 275 | switch (node_tags[node]) { |
| 214 | .Range => unreachable, // Handled in `switchExpr`. | 276 | .root => unreachable, // Top-level declaration. |
| 215 | .Else => unreachable, // Handled explicitly the control flow expression functions. | 277 | .@"usingnamespace" => unreachable, // Top-level declaration. |
| 216 | .Payload => unreachable, // Handled explicitly. | 278 | .test_decl => unreachable, // Top-level declaration. |
| 217 | .PointerPayload => unreachable, // Handled explicitly. | 279 | .container_field_init => unreachable, // Top-level declaration. |
| 218 | .PointerIndexPayload => unreachable, // Handled explicitly. | 280 | .container_field_align => unreachable, // Top-level declaration. |
| 219 | .ErrorTag => unreachable, // Handled explicitly. | 281 | .container_field => unreachable, // Top-level declaration. |
| 220 | .FieldInitializer => unreachable, // Handled explicitly. | 282 | .fn_decl => unreachable, // Top-level declaration. |
| 221 | .ContainerField => unreachable, // Handled explicitly. | 283 | |
| 222 | 284 | .global_var_decl => unreachable, // Handled in `blockExpr`. | |
| 223 | .Assign => return rvalueVoid(mod, scope, rl, node, try assign(mod, scope, node.castTag(.Assign).?)), | 285 | .local_var_decl => unreachable, // Handled in `blockExpr`. |
| 224 | .AssignBitAnd => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitAnd).?, .bit_and)), | 286 | .simple_var_decl => unreachable, // Handled in `blockExpr`. |
| 225 | .AssignBitOr => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitOr).?, .bit_or)), | 287 | .aligned_var_decl => unreachable, // Handled in `blockExpr`. |
| 226 | .AssignBitShiftLeft => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftLeft).?, .shl)), | 288 | |
| 227 | .AssignBitShiftRight => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftRight).?, .shr)), | 289 | .switch_case => unreachable, // Handled in `switchExpr`. |
| 228 | .AssignBitXor => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitXor).?, .xor)), | 290 | .switch_case_one => unreachable, // Handled in `switchExpr`. |
| 229 | .AssignDiv => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignDiv).?, .div)), | 291 | .switch_range => unreachable, // Handled in `switchExpr`. |
| 230 | .AssignSub => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSub).?, .sub)), | 292 | |
| 231 | .AssignSubWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSubWrap).?, .subwrap)), | 293 | .asm_output => unreachable, // Handled in `asmExpr`. |
| 232 | .AssignMod => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMod).?, .mod_rem)), | 294 | .asm_input => unreachable, // Handled in `asmExpr`. |
| 233 | .AssignAdd => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAdd).?, .add)), | 295 | |
| 234 | .AssignAddWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAddWrap).?, .addwrap)), | 296 | .assign => return rvalueVoid(mod, scope, rl, node, try assign(mod, scope, node)), |
| 235 | .AssignMul => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMul).?, .mul)), | 297 | .assign_bit_and => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .bit_and)), |
| 236 | .AssignMulWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMulWrap).?, .mulwrap)), | 298 | .assign_bit_or => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .bit_or)), |
| 237 | 299 | .assign_bit_shift_left => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .shl)), | |
| 238 | .Add => return simpleBinOp(mod, scope, rl, node.castTag(.Add).?, .add), | 300 | .assign_bit_shift_right => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .shr)), |
| 239 | .AddWrap => return simpleBinOp(mod, scope, rl, node.castTag(.AddWrap).?, .addwrap), | 301 | .assign_bit_xor => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .xor)), |
| 240 | .Sub => return simpleBinOp(mod, scope, rl, node.castTag(.Sub).?, .sub), | 302 | .assign_div => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .div)), |
| 241 | .SubWrap => return simpleBinOp(mod, scope, rl, node.castTag(.SubWrap).?, .subwrap), | 303 | .assign_sub => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .sub)), |
| 242 | .Mul => return simpleBinOp(mod, scope, rl, node.castTag(.Mul).?, .mul), | 304 | .assign_sub_wrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .subwrap)), |
| 243 | .MulWrap => return simpleBinOp(mod, scope, rl, node.castTag(.MulWrap).?, .mulwrap), | 305 | .assign_mod => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .mod_rem)), |
| 244 | .Div => return simpleBinOp(mod, scope, rl, node.castTag(.Div).?, .div), | 306 | .assign_add => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .add)), |
| 245 | .Mod => return simpleBinOp(mod, scope, rl, node.castTag(.Mod).?, .mod_rem), | 307 | .assign_add_wrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .addwrap)), |
| 246 | .BitAnd => return simpleBinOp(mod, scope, rl, node.castTag(.BitAnd).?, .bit_and), | 308 | .assign_mul => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .mul)), |
| 247 | .BitOr => return simpleBinOp(mod, scope, rl, node.castTag(.BitOr).?, .bit_or), | 309 | .assign_mul_wrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .mulwrap)), |
| 248 | .BitShiftLeft => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftLeft).?, .shl), | 310 | |
| 249 | .BitShiftRight => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftRight).?, .shr), | 311 | .add => return simpleBinOp(mod, scope, rl, node, .add), |
| 250 | .BitXor => return simpleBinOp(mod, scope, rl, node.castTag(.BitXor).?, .xor), | 312 | .add_wrap => return simpleBinOp(mod, scope, rl, node, .addwrap), |
| 251 | 313 | .sub => return simpleBinOp(mod, scope, rl, node, .sub), | |
| 252 | .BangEqual => return simpleBinOp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq), | 314 | .sub_wrap => return simpleBinOp(mod, scope, rl, node, .subwrap), |
| 253 | .EqualEqual => return simpleBinOp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq), | 315 | .mul => return simpleBinOp(mod, scope, rl, node, .mul), |
| 254 | .GreaterThan => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt), | 316 | .mul_wrap => return simpleBinOp(mod, scope, rl, node, .mulwrap), |
| 255 | .GreaterOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte), | 317 | .div => return simpleBinOp(mod, scope, rl, node, .div), |
| 256 | .LessThan => return simpleBinOp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt), | 318 | .mod => return simpleBinOp(mod, scope, rl, node, .mod_rem), |
| 257 | .LessOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte), | 319 | .bit_and => return simpleBinOp(mod, scope, rl, node, .bit_and), |
| 258 | 320 | .bit_or => return simpleBinOp(mod, scope, rl, node, .bit_or), | |
| 259 | .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat), | 321 | .bit_shift_left => return simpleBinOp(mod, scope, rl, node, .shl), |
| 260 | .ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul), | 322 | .bit_shift_right => return simpleBinOp(mod, scope, rl, node, .shr), |
| 261 | 323 | .bit_xor => return simpleBinOp(mod, scope, rl, node, .xor), | |
| 262 | .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?), | 324 | |
| 263 | .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?), | 325 | .bang_equal => return simpleBinOp(mod, scope, rl, node, .cmp_neq), |
| 264 | 326 | .equal_equal => return simpleBinOp(mod, scope, rl, node, .cmp_eq), | |
| 265 | .BoolNot => return rvalue(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)), | 327 | .greater_than => return simpleBinOp(mod, scope, rl, node, .cmp_gt), |
| 266 | .BitNot => return rvalue(mod, scope, rl, try bitNot(mod, scope, node.castTag(.BitNot).?)), | 328 | .greater_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_gte), |
| 267 | .Negation => return rvalue(mod, scope, rl, try negation(mod, scope, node.castTag(.Negation).?, .sub)), | 329 | .less_than => return simpleBinOp(mod, scope, rl, node, .cmp_lt), |
| 268 | .NegationWrap => return rvalue(mod, scope, rl, try negation(mod, scope, node.castTag(.NegationWrap).?, .subwrap)), | 330 | .less_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_lte), |
| 269 | 331 | ||
| 270 | .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?), | 332 | .array_cat => return simpleBinOp(mod, scope, rl, node, .array_cat), |
| 271 | .Asm => return rvalue(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)), | 333 | .array_mult => return simpleBinOp(mod, scope, rl, node, .array_mul), |
| 272 | .StringLiteral => return rvalue(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)), | 334 | |
| 273 | .IntegerLiteral => return rvalue(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)), | 335 | .bool_and => return boolBinOp(mod, scope, rl, node, true), |
| 274 | .BuiltinCall => return builtinCall(mod, scope, rl, node.castTag(.BuiltinCall).?), | 336 | .bool_or => return boolBinOp(mod, scope, rl, node, false), |
| 275 | .Call => return callExpr(mod, scope, rl, node.castTag(.Call).?), | 337 | |
| 276 | .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?), | 338 | .bool_not => return rvalue(mod, scope, rl, try boolNot(mod, scope, node)), |
| 277 | .Return => return ret(mod, scope, node.castTag(.Return).?), | 339 | .bit_not => return rvalue(mod, scope, rl, try bitNot(mod, scope, node)), |
| 278 | .If => return ifExpr(mod, scope, rl, node.castTag(.If).?), | 340 | .negation => return rvalue(mod, scope, rl, try negation(mod, scope, node, .sub)), |
| 279 | .While => return whileExpr(mod, scope, rl, node.castTag(.While).?), | 341 | .negation_wrap => return rvalue(mod, scope, rl, try negation(mod, scope, node, .subwrap)), |
| 280 | .Period => return field(mod, scope, rl, node.castTag(.Period).?), | 342 | |
| 281 | .Deref => return rvalue(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)), | 343 | .identifier => return identifier(mod, scope, rl, node), |
| 282 | .AddressOf => return rvalue(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)), | 344 | |
| 283 | .FloatLiteral => return rvalue(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)), | 345 | .asm_simple => return asmExpr(mod, scope, rl, tree.asmSimple(node)), |
| 284 | .UndefinedLiteral => return rvalue(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)), | 346 | .@"asm" => return asmExpr(mod, scope, rl, tree.asmFull(node)), |
| 285 | .BoolLiteral => return rvalue(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)), | 347 | |
| 286 | .NullLiteral => return rvalue(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)), | 348 | .string_literal => return stringLiteral(mod, scope, rl, node), |
| 287 | .OptionalType => return rvalue(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)), | 349 | .multiline_string_literal => return multilineStringLiteral(mod, scope, rl, node), |
| 288 | .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?), | 350 | |
| 289 | .Block => return rvalueVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)), | 351 | .integer_literal => return integerLiteral(mod, scope, rl, node), |
| 290 | .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block), | 352 | |
| 291 | .Break => return rvalue(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)), | 353 | .builtin_call_two, .builtin_call_two_comma => { |
| 292 | .Continue => return rvalue(mod, scope, rl, try continueExpr(mod, scope, node.castTag(.Continue).?)), | 354 | if (node_datas[node].lhs == 0) { |
| 293 | .PtrType => return rvalue(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)), | 355 | const params = [_]ast.Node.Index{}; |
| 294 | .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr), | 356 | return builtinCall(mod, scope, rl, node, &params); |
| 295 | .ArrayType => return rvalue(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)), | 357 | } else if (node_datas[node].rhs == 0) { |
| 296 | .ArrayTypeSentinel => return rvalue(mod, scope, rl, try arrayTypeSentinel(mod, scope, node.castTag(.ArrayTypeSentinel).?)), | 358 | const params = [_]ast.Node.Index{node_datas[node].lhs}; |
| 297 | .EnumLiteral => return rvalue(mod, scope, rl, try enumLiteral(mod, scope, node.castTag(.EnumLiteral).?)), | 359 | return builtinCall(mod, scope, rl, node, &params); |
| 298 | .MultilineStringLiteral => return rvalue(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)), | 360 | } else { |
| 299 | .CharLiteral => return rvalue(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)), | 361 | const params = [_]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; |
| 300 | .SliceType => return rvalue(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)), | 362 | return builtinCall(mod, scope, rl, node, &params); |
| 301 | .ErrorUnion => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)), | 363 | } |
| 302 | .MergeErrorSets => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)), | 364 | }, |
| 303 | .AnyFrameType => return rvalue(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)), | 365 | .builtin_call, .builtin_call_comma => { |
| 304 | .ErrorSetDecl => return rvalue(mod, scope, rl, try errorSetDecl(mod, scope, node.castTag(.ErrorSetDecl).?)), | 366 | const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; |
| 305 | .ErrorType => return rvalue(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)), | 367 | return builtinCall(mod, scope, rl, node, params); |
| 306 | .For => return forExpr(mod, scope, rl, node.castTag(.For).?), | 368 | }, |
| 307 | .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?), | 369 | |
| 308 | .Slice => return rvalue(mod, scope, rl, try sliceExpr(mod, scope, node.castTag(.Slice).?)), | 370 | .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => { |
| 309 | .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?), | 371 | var params: [1]ast.Node.Index = undefined; |
| 310 | .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?), | 372 | return callExpr(mod, scope, rl, tree.callOne(&params, node)); |
| 311 | .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?), | 373 | }, |
| 312 | .Switch => return switchExpr(mod, scope, rl, node.castTag(.Switch).?), | 374 | .call, .call_comma, .async_call, .async_call_comma => { |
| 313 | .ContainerDecl => return containerDecl(mod, scope, rl, node.castTag(.ContainerDecl).?), | 375 | return callExpr(mod, scope, rl, tree.callFull(node)); |
| 314 | 376 | }, | |
| 315 | .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), | 377 | |
| 316 | .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}), | 378 | .unreachable_literal => { |
| 317 | .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}), | 379 | const main_token = main_tokens[node]; |
| 318 | .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), | 380 | const src = token_starts[main_token]; |
| 319 | .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}), | 381 | return addZIRNoOp(mod, scope, src, .unreachable_safe); |
| 320 | .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}), | 382 | }, |
| 321 | .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}), | 383 | .@"return" => return ret(mod, scope, node), |
| 322 | .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}), | 384 | .field_access => return fieldAccess(mod, scope, rl, node), |
| 323 | .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}), | 385 | .float_literal => return floatLiteral(mod, scope, rl, node), |
| 324 | .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}), | 386 | |
| 325 | .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}), | 387 | .if_simple => return ifExpr(mod, scope, rl, tree.ifSimple(node)), |
| 326 | .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}), | 388 | .@"if" => return ifExpr(mod, scope, rl, tree.ifFull(node)), |
| 327 | } | 389 | |
| 328 | } | 390 | .while_simple => return whileExpr(mod, scope, rl, tree.whileSimple(node)), |
| 329 | 391 | .while_cont => return whileExpr(mod, scope, rl, tree.whileCont(node)), | |
| 330 | fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Comptime) InnerError!*zir.Inst { | 392 | .@"while" => return whileExpr(mod, scope, rl, tree.whileFull(node)), |
| 331 | const tracy = trace(@src()); | 393 | |
| 332 | defer tracy.end(); | 394 | .for_simple => return forExpr(mod, scope, rl, tree.forSimple(node)), |
| 395 | .@"for" => return forExpr(mod, scope, rl, tree.forFull(node)), | ||
| 396 | |||
| 397 | // TODO handling these separately would actually be simpler & have fewer branches | ||
| 398 | // once we have a ZIR instruction for each of these 3 cases. | ||
| 399 | .slice_open => return sliceExpr(mod, scope, rl, tree.sliceOpen(node)), | ||
| 400 | .slice => return sliceExpr(mod, scope, rl, tree.slice(node)), | ||
| 401 | .slice_sentinel => return sliceExpr(mod, scope, rl, tree.sliceSentinel(node)), | ||
| 402 | |||
| 403 | .deref => { | ||
| 404 | const lhs = try expr(mod, scope, .none, node_datas[node].lhs); | ||
| 405 | const src = token_starts[main_tokens[node]]; | ||
| 406 | const result = try addZIRUnOp(mod, scope, src, .deref, lhs); | ||
| 407 | return rvalue(mod, scope, rl, result); | ||
| 408 | }, | ||
| 409 | .address_of => { | ||
| 410 | const result = try expr(mod, scope, .ref, node_datas[node].lhs); | ||
| 411 | return rvalue(mod, scope, rl, result); | ||
| 412 | }, | ||
| 413 | .undefined_literal => { | ||
| 414 | const main_token = main_tokens[node]; | ||
| 415 | const src = token_starts[main_token]; | ||
| 416 | const result = try addZIRInstConst(mod, scope, src, .{ | ||
| 417 | .ty = Type.initTag(.@"undefined"), | ||
| 418 | .val = Value.initTag(.undef), | ||
| 419 | }); | ||
| 420 | return rvalue(mod, scope, rl, result); | ||
| 421 | }, | ||
| 422 | .true_literal => { | ||
| 423 | const main_token = main_tokens[node]; | ||
| 424 | const src = token_starts[main_token]; | ||
| 425 | const result = try addZIRInstConst(mod, scope, src, .{ | ||
| 426 | .ty = Type.initTag(.bool), | ||
| 427 | .val = Value.initTag(.bool_true), | ||
| 428 | }); | ||
| 429 | return rvalue(mod, scope, rl, result); | ||
| 430 | }, | ||
| 431 | .false_literal => { | ||
| 432 | const main_token = main_tokens[node]; | ||
| 433 | const src = token_starts[main_token]; | ||
| 434 | const result = try addZIRInstConst(mod, scope, src, .{ | ||
| 435 | .ty = Type.initTag(.bool), | ||
| 436 | .val = Value.initTag(.bool_false), | ||
| 437 | }); | ||
| 438 | return rvalue(mod, scope, rl, result); | ||
| 439 | }, | ||
| 440 | .null_literal => { | ||
| 441 | const main_token = main_tokens[node]; | ||
| 442 | const src = token_starts[main_token]; | ||
| 443 | const result = try addZIRInstConst(mod, scope, src, .{ | ||
| 444 | .ty = Type.initTag(.@"null"), | ||
| 445 | .val = Value.initTag(.null_value), | ||
| 446 | }); | ||
| 447 | return rvalue(mod, scope, rl, result); | ||
| 448 | }, | ||
| 449 | .optional_type => { | ||
| 450 | const src = token_starts[main_tokens[node]]; | ||
| 451 | const operand = try typeExpr(mod, scope, node_datas[node].lhs); | ||
| 452 | const result = try addZIRUnOp(mod, scope, src, .optional_type, operand); | ||
| 453 | return rvalue(mod, scope, rl, result); | ||
| 454 | }, | ||
| 455 | .unwrap_optional => { | ||
| 456 | const operand = try expr(mod, scope, rl, node_datas[node].lhs); | ||
| 457 | const op: zir.Inst.Tag = switch (rl) { | ||
| 458 | .ref => .optional_payload_safe_ptr, | ||
| 459 | else => .optional_payload_safe, | ||
| 460 | }; | ||
| 461 | const src = token_starts[main_tokens[node]]; | ||
| 462 | return addZIRUnOp(mod, scope, src, op, operand); | ||
| 463 | }, | ||
| 464 | .block_two, .block_two_semicolon => { | ||
| 465 | const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; | ||
| 466 | if (node_datas[node].lhs == 0) { | ||
| 467 | return blockExpr(mod, scope, rl, node, statements[0..0]); | ||
| 468 | } else if (node_datas[node].rhs == 0) { | ||
| 469 | return blockExpr(mod, scope, rl, node, statements[0..1]); | ||
| 470 | } else { | ||
| 471 | return blockExpr(mod, scope, rl, node, statements[0..2]); | ||
| 472 | } | ||
| 473 | }, | ||
| 474 | .block, .block_semicolon => { | ||
| 475 | const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; | ||
| 476 | return blockExpr(mod, scope, rl, node, statements); | ||
| 477 | }, | ||
| 478 | .enum_literal => { | ||
| 479 | const ident_token = main_tokens[node]; | ||
| 480 | const name = try mod.identifierTokenString(scope, ident_token); | ||
| 481 | const src = token_starts[ident_token]; | ||
| 482 | const result = try addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{}); | ||
| 483 | return rvalue(mod, scope, rl, result); | ||
| 484 | }, | ||
| 485 | .error_union => { | ||
| 486 | const error_set = try typeExpr(mod, scope, node_datas[node].lhs); | ||
| 487 | const payload = try typeExpr(mod, scope, node_datas[node].rhs); | ||
| 488 | const src = token_starts[main_tokens[node]]; | ||
| 489 | const result = try addZIRBinOp(mod, scope, src, .error_union_type, error_set, payload); | ||
| 490 | return rvalue(mod, scope, rl, result); | ||
| 491 | }, | ||
| 492 | .merge_error_sets => { | ||
| 493 | const lhs = try typeExpr(mod, scope, node_datas[node].lhs); | ||
| 494 | const rhs = try typeExpr(mod, scope, node_datas[node].rhs); | ||
| 495 | const src = token_starts[main_tokens[node]]; | ||
| 496 | const result = try addZIRBinOp(mod, scope, src, .merge_error_sets, lhs, rhs); | ||
| 497 | return rvalue(mod, scope, rl, result); | ||
| 498 | }, | ||
| 499 | .anyframe_literal => { | ||
| 500 | const main_token = main_tokens[node]; | ||
| 501 | const src = token_starts[main_token]; | ||
| 502 | const result = try addZIRInstConst(mod, scope, src, .{ | ||
| 503 | .ty = Type.initTag(.type), | ||
| 504 | .val = Value.initTag(.anyframe_type), | ||
| 505 | }); | ||
| 506 | return rvalue(mod, scope, rl, result); | ||
| 507 | }, | ||
| 508 | .anyframe_type => { | ||
| 509 | const src = token_starts[node_datas[node].lhs]; | ||
| 510 | const return_type = try typeExpr(mod, scope, node_datas[node].rhs); | ||
| 511 | const result = try addZIRUnOp(mod, scope, src, .anyframe_type, return_type); | ||
| 512 | return rvalue(mod, scope, rl, result); | ||
| 513 | }, | ||
| 514 | .@"catch" => { | ||
| 515 | const catch_token = main_tokens[node]; | ||
| 516 | const payload_token: ?ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe) | ||
| 517 | catch_token + 2 | ||
| 518 | else | ||
| 519 | null; | ||
| 520 | switch (rl) { | ||
| 521 | .ref => return orelseCatchExpr( | ||
| 522 | mod, | ||
| 523 | scope, | ||
| 524 | rl, | ||
| 525 | node_datas[node].lhs, | ||
| 526 | main_tokens[node], | ||
| 527 | .is_err_ptr, | ||
| 528 | .err_union_payload_unsafe_ptr, | ||
| 529 | .err_union_code_ptr, | ||
| 530 | node_datas[node].rhs, | ||
| 531 | payload_token, | ||
| 532 | ), | ||
| 533 | else => return orelseCatchExpr( | ||
| 534 | mod, | ||
| 535 | scope, | ||
| 536 | rl, | ||
| 537 | node_datas[node].lhs, | ||
| 538 | main_tokens[node], | ||
| 539 | .is_err, | ||
| 540 | .err_union_payload_unsafe, | ||
| 541 | .err_union_code, | ||
| 542 | node_datas[node].rhs, | ||
| 543 | payload_token, | ||
| 544 | ), | ||
| 545 | } | ||
| 546 | }, | ||
| 547 | .@"orelse" => switch (rl) { | ||
| 548 | .ref => return orelseCatchExpr( | ||
| 549 | mod, | ||
| 550 | scope, | ||
| 551 | rl, | ||
| 552 | node_datas[node].lhs, | ||
| 553 | main_tokens[node], | ||
| 554 | .is_null_ptr, | ||
| 555 | .optional_payload_unsafe_ptr, | ||
| 556 | undefined, | ||
| 557 | node_datas[node].rhs, | ||
| 558 | null, | ||
| 559 | ), | ||
| 560 | else => return orelseCatchExpr( | ||
| 561 | mod, | ||
| 562 | scope, | ||
| 563 | rl, | ||
| 564 | node_datas[node].lhs, | ||
| 565 | main_tokens[node], | ||
| 566 | .is_null, | ||
| 567 | .optional_payload_unsafe, | ||
| 568 | undefined, | ||
| 569 | node_datas[node].rhs, | ||
| 570 | null, | ||
| 571 | ), | ||
| 572 | }, | ||
| 333 | 573 | ||
| 334 | return comptimeExpr(mod, scope, rl, node.expr); | 574 | .ptr_type_aligned => return ptrType(mod, scope, rl, tree.ptrTypeAligned(node)), |
| 575 | .ptr_type_sentinel => return ptrType(mod, scope, rl, tree.ptrTypeSentinel(node)), | ||
| 576 | .ptr_type => return ptrType(mod, scope, rl, tree.ptrType(node)), | ||
| 577 | .ptr_type_bit_range => return ptrType(mod, scope, rl, tree.ptrTypeBitRange(node)), | ||
| 578 | |||
| 579 | .container_decl, | ||
| 580 | .container_decl_trailing, | ||
| 581 | => return containerDecl(mod, scope, rl, tree.containerDecl(node)), | ||
| 582 | .container_decl_two, .container_decl_two_trailing => { | ||
| 583 | var buffer: [2]ast.Node.Index = undefined; | ||
| 584 | return containerDecl(mod, scope, rl, tree.containerDeclTwo(&buffer, node)); | ||
| 585 | }, | ||
| 586 | .container_decl_arg, | ||
| 587 | .container_decl_arg_trailing, | ||
| 588 | => return containerDecl(mod, scope, rl, tree.containerDeclArg(node)), | ||
| 589 | |||
| 590 | .tagged_union, | ||
| 591 | .tagged_union_trailing, | ||
| 592 | => return containerDecl(mod, scope, rl, tree.taggedUnion(node)), | ||
| 593 | .tagged_union_two, .tagged_union_two_trailing => { | ||
| 594 | var buffer: [2]ast.Node.Index = undefined; | ||
| 595 | return containerDecl(mod, scope, rl, tree.taggedUnionTwo(&buffer, node)); | ||
| 596 | }, | ||
| 597 | .tagged_union_enum_tag, | ||
| 598 | .tagged_union_enum_tag_trailing, | ||
| 599 | => return containerDecl(mod, scope, rl, tree.taggedUnionEnumTag(node)), | ||
| 600 | |||
| 601 | .@"break" => return breakExpr(mod, scope, rl, node), | ||
| 602 | .@"continue" => return continueExpr(mod, scope, rl, node), | ||
| 603 | .grouped_expression => return expr(mod, scope, rl, node_datas[node].lhs), | ||
| 604 | .array_type => return arrayType(mod, scope, rl, node), | ||
| 605 | .array_type_sentinel => return arrayTypeSentinel(mod, scope, rl, node), | ||
| 606 | .char_literal => return charLiteral(mod, scope, rl, node), | ||
| 607 | .error_set_decl => return errorSetDecl(mod, scope, rl, node), | ||
| 608 | .array_access => return arrayAccess(mod, scope, rl, node), | ||
| 609 | .@"comptime" => return comptimeExpr(mod, scope, rl, node_datas[node].lhs), | ||
| 610 | .@"switch", .switch_comma => return switchExpr(mod, scope, rl, node), | ||
| 611 | |||
| 612 | .@"defer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .defer", .{}), | ||
| 613 | .@"errdefer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .errdefer", .{}), | ||
| 614 | .@"await" => return mod.failNode(scope, node, "TODO implement astgen.expr for .await", .{}), | ||
| 615 | .@"resume" => return mod.failNode(scope, node, "TODO implement astgen.expr for .resume", .{}), | ||
| 616 | .@"try" => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), | ||
| 617 | |||
| 618 | .array_init_one, | ||
| 619 | .array_init_one_comma, | ||
| 620 | .array_init_dot_two, | ||
| 621 | .array_init_dot_two_comma, | ||
| 622 | .array_init_dot, | ||
| 623 | .array_init_dot_comma, | ||
| 624 | .array_init, | ||
| 625 | .array_init_comma, | ||
| 626 | => return mod.failNode(scope, node, "TODO implement astgen.expr for array literals", .{}), | ||
| 627 | |||
| 628 | .struct_init_one, | ||
| 629 | .struct_init_one_comma, | ||
| 630 | .struct_init_dot_two, | ||
| 631 | .struct_init_dot_two_comma, | ||
| 632 | .struct_init_dot, | ||
| 633 | .struct_init_dot_comma, | ||
| 634 | .struct_init, | ||
| 635 | .struct_init_comma, | ||
| 636 | => return mod.failNode(scope, node, "TODO implement astgen.expr for struct literals", .{}), | ||
| 637 | |||
| 638 | .@"suspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .suspend", .{}), | ||
| 639 | .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}), | ||
| 640 | .fn_proto_simple, | ||
| 641 | .fn_proto_multi, | ||
| 642 | .fn_proto_one, | ||
| 643 | .fn_proto, | ||
| 644 | => return mod.failNode(scope, node, "TODO implement astgen.expr for function prototypes", .{}), | ||
| 645 | |||
| 646 | .@"nosuspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .nosuspend", .{}), | ||
| 647 | .error_value => return mod.failNode(scope, node, "TODO implement astgen.expr for .error_value", .{}), | ||
| 648 | } | ||
| 335 | } | 649 | } |
| 336 | 650 | ||
| 337 | pub fn comptimeExpr( | 651 | pub fn comptimeExpr( |
| 338 | mod: *Module, | 652 | mod: *Module, |
| 339 | parent_scope: *Scope, | 653 | parent_scope: *Scope, |
| 340 | rl: ResultLoc, | 654 | rl: ResultLoc, |
| 341 | node: *ast.Node, | 655 | node: ast.Node.Index, |
| 342 | ) InnerError!*zir.Inst { | 656 | ) InnerError!*zir.Inst { |
| 343 | // If we are already in a comptime scope, no need to make another one. | 657 | // If we are already in a comptime scope, no need to make another one. |
| 344 | if (parent_scope.isComptime()) { | 658 | if (parent_scope.isComptime()) { |
| 345 | return expr(mod, parent_scope, rl, node); | 659 | return expr(mod, parent_scope, rl, node); |
| 346 | } | 660 | } |
| 347 | 661 | ||
| 348 | // Optimization for labeled blocks: don't need to have 2 layers of blocks, | 662 | const tree = parent_scope.tree(); |
| 349 | // we can reuse the existing one. | 663 | const token_starts = tree.tokens.items(.start); |
| 350 | if (node.castTag(.LabeledBlock)) |block_node| { | ||
| 351 | return labeledBlockExpr(mod, parent_scope, rl, block_node, .block_comptime); | ||
| 352 | } | ||
| 353 | 664 | ||
| 354 | // Make a scope to collect generated instructions in the sub-expression. | 665 | // Make a scope to collect generated instructions in the sub-expression. |
| 355 | var block_scope: Scope.GenZIR = .{ | 666 | var block_scope: Scope.GenZIR = .{ |
| ... | @@ -365,9 +676,7 @@ pub fn comptimeExpr( | ... | @@ -365,9 +676,7 @@ pub fn comptimeExpr( |
| 365 | // instruction is the block's result value. | 676 | // instruction is the block's result value. |
| 366 | _ = try expr(mod, &block_scope.base, rl, node); | 677 | _ = try expr(mod, &block_scope.base, rl, node); |
| 367 | 678 | ||
| 368 | const tree = parent_scope.tree(); | 679 | const src = token_starts[tree.firstToken(node)]; |
| 369 | const src = tree.token_locs[node.firstToken()].start; | ||
| 370 | |||
| 371 | const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{ | 680 | const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{ |
| 372 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), | 681 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), |
| 373 | }); | 682 | }); |
| ... | @@ -378,10 +687,17 @@ pub fn comptimeExpr( | ... | @@ -378,10 +687,17 @@ pub fn comptimeExpr( |
| 378 | fn breakExpr( | 687 | fn breakExpr( |
| 379 | mod: *Module, | 688 | mod: *Module, |
| 380 | parent_scope: *Scope, | 689 | parent_scope: *Scope, |
| 381 | node: *ast.Node.ControlFlowExpression, | 690 | rl: ResultLoc, |
| 691 | node: ast.Node.Index, | ||
| 382 | ) InnerError!*zir.Inst { | 692 | ) InnerError!*zir.Inst { |
| 383 | const tree = parent_scope.tree(); | 693 | const tree = parent_scope.tree(); |
| 384 | const src = tree.token_locs[node.ltoken].start; | 694 | const node_datas = tree.nodes.items(.data); |
| 695 | const main_tokens = tree.nodes.items(.main_token); | ||
| 696 | const token_starts = tree.tokens.items(.start); | ||
| 697 | |||
| 698 | const src = token_starts[main_tokens[node]]; | ||
| 699 | const break_label = node_datas[node].lhs; | ||
| 700 | const rhs = node_datas[node].rhs; | ||
| 385 | 701 | ||
| 386 | // Look for the label in the scope. | 702 | // Look for the label in the scope. |
| 387 | var scope = parent_scope; | 703 | var scope = parent_scope; |
| ... | @@ -391,7 +707,7 @@ fn breakExpr( | ... | @@ -391,7 +707,7 @@ fn breakExpr( |
| 391 | const gen_zir = scope.cast(Scope.GenZIR).?; | 707 | const gen_zir = scope.cast(Scope.GenZIR).?; |
| 392 | 708 | ||
| 393 | const block_inst = blk: { | 709 | const block_inst = blk: { |
| 394 | if (node.getLabel()) |break_label| { | 710 | if (break_label != 0) { |
| 395 | if (gen_zir.label) |*label| { | 711 | if (gen_zir.label) |*label| { |
| 396 | if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) { | 712 | if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) { |
| 397 | label.used = true; | 713 | label.used = true; |
| ... | @@ -405,11 +721,12 @@ fn breakExpr( | ... | @@ -405,11 +721,12 @@ fn breakExpr( |
| 405 | continue; | 721 | continue; |
| 406 | }; | 722 | }; |
| 407 | 723 | ||
| 408 | const rhs = node.getRHS() orelse { | 724 | if (rhs == 0) { |
| 409 | return addZirInstTag(mod, parent_scope, src, .break_void, .{ | 725 | const result = try addZirInstTag(mod, parent_scope, src, .break_void, .{ |
| 410 | .block = block_inst, | 726 | .block = block_inst, |
| 411 | }); | 727 | }); |
| 412 | }; | 728 | return rvalue(mod, parent_scope, rl, result); |
| 729 | } | ||
| 413 | gen_zir.break_count += 1; | 730 | gen_zir.break_count += 1; |
| 414 | const prev_rvalue_rl_count = gen_zir.rvalue_rl_count; | 731 | const prev_rvalue_rl_count = gen_zir.rvalue_rl_count; |
| 415 | const operand = try expr(mod, parent_scope, gen_zir.break_result_loc, rhs); | 732 | const operand = try expr(mod, parent_scope, gen_zir.break_result_loc, rhs); |
| ... | @@ -429,11 +746,11 @@ fn breakExpr( | ... | @@ -429,11 +746,11 @@ fn breakExpr( |
| 429 | try gen_zir.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst); | 746 | try gen_zir.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst); |
| 430 | } | 747 | } |
| 431 | } | 748 | } |
| 432 | return br; | 749 | return rvalue(mod, parent_scope, rl, br); |
| 433 | }, | 750 | }, |
| 434 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, | 751 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, |
| 435 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, | 752 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, |
| 436 | else => if (node.getLabel()) |break_label| { | 753 | else => if (break_label != 0) { |
| 437 | const label_name = try mod.identifierTokenString(parent_scope, break_label); | 754 | const label_name = try mod.identifierTokenString(parent_scope, break_label); |
| 438 | return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); | 755 | return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); |
| 439 | } else { | 756 | } else { |
| ... | @@ -443,9 +760,19 @@ fn breakExpr( | ... | @@ -443,9 +760,19 @@ fn breakExpr( |
| 443 | } | 760 | } |
| 444 | } | 761 | } |
| 445 | 762 | ||
| 446 | fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { | 763 | fn continueExpr( |
| 764 | mod: *Module, | ||
| 765 | parent_scope: *Scope, | ||
| 766 | rl: ResultLoc, | ||
| 767 | node: ast.Node.Index, | ||
| 768 | ) InnerError!*zir.Inst { | ||
| 447 | const tree = parent_scope.tree(); | 769 | const tree = parent_scope.tree(); |
| 448 | const src = tree.token_locs[node.ltoken].start; | 770 | const node_datas = tree.nodes.items(.data); |
| 771 | const main_tokens = tree.nodes.items(.main_token); | ||
| 772 | const token_starts = tree.tokens.items(.start); | ||
| 773 | |||
| 774 | const src = token_starts[main_tokens[node]]; | ||
| 775 | const break_label = node_datas[node].lhs; | ||
| 449 | 776 | ||
| 450 | // Look for the label in the scope. | 777 | // Look for the label in the scope. |
| 451 | var scope = parent_scope; | 778 | var scope = parent_scope; |
| ... | @@ -457,7 +784,7 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE | ... | @@ -457,7 +784,7 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE |
| 457 | scope = gen_zir.parent; | 784 | scope = gen_zir.parent; |
| 458 | continue; | 785 | continue; |
| 459 | }; | 786 | }; |
| 460 | if (node.getLabel()) |break_label| blk: { | 787 | if (break_label != 0) blk: { |
| 461 | if (gen_zir.label) |*label| { | 788 | if (gen_zir.label) |*label| { |
| 462 | if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) { | 789 | if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) { |
| 463 | label.used = true; | 790 | label.used = true; |
| ... | @@ -469,13 +796,14 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE | ... | @@ -469,13 +796,14 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE |
| 469 | continue; | 796 | continue; |
| 470 | } | 797 | } |
| 471 | 798 | ||
| 472 | return addZirInstTag(mod, parent_scope, src, .break_void, .{ | 799 | const result = try addZirInstTag(mod, parent_scope, src, .break_void, .{ |
| 473 | .block = continue_block, | 800 | .block = continue_block, |
| 474 | }); | 801 | }); |
| 802 | return rvalue(mod, parent_scope, rl, result); | ||
| 475 | }, | 803 | }, |
| 476 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, | 804 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, |
| 477 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, | 805 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, |
| 478 | else => if (node.getLabel()) |break_label| { | 806 | else => if (break_label != 0) { |
| 479 | const label_name = try mod.identifierTokenString(parent_scope, break_label); | 807 | const label_name = try mod.identifierTokenString(parent_scope, break_label); |
| 480 | return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); | 808 | return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); |
| 481 | } else { | 809 | } else { |
| ... | @@ -485,11 +813,27 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE | ... | @@ -485,11 +813,27 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE |
| 485 | } | 813 | } |
| 486 | } | 814 | } |
| 487 | 815 | ||
| 488 | pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void { | 816 | pub fn blockExpr( |
| 817 | mod: *Module, | ||
| 818 | scope: *Scope, | ||
| 819 | rl: ResultLoc, | ||
| 820 | block_node: ast.Node.Index, | ||
| 821 | statements: []const ast.Node.Index, | ||
| 822 | ) InnerError!*zir.Inst { | ||
| 489 | const tracy = trace(@src()); | 823 | const tracy = trace(@src()); |
| 490 | defer tracy.end(); | 824 | defer tracy.end(); |
| 491 | 825 | ||
| 492 | try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements()); | 826 | const tree = scope.tree(); |
| 827 | const main_tokens = tree.nodes.items(.main_token); | ||
| 828 | const token_tags = tree.tokens.items(.tag); | ||
| 829 | |||
| 830 | const lbrace = main_tokens[block_node]; | ||
| 831 | if (token_tags[lbrace - 1] == .colon) { | ||
| 832 | return labeledBlockExpr(mod, scope, rl, block_node, statements, .block); | ||
| 833 | } | ||
| 834 | |||
| 835 | try blockExprStmts(mod, scope, block_node, statements); | ||
| 836 | return rvalueVoid(mod, scope, rl, block_node, {}); | ||
| 493 | } | 837 | } |
| 494 | 838 | ||
| 495 | fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void { | 839 | fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void { |
| ... | @@ -502,8 +846,11 @@ fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIn | ... | @@ -502,8 +846,11 @@ fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIn |
| 502 | if (gen_zir.label) |prev_label| { | 846 | if (gen_zir.label) |prev_label| { |
| 503 | if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) { | 847 | if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) { |
| 504 | const tree = parent_scope.tree(); | 848 | const tree = parent_scope.tree(); |
| 505 | const label_src = tree.token_locs[label].start; | 849 | const main_tokens = tree.nodes.items(.main_token); |
| 506 | const prev_label_src = tree.token_locs[prev_label.token].start; | 850 | const token_starts = tree.tokens.items(.start); |
| 851 | |||
| 852 | const label_src = token_starts[label]; | ||
| 853 | const prev_label_src = token_starts[prev_label.token]; | ||
| 507 | 854 | ||
| 508 | const label_name = try mod.identifierTokenString(parent_scope, label); | 855 | const label_name = try mod.identifierTokenString(parent_scope, label); |
| 509 | const msg = msg: { | 856 | const msg = msg: { |
| ... | @@ -539,7 +886,8 @@ fn labeledBlockExpr( | ... | @@ -539,7 +886,8 @@ fn labeledBlockExpr( |
| 539 | mod: *Module, | 886 | mod: *Module, |
| 540 | parent_scope: *Scope, | 887 | parent_scope: *Scope, |
| 541 | rl: ResultLoc, | 888 | rl: ResultLoc, |
| 542 | block_node: *ast.Node.LabeledBlock, | 889 | block_node: ast.Node.Index, |
| 890 | statements: []const ast.Node.Index, | ||
| 543 | zir_tag: zir.Inst.Tag, | 891 | zir_tag: zir.Inst.Tag, |
| 544 | ) InnerError!*zir.Inst { | 892 | ) InnerError!*zir.Inst { |
| 545 | const tracy = trace(@src()); | 893 | const tracy = trace(@src()); |
| ... | @@ -548,9 +896,16 @@ fn labeledBlockExpr( | ... | @@ -548,9 +896,16 @@ fn labeledBlockExpr( |
| 548 | assert(zir_tag == .block or zir_tag == .block_comptime); | 896 | assert(zir_tag == .block or zir_tag == .block_comptime); |
| 549 | 897 | ||
| 550 | const tree = parent_scope.tree(); | 898 | const tree = parent_scope.tree(); |
| 551 | const src = tree.token_locs[block_node.lbrace].start; | 899 | const main_tokens = tree.nodes.items(.main_token); |
| 900 | const token_starts = tree.tokens.items(.start); | ||
| 901 | const token_tags = tree.tokens.items(.tag); | ||
| 552 | 902 | ||
| 553 | try checkLabelRedefinition(mod, parent_scope, block_node.label); | 903 | const lbrace = main_tokens[block_node]; |
| 904 | const label_token = lbrace - 2; | ||
| 905 | assert(token_tags[label_token] == .identifier); | ||
| 906 | const src = token_starts[lbrace]; | ||
| 907 | |||
| 908 | try checkLabelRedefinition(mod, parent_scope, label_token); | ||
| 554 | 909 | ||
| 555 | // Create the Block ZIR instruction so that we can put it into the GenZIR struct | 910 | // Create the Block ZIR instruction so that we can put it into the GenZIR struct |
| 556 | // so that break statements can reference it. | 911 | // so that break statements can reference it. |
| ... | @@ -575,7 +930,7 @@ fn labeledBlockExpr( | ... | @@ -575,7 +930,7 @@ fn labeledBlockExpr( |
| 575 | .instructions = .{}, | 930 | .instructions = .{}, |
| 576 | // TODO @as here is working around a stage1 miscompilation bug :( | 931 | // TODO @as here is working around a stage1 miscompilation bug :( |
| 577 | .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ | 932 | .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ |
| 578 | .token = block_node.label, | 933 | .token = label_token, |
| 579 | .block_inst = block_inst, | 934 | .block_inst = block_inst, |
| 580 | }), | 935 | }), |
| 581 | }; | 936 | }; |
| ... | @@ -584,10 +939,10 @@ fn labeledBlockExpr( | ... | @@ -584,10 +939,10 @@ fn labeledBlockExpr( |
| 584 | defer block_scope.labeled_breaks.deinit(mod.gpa); | 939 | defer block_scope.labeled_breaks.deinit(mod.gpa); |
| 585 | defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa); | 940 | defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa); |
| 586 | 941 | ||
| 587 | try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements()); | 942 | try blockExprStmts(mod, &block_scope.base, block_node, statements); |
| 588 | 943 | ||
| 589 | if (!block_scope.label.?.used) { | 944 | if (!block_scope.label.?.used) { |
| 590 | return mod.fail(parent_scope, tree.token_locs[block_node.label].start, "unused block label", .{}); | 945 | return mod.failTok(parent_scope, label_token, "unused block label", .{}); |
| 591 | } | 946 | } |
| 592 | 947 | ||
| 593 | try gen_zir.instructions.append(mod.gpa, &block_inst.base); | 948 | try gen_zir.instructions.append(mod.gpa, &block_inst.base); |
| ... | @@ -627,37 +982,41 @@ fn labeledBlockExpr( | ... | @@ -627,37 +982,41 @@ fn labeledBlockExpr( |
| 627 | fn blockExprStmts( | 982 | fn blockExprStmts( |
| 628 | mod: *Module, | 983 | mod: *Module, |
| 629 | parent_scope: *Scope, | 984 | parent_scope: *Scope, |
| 630 | node: *ast.Node, | 985 | node: ast.Node.Index, |
| 631 | statements: []*ast.Node, | 986 | statements: []const ast.Node.Index, |
| 632 | ) !void { | 987 | ) !void { |
| 633 | const tree = parent_scope.tree(); | 988 | const tree = parent_scope.tree(); |
| 989 | const main_tokens = tree.nodes.items(.main_token); | ||
| 990 | const token_starts = tree.tokens.items(.start); | ||
| 991 | const node_tags = tree.nodes.items(.tag); | ||
| 634 | 992 | ||
| 635 | var block_arena = std.heap.ArenaAllocator.init(mod.gpa); | 993 | var block_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| 636 | defer block_arena.deinit(); | 994 | defer block_arena.deinit(); |
| 637 | 995 | ||
| 638 | var scope = parent_scope; | 996 | var scope = parent_scope; |
| 639 | for (statements) |statement| { | 997 | for (statements) |statement| { |
| 640 | const src = tree.token_locs[statement.firstToken()].start; | 998 | const src = token_starts[tree.firstToken(statement)]; |
| 641 | _ = try addZIRNoOp(mod, scope, src, .dbg_stmt); | 999 | _ = try addZIRNoOp(mod, scope, src, .dbg_stmt); |
| 642 | switch (statement.tag) { | 1000 | switch (node_tags[statement]) { |
| 643 | .VarDecl => { | 1001 | .global_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.globalVarDecl(statement)), |
| 644 | const var_decl_node = statement.castTag(.VarDecl).?; | 1002 | .local_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.localVarDecl(statement)), |
| 645 | scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator); | 1003 | .simple_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.simpleVarDecl(statement)), |
| 646 | }, | 1004 | .aligned_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.alignedVarDecl(statement)), |
| 647 | .Assign => try assign(mod, scope, statement.castTag(.Assign).?), | 1005 | |
| 648 | .AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bit_and), | 1006 | .assign => try assign(mod, scope, statement), |
| 649 | .AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bit_or), | 1007 | .assign_bit_and => try assignOp(mod, scope, statement, .bit_and), |
| 650 | .AssignBitShiftLeft => try assignOp(mod, scope, statement.castTag(.AssignBitShiftLeft).?, .shl), | 1008 | .assign_bit_or => try assignOp(mod, scope, statement, .bit_or), |
| 651 | .AssignBitShiftRight => try assignOp(mod, scope, statement.castTag(.AssignBitShiftRight).?, .shr), | 1009 | .assign_bit_shift_left => try assignOp(mod, scope, statement, .shl), |
| 652 | .AssignBitXor => try assignOp(mod, scope, statement.castTag(.AssignBitXor).?, .xor), | 1010 | .assign_bit_shift_right => try assignOp(mod, scope, statement, .shr), |
| 653 | .AssignDiv => try assignOp(mod, scope, statement.castTag(.AssignDiv).?, .div), | 1011 | .assign_bit_xor => try assignOp(mod, scope, statement, .xor), |
| 654 | .AssignSub => try assignOp(mod, scope, statement.castTag(.AssignSub).?, .sub), | 1012 | .assign_div => try assignOp(mod, scope, statement, .div), |
| 655 | .AssignSubWrap => try assignOp(mod, scope, statement.castTag(.AssignSubWrap).?, .subwrap), | 1013 | .assign_sub => try assignOp(mod, scope, statement, .sub), |
| 656 | .AssignMod => try assignOp(mod, scope, statement.castTag(.AssignMod).?, .mod_rem), | 1014 | .assign_sub_wrap => try assignOp(mod, scope, statement, .subwrap), |
| 657 | .AssignAdd => try assignOp(mod, scope, statement.castTag(.AssignAdd).?, .add), | 1015 | .assign_mod => try assignOp(mod, scope, statement, .mod_rem), |
| 658 | .AssignAddWrap => try assignOp(mod, scope, statement.castTag(.AssignAddWrap).?, .addwrap), | 1016 | .assign_add => try assignOp(mod, scope, statement, .add), |
| 659 | .AssignMul => try assignOp(mod, scope, statement.castTag(.AssignMul).?, .mul), | 1017 | .assign_add_wrap => try assignOp(mod, scope, statement, .addwrap), |
| 660 | .AssignMulWrap => try assignOp(mod, scope, statement.castTag(.AssignMulWrap).?, .mulwrap), | 1018 | .assign_mul => try assignOp(mod, scope, statement, .mul), |
| 1019 | .assign_mul_wrap => try assignOp(mod, scope, statement, .mulwrap), | ||
| 661 | 1020 | ||
| 662 | else => { | 1021 | else => { |
| 663 | const possibly_unused_result = try expr(mod, scope, .none, statement); | 1022 | const possibly_unused_result = try expr(mod, scope, .none, statement); |
| ... | @@ -672,18 +1031,23 @@ fn blockExprStmts( | ... | @@ -672,18 +1031,23 @@ fn blockExprStmts( |
| 672 | fn varDecl( | 1031 | fn varDecl( |
| 673 | mod: *Module, | 1032 | mod: *Module, |
| 674 | scope: *Scope, | 1033 | scope: *Scope, |
| 675 | node: *ast.Node.VarDecl, | ||
| 676 | block_arena: *Allocator, | 1034 | block_arena: *Allocator, |
| 1035 | var_decl: ast.full.VarDecl, | ||
| 677 | ) InnerError!*Scope { | 1036 | ) InnerError!*Scope { |
| 678 | if (node.getComptimeToken()) |comptime_token| { | 1037 | if (var_decl.comptime_token) |comptime_token| { |
| 679 | return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{}); | 1038 | return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{}); |
| 680 | } | 1039 | } |
| 681 | if (node.getAlignNode()) |align_node| { | 1040 | if (var_decl.ast.align_node != 0) { |
| 682 | return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{}); | 1041 | return mod.failNode(scope, var_decl.ast.align_node, "TODO implement alignment on locals", .{}); |
| 683 | } | 1042 | } |
| 684 | const tree = scope.tree(); | 1043 | const tree = scope.tree(); |
| 685 | const name_src = tree.token_locs[node.name_token].start; | 1044 | const main_tokens = tree.nodes.items(.main_token); |
| 686 | const ident_name = try mod.identifierTokenString(scope, node.name_token); | 1045 | const token_starts = tree.tokens.items(.start); |
| 1046 | const token_tags = tree.tokens.items(.tag); | ||
| 1047 | |||
| 1048 | const name_token = var_decl.ast.mut_token + 1; | ||
| 1049 | const name_src = token_starts[name_token]; | ||
| 1050 | const ident_name = try mod.identifierTokenString(scope, name_token); | ||
| 687 | 1051 | ||
| 688 | // Local variables shadowing detection, including function parameters. | 1052 | // Local variables shadowing detection, including function parameters. |
| 689 | { | 1053 | { |
| ... | @@ -729,20 +1093,21 @@ fn varDecl( | ... | @@ -729,20 +1093,21 @@ fn varDecl( |
| 729 | // TODO add note for other definition | 1093 | // TODO add note for other definition |
| 730 | return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name}); | 1094 | return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name}); |
| 731 | } | 1095 | } |
| 732 | const init_node = node.getInitNode() orelse | 1096 | if (var_decl.ast.init_node == 0) { |
| 733 | return mod.fail(scope, name_src, "variables must be initialized", .{}); | 1097 | return mod.fail(scope, name_src, "variables must be initialized", .{}); |
| 1098 | } | ||
| 734 | 1099 | ||
| 735 | switch (tree.token_ids[node.mut_token]) { | 1100 | switch (token_tags[var_decl.ast.mut_token]) { |
| 736 | .Keyword_const => { | 1101 | .keyword_const => { |
| 737 | // Depending on the type of AST the initialization expression is, we may need an lvalue | 1102 | // Depending on the type of AST the initialization expression is, we may need an lvalue |
| 738 | // or an rvalue as a result location. If it is an rvalue, we can use the instruction as | 1103 | // or an rvalue as a result location. If it is an rvalue, we can use the instruction as |
| 739 | // the variable, no memory location needed. | 1104 | // the variable, no memory location needed. |
| 740 | if (!nodeMayNeedMemoryLocation(init_node, scope)) { | 1105 | if (!nodeMayNeedMemoryLocation(scope, var_decl.ast.init_node)) { |
| 741 | const result_loc: ResultLoc = if (node.getTypeNode()) |type_node| | 1106 | const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) |
| 742 | .{ .ty = try typeExpr(mod, scope, type_node) } | 1107 | .{ .ty = try typeExpr(mod, scope, var_decl.ast.type_node) } |
| 743 | else | 1108 | else |
| 744 | .none; | 1109 | .none; |
| 745 | const init_inst = try expr(mod, scope, result_loc, init_node); | 1110 | const init_inst = try expr(mod, scope, result_loc, var_decl.ast.init_node); |
| 746 | const sub_scope = try block_arena.create(Scope.LocalVal); | 1111 | const sub_scope = try block_arena.create(Scope.LocalVal); |
| 747 | sub_scope.* = .{ | 1112 | sub_scope.* = .{ |
| 748 | .parent = scope, | 1113 | .parent = scope, |
| ... | @@ -766,8 +1131,8 @@ fn varDecl( | ... | @@ -766,8 +1131,8 @@ fn varDecl( |
| 766 | 1131 | ||
| 767 | var resolve_inferred_alloc: ?*zir.Inst = null; | 1132 | var resolve_inferred_alloc: ?*zir.Inst = null; |
| 768 | var opt_type_inst: ?*zir.Inst = null; | 1133 | var opt_type_inst: ?*zir.Inst = null; |
| 769 | if (node.getTypeNode()) |type_node| { | 1134 | if (var_decl.ast.type_node != 0) { |
| 770 | const type_inst = try typeExpr(mod, &init_scope.base, type_node); | 1135 | const type_inst = try typeExpr(mod, &init_scope.base, var_decl.ast.type_node); |
| 771 | opt_type_inst = type_inst; | 1136 | opt_type_inst = type_inst; |
| 772 | init_scope.rl_ptr = try addZIRUnOp(mod, &init_scope.base, name_src, .alloc, type_inst); | 1137 | init_scope.rl_ptr = try addZIRUnOp(mod, &init_scope.base, name_src, .alloc, type_inst); |
| 773 | } else { | 1138 | } else { |
| ... | @@ -776,7 +1141,7 @@ fn varDecl( | ... | @@ -776,7 +1141,7 @@ fn varDecl( |
| 776 | init_scope.rl_ptr = &alloc.base; | 1141 | init_scope.rl_ptr = &alloc.base; |
| 777 | } | 1142 | } |
| 778 | const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope }; | 1143 | const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope }; |
| 779 | const init_inst = try expr(mod, &init_scope.base, init_result_loc, init_node); | 1144 | const init_inst = try expr(mod, &init_scope.base, init_result_loc, var_decl.ast.init_node); |
| 780 | const parent_zir = &scope.getGenZIR().instructions; | 1145 | const parent_zir = &scope.getGenZIR().instructions; |
| 781 | if (init_scope.rvalue_rl_count == 1) { | 1146 | if (init_scope.rvalue_rl_count == 1) { |
| 782 | // Result location pointer not used. We don't need an alloc for this | 1147 | // Result location pointer not used. We don't need an alloc for this |
| ... | @@ -834,10 +1199,13 @@ fn varDecl( | ... | @@ -834,10 +1199,13 @@ fn varDecl( |
| 834 | }; | 1199 | }; |
| 835 | return &sub_scope.base; | 1200 | return &sub_scope.base; |
| 836 | }, | 1201 | }, |
| 837 | .Keyword_var => { | 1202 | .keyword_var => { |
| 838 | var resolve_inferred_alloc: ?*zir.Inst = null; | 1203 | var resolve_inferred_alloc: ?*zir.Inst = null; |
| 839 | const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTypeNode()) |type_node| a: { | 1204 | const var_data: struct { |
| 840 | const type_inst = try typeExpr(mod, scope, type_node); | 1205 | result_loc: ResultLoc, |
| 1206 | alloc: *zir.Inst, | ||
| 1207 | } = if (var_decl.ast.type_node != 0) a: { | ||
| 1208 | const type_inst = try typeExpr(mod, scope, var_decl.ast.type_node); | ||
| 841 | const alloc = try addZIRUnOp(mod, scope, name_src, .alloc_mut, type_inst); | 1209 | const alloc = try addZIRUnOp(mod, scope, name_src, .alloc_mut, type_inst); |
| 842 | break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } }; | 1210 | break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } }; |
| 843 | } else a: { | 1211 | } else a: { |
| ... | @@ -845,7 +1213,7 @@ fn varDecl( | ... | @@ -845,7 +1213,7 @@ fn varDecl( |
| 845 | resolve_inferred_alloc = &alloc.base; | 1213 | resolve_inferred_alloc = &alloc.base; |
| 846 | break :a .{ .alloc = &alloc.base, .result_loc = .{ .inferred_ptr = alloc } }; | 1214 | break :a .{ .alloc = &alloc.base, .result_loc = .{ .inferred_ptr = alloc } }; |
| 847 | }; | 1215 | }; |
| 848 | const init_inst = try expr(mod, scope, var_data.result_loc, init_node); | 1216 | const init_inst = try expr(mod, scope, var_data.result_loc, var_decl.ast.init_node); |
| 849 | if (resolve_inferred_alloc) |inst| { | 1217 | if (resolve_inferred_alloc) |inst| { |
| 850 | _ = try addZIRUnOp(mod, scope, name_src, .resolve_inferred_alloc, inst); | 1218 | _ = try addZIRUnOp(mod, scope, name_src, .resolve_inferred_alloc, inst); |
| 851 | } | 1219 | } |
| ... | @@ -862,232 +1230,210 @@ fn varDecl( | ... | @@ -862,232 +1230,210 @@ fn varDecl( |
| 862 | } | 1230 | } |
| 863 | } | 1231 | } |
| 864 | 1232 | ||
| 865 | fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!void { | 1233 | fn assign(mod: *Module, scope: *Scope, infix_node: ast.Node.Index) InnerError!void { |
| 866 | if (infix_node.lhs.castTag(.Identifier)) |ident| { | 1234 | const tree = scope.tree(); |
| 867 | // This intentionally does not support @"_" syntax. | 1235 | const node_datas = tree.nodes.items(.data); |
| 868 | const ident_name = scope.tree().tokenSlice(ident.token); | 1236 | const main_tokens = tree.nodes.items(.main_token); |
| 1237 | const node_tags = tree.nodes.items(.tag); | ||
| 1238 | |||
| 1239 | const lhs = node_datas[infix_node].lhs; | ||
| 1240 | const rhs = node_datas[infix_node].rhs; | ||
| 1241 | if (node_tags[lhs] == .identifier) { | ||
| 1242 | // This intentionally does not support `@"_"` syntax. | ||
| 1243 | const ident_name = tree.tokenSlice(main_tokens[lhs]); | ||
| 869 | if (mem.eql(u8, ident_name, "_")) { | 1244 | if (mem.eql(u8, ident_name, "_")) { |
| 870 | _ = try expr(mod, scope, .discard, infix_node.rhs); | 1245 | _ = try expr(mod, scope, .discard, rhs); |
| 871 | return; | 1246 | return; |
| 872 | } | 1247 | } |
| 873 | } | 1248 | } |
| 874 | const lvalue = try lvalExpr(mod, scope, infix_node.lhs); | 1249 | const lvalue = try lvalExpr(mod, scope, lhs); |
| 875 | _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs); | 1250 | _ = try expr(mod, scope, .{ .ptr = lvalue }, rhs); |
| 876 | } | 1251 | } |
| 877 | 1252 | ||
| 878 | fn assignOp( | 1253 | fn assignOp( |
| 879 | mod: *Module, | 1254 | mod: *Module, |
| 880 | scope: *Scope, | 1255 | scope: *Scope, |
| 881 | infix_node: *ast.Node.SimpleInfixOp, | 1256 | infix_node: ast.Node.Index, |
| 882 | op_inst_tag: zir.Inst.Tag, | 1257 | op_inst_tag: zir.Inst.Tag, |
| 883 | ) InnerError!void { | 1258 | ) InnerError!void { |
| 884 | const lhs_ptr = try lvalExpr(mod, scope, infix_node.lhs); | ||
| 885 | const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr); | ||
| 886 | const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs); | ||
| 887 | const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs); | ||
| 888 | |||
| 889 | const tree = scope.tree(); | 1259 | const tree = scope.tree(); |
| 890 | const src = tree.token_locs[infix_node.op_token].start; | 1260 | const node_datas = tree.nodes.items(.data); |
| 1261 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1262 | const token_starts = tree.tokens.items(.start); | ||
| 891 | 1263 | ||
| 1264 | const lhs_ptr = try lvalExpr(mod, scope, node_datas[infix_node].lhs); | ||
| 1265 | const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr); | ||
| 1266 | const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs); | ||
| 1267 | const rhs = try expr(mod, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs); | ||
| 1268 | const src = token_starts[main_tokens[infix_node]]; | ||
| 892 | const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); | 1269 | const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); |
| 893 | _ = try addZIRBinOp(mod, scope, src, .store, lhs_ptr, result); | 1270 | _ = try addZIRBinOp(mod, scope, src, .store, lhs_ptr, result); |
| 894 | } | 1271 | } |
| 895 | 1272 | ||
| 896 | fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { | 1273 | fn boolNot(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst { |
| 897 | const tree = scope.tree(); | 1274 | const tree = scope.tree(); |
| 898 | const src = tree.token_locs[node.op_token].start; | 1275 | const node_datas = tree.nodes.items(.data); |
| 1276 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1277 | const token_starts = tree.tokens.items(.start); | ||
| 1278 | |||
| 1279 | const src = token_starts[main_tokens[node]]; | ||
| 899 | const bool_type = try addZIRInstConst(mod, scope, src, .{ | 1280 | const bool_type = try addZIRInstConst(mod, scope, src, .{ |
| 900 | .ty = Type.initTag(.type), | 1281 | .ty = Type.initTag(.type), |
| 901 | .val = Value.initTag(.bool_type), | 1282 | .val = Value.initTag(.bool_type), |
| 902 | }); | 1283 | }); |
| 903 | const operand = try expr(mod, scope, .{ .ty = bool_type }, node.rhs); | 1284 | const operand = try expr(mod, scope, .{ .ty = bool_type }, node_datas[node].lhs); |
| 904 | return addZIRUnOp(mod, scope, src, .bool_not, operand); | 1285 | return addZIRUnOp(mod, scope, src, .bool_not, operand); |
| 905 | } | 1286 | } |
| 906 | 1287 | ||
| 907 | fn bitNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { | 1288 | fn bitNot(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst { |
| 908 | const tree = scope.tree(); | 1289 | const tree = scope.tree(); |
| 909 | const src = tree.token_locs[node.op_token].start; | 1290 | const node_datas = tree.nodes.items(.data); |
| 910 | const operand = try expr(mod, scope, .none, node.rhs); | 1291 | const main_tokens = tree.nodes.items(.main_token); |
| 1292 | const token_starts = tree.tokens.items(.start); | ||
| 1293 | |||
| 1294 | const src = token_starts[main_tokens[node]]; | ||
| 1295 | const operand = try expr(mod, scope, .none, node_datas[node].lhs); | ||
| 911 | return addZIRUnOp(mod, scope, src, .bit_not, operand); | 1296 | return addZIRUnOp(mod, scope, src, .bit_not, operand); |
| 912 | } | 1297 | } |
| 913 | 1298 | ||
| 914 | fn negation(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst { | 1299 | fn negation( |
| 1300 | mod: *Module, | ||
| 1301 | scope: *Scope, | ||
| 1302 | node: ast.Node.Index, | ||
| 1303 | op_inst_tag: zir.Inst.Tag, | ||
| 1304 | ) InnerError!*zir.Inst { | ||
| 915 | const tree = scope.tree(); | 1305 | const tree = scope.tree(); |
| 916 | const src = tree.token_locs[node.op_token].start; | 1306 | const node_datas = tree.nodes.items(.data); |
| 1307 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1308 | const token_starts = tree.tokens.items(.start); | ||
| 917 | 1309 | ||
| 1310 | const src = token_starts[main_tokens[node]]; | ||
| 918 | const lhs = try addZIRInstConst(mod, scope, src, .{ | 1311 | const lhs = try addZIRInstConst(mod, scope, src, .{ |
| 919 | .ty = Type.initTag(.comptime_int), | 1312 | .ty = Type.initTag(.comptime_int), |
| 920 | .val = Value.initTag(.zero), | 1313 | .val = Value.initTag(.zero), |
| 921 | }); | 1314 | }); |
| 922 | const rhs = try expr(mod, scope, .none, node.rhs); | 1315 | const rhs = try expr(mod, scope, .none, node_datas[node].lhs); |
| 923 | |||
| 924 | return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); | 1316 | return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); |
| 925 | } | 1317 | } |
| 926 | 1318 | ||
| 927 | fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { | 1319 | fn ptrType( |
| 928 | return expr(mod, scope, .ref, node.rhs); | 1320 | mod: *Module, |
| 929 | } | 1321 | scope: *Scope, |
| 930 | 1322 | rl: ResultLoc, | |
| 931 | fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { | 1323 | ptr_info: ast.full.PtrType, |
| 932 | const tree = scope.tree(); | 1324 | ) InnerError!*zir.Inst { |
| 933 | const src = tree.token_locs[node.op_token].start; | ||
| 934 | const operand = try typeExpr(mod, scope, node.rhs); | ||
| 935 | return addZIRUnOp(mod, scope, src, .optional_type, operand); | ||
| 936 | } | ||
| 937 | |||
| 938 | fn sliceType(mod: *Module, scope: *Scope, node: *ast.Node.SliceType) InnerError!*zir.Inst { | ||
| 939 | const tree = scope.tree(); | 1325 | const tree = scope.tree(); |
| 940 | const src = tree.token_locs[node.op_token].start; | 1326 | const token_starts = tree.tokens.items(.start); |
| 941 | return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, .Slice); | ||
| 942 | } | ||
| 943 | 1327 | ||
| 944 | fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir.Inst { | 1328 | const src = token_starts[ptr_info.ast.main_token]; |
| 945 | const tree = scope.tree(); | ||
| 946 | const src = tree.token_locs[node.op_token].start; | ||
| 947 | return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, switch (tree.token_ids[node.op_token]) { | ||
| 948 | .Asterisk, .AsteriskAsterisk => .One, | ||
| 949 | // TODO stage1 type inference bug | ||
| 950 | .LBracket => @as(std.builtin.TypeInfo.Pointer.Size, switch (tree.token_ids[node.op_token + 2]) { | ||
| 951 | .Identifier => .C, | ||
| 952 | else => .Many, | ||
| 953 | }), | ||
| 954 | else => unreachable, | ||
| 955 | }); | ||
| 956 | } | ||
| 957 | 1329 | ||
| 958 | fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, rhs: *ast.Node, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*zir.Inst { | ||
| 959 | const simple = ptr_info.allowzero_token == null and | 1330 | const simple = ptr_info.allowzero_token == null and |
| 960 | ptr_info.align_info == null and | 1331 | ptr_info.ast.align_node == 0 and |
| 961 | ptr_info.volatile_token == null and | 1332 | ptr_info.volatile_token == null and |
| 962 | ptr_info.sentinel == null; | 1333 | ptr_info.ast.sentinel == 0; |
| 963 | 1334 | ||
| 964 | if (simple) { | 1335 | if (simple) { |
| 965 | const child_type = try typeExpr(mod, scope, rhs); | 1336 | const child_type = try typeExpr(mod, scope, ptr_info.ast.child_type); |
| 966 | const mutable = ptr_info.const_token == null; | 1337 | const mutable = ptr_info.const_token == null; |
| 967 | // TODO stage1 type inference bug | ||
| 968 | const T = zir.Inst.Tag; | 1338 | const T = zir.Inst.Tag; |
| 969 | return addZIRUnOp(mod, scope, src, switch (size) { | 1339 | const result = try addZIRUnOp(mod, scope, src, switch (ptr_info.size) { |
| 970 | .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type, | 1340 | .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type, |
| 971 | .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type, | 1341 | .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type, |
| 972 | .C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type, | 1342 | .C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type, |
| 973 | .Slice => if (mutable) T.mut_slice_type else T.const_slice_type, | 1343 | .Slice => if (mutable) T.mut_slice_type else T.const_slice_type, |
| 974 | }, child_type); | 1344 | }, child_type); |
| 1345 | return rvalue(mod, scope, rl, result); | ||
| 975 | } | 1346 | } |
| 976 | 1347 | ||
| 977 | var kw_args: std.meta.fieldInfo(zir.Inst.PtrType, .kw_args).field_type = .{}; | 1348 | var kw_args: std.meta.fieldInfo(zir.Inst.PtrType, .kw_args).field_type = .{}; |
| 978 | kw_args.size = size; | 1349 | kw_args.size = ptr_info.size; |
| 979 | kw_args.@"allowzero" = ptr_info.allowzero_token != null; | 1350 | kw_args.@"allowzero" = ptr_info.allowzero_token != null; |
| 980 | if (ptr_info.align_info) |some| { | 1351 | if (ptr_info.ast.align_node != 0) { |
| 981 | kw_args.@"align" = try expr(mod, scope, .none, some.node); | 1352 | kw_args.@"align" = try expr(mod, scope, .none, ptr_info.ast.align_node); |
| 982 | if (some.bit_range) |bit_range| { | 1353 | if (ptr_info.ast.bit_range_start != 0) { |
| 983 | kw_args.align_bit_start = try expr(mod, scope, .none, bit_range.start); | 1354 | kw_args.align_bit_start = try expr(mod, scope, .none, ptr_info.ast.bit_range_start); |
| 984 | kw_args.align_bit_end = try expr(mod, scope, .none, bit_range.end); | 1355 | kw_args.align_bit_end = try expr(mod, scope, .none, ptr_info.ast.bit_range_end); |
| 985 | } | 1356 | } |
| 986 | } | 1357 | } |
| 987 | kw_args.mutable = ptr_info.const_token == null; | 1358 | kw_args.mutable = ptr_info.const_token == null; |
| 988 | kw_args.@"volatile" = ptr_info.volatile_token != null; | 1359 | kw_args.@"volatile" = ptr_info.volatile_token != null; |
| 989 | if (ptr_info.sentinel) |some| { | 1360 | const child_type = try typeExpr(mod, scope, ptr_info.ast.child_type); |
| 990 | kw_args.sentinel = try expr(mod, scope, .none, some); | 1361 | if (ptr_info.ast.sentinel != 0) { |
| 991 | } | 1362 | kw_args.sentinel = try expr(mod, scope, .{ .ty = child_type }, ptr_info.ast.sentinel); |
| 992 | |||
| 993 | const child_type = try typeExpr(mod, scope, rhs); | ||
| 994 | if (kw_args.sentinel) |some| { | ||
| 995 | kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some); | ||
| 996 | } | 1363 | } |
| 997 | 1364 | const result = try addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args); | |
| 998 | return addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args); | 1365 | return rvalue(mod, scope, rl, result); |
| 999 | } | 1366 | } |
| 1000 | 1367 | ||
| 1001 | fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst { | 1368 | fn arrayType(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !*zir.Inst { |
| 1002 | const tree = scope.tree(); | 1369 | const tree = scope.tree(); |
| 1003 | const src = tree.token_locs[node.op_token].start; | 1370 | const main_tokens = tree.nodes.items(.main_token); |
| 1371 | const node_datas = tree.nodes.items(.data); | ||
| 1372 | const token_starts = tree.tokens.items(.start); | ||
| 1373 | |||
| 1374 | const src = token_starts[main_tokens[node]]; | ||
| 1004 | const usize_type = try addZIRInstConst(mod, scope, src, .{ | 1375 | const usize_type = try addZIRInstConst(mod, scope, src, .{ |
| 1005 | .ty = Type.initTag(.type), | 1376 | .ty = Type.initTag(.type), |
| 1006 | .val = Value.initTag(.usize_type), | 1377 | .val = Value.initTag(.usize_type), |
| 1007 | }); | 1378 | }); |
| 1379 | const len_node = node_datas[node].lhs; | ||
| 1380 | const elem_node = node_datas[node].rhs; | ||
| 1381 | if (len_node == 0) { | ||
| 1382 | const elem_type = try typeExpr(mod, scope, elem_node); | ||
| 1383 | const result = try addZIRUnOp(mod, scope, src, .mut_slice_type, elem_type); | ||
| 1384 | return rvalue(mod, scope, rl, result); | ||
| 1385 | } else { | ||
| 1386 | // TODO check for [_]T | ||
| 1387 | const len = try expr(mod, scope, .{ .ty = usize_type }, len_node); | ||
| 1388 | const elem_type = try typeExpr(mod, scope, elem_node); | ||
| 1008 | 1389 | ||
| 1009 | // TODO check for [_]T | 1390 | const result = try addZIRBinOp(mod, scope, src, .array_type, len, elem_type); |
| 1010 | const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr); | 1391 | return rvalue(mod, scope, rl, result); |
| 1011 | const elem_type = try typeExpr(mod, scope, node.rhs); | 1392 | } |
| 1012 | |||
| 1013 | return addZIRBinOp(mod, scope, src, .array_type, len, elem_type); | ||
| 1014 | } | 1393 | } |
| 1015 | 1394 | ||
| 1016 | fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst { | 1395 | fn arrayTypeSentinel(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !*zir.Inst { |
| 1017 | const tree = scope.tree(); | 1396 | const tree = scope.tree(); |
| 1018 | const src = tree.token_locs[node.op_token].start; | 1397 | const main_tokens = tree.nodes.items(.main_token); |
| 1398 | const token_starts = tree.tokens.items(.start); | ||
| 1399 | const node_datas = tree.nodes.items(.data); | ||
| 1400 | |||
| 1401 | const len_node = node_datas[node].lhs; | ||
| 1402 | const extra = tree.extraData(node_datas[node].rhs, ast.Node.ArrayTypeSentinel); | ||
| 1403 | const src = token_starts[main_tokens[node]]; | ||
| 1019 | const usize_type = try addZIRInstConst(mod, scope, src, .{ | 1404 | const usize_type = try addZIRInstConst(mod, scope, src, .{ |
| 1020 | .ty = Type.initTag(.type), | 1405 | .ty = Type.initTag(.type), |
| 1021 | .val = Value.initTag(.usize_type), | 1406 | .val = Value.initTag(.usize_type), |
| 1022 | }); | 1407 | }); |
| 1023 | 1408 | ||
| 1024 | // TODO check for [_]T | 1409 | // TODO check for [_]T |
| 1025 | const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr); | 1410 | const len = try expr(mod, scope, .{ .ty = usize_type }, len_node); |
| 1026 | const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel); | 1411 | const sentinel_uncasted = try expr(mod, scope, .none, extra.sentinel); |
| 1027 | const elem_type = try typeExpr(mod, scope, node.rhs); | 1412 | const elem_type = try typeExpr(mod, scope, extra.elem_type); |
| 1028 | const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted); | 1413 | const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted); |
| 1029 | 1414 | ||
| 1030 | return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{ | 1415 | const result = try addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{ |
| 1031 | .len = len, | 1416 | .len = len, |
| 1032 | .sentinel = sentinel, | 1417 | .sentinel = sentinel, |
| 1033 | .elem_type = elem_type, | 1418 | .elem_type = elem_type, |
| 1034 | }, .{}); | 1419 | }, .{}); |
| 1035 | } | 1420 | return rvalue(mod, scope, rl, result); |
| 1036 | |||
| 1037 | fn anyFrameType(mod: *Module, scope: *Scope, node: *ast.Node.AnyFrameType) InnerError!*zir.Inst { | ||
| 1038 | const tree = scope.tree(); | ||
| 1039 | const src = tree.token_locs[node.anyframe_token].start; | ||
| 1040 | if (node.result) |some| { | ||
| 1041 | const return_type = try typeExpr(mod, scope, some.return_type); | ||
| 1042 | return addZIRUnOp(mod, scope, src, .anyframe_type, return_type); | ||
| 1043 | } else { | ||
| 1044 | return addZIRInstConst(mod, scope, src, .{ | ||
| 1045 | .ty = Type.initTag(.type), | ||
| 1046 | .val = Value.initTag(.anyframe_type), | ||
| 1047 | }); | ||
| 1048 | } | ||
| 1049 | } | ||
| 1050 | |||
| 1051 | fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst { | ||
| 1052 | const tree = scope.tree(); | ||
| 1053 | const src = tree.token_locs[node.op_token].start; | ||
| 1054 | const error_set = try typeExpr(mod, scope, node.lhs); | ||
| 1055 | const payload = try typeExpr(mod, scope, node.rhs); | ||
| 1056 | return addZIRBinOp(mod, scope, src, op_inst_tag, error_set, payload); | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst { | ||
| 1060 | const tree = scope.tree(); | ||
| 1061 | const src = tree.token_locs[node.name].start; | ||
| 1062 | const name = try mod.identifierTokenString(scope, node.name); | ||
| 1063 | |||
| 1064 | return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{}); | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst { | ||
| 1068 | const tree = scope.tree(); | ||
| 1069 | const src = tree.token_locs[node.rtoken].start; | ||
| 1070 | |||
| 1071 | const operand = try expr(mod, scope, rl, node.lhs); | ||
| 1072 | const op: zir.Inst.Tag = switch (rl) { | ||
| 1073 | .ref => .optional_payload_safe_ptr, | ||
| 1074 | else => .optional_payload_safe, | ||
| 1075 | }; | ||
| 1076 | return addZIRUnOp(mod, scope, src, op, operand); | ||
| 1077 | } | 1421 | } |
| 1078 | 1422 | ||
| 1079 | fn containerField( | 1423 | fn containerField( |
| 1080 | mod: *Module, | 1424 | mod: *Module, |
| 1081 | scope: *Scope, | 1425 | scope: *Scope, |
| 1082 | node: *ast.Node.ContainerField, | 1426 | field: ast.full.ContainerField, |
| 1083 | ) InnerError!*zir.Inst { | 1427 | ) InnerError!*zir.Inst { |
| 1084 | const tree = scope.tree(); | 1428 | const tree = scope.tree(); |
| 1085 | const src = tree.token_locs[node.firstToken()].start; | 1429 | const token_starts = tree.tokens.items(.start); |
| 1086 | const name = try mod.identifierTokenString(scope, node.name_token); | 1430 | |
| 1431 | const src = token_starts[field.ast.name_token]; | ||
| 1432 | const name = try mod.identifierTokenString(scope, field.ast.name_token); | ||
| 1087 | 1433 | ||
| 1088 | if (node.comptime_token == null and node.value_expr == null and node.align_expr == null) { | 1434 | if (field.comptime_token == null and field.ast.value_expr == 0 and field.ast.align_expr == 0) { |
| 1089 | if (node.type_expr) |some| { | 1435 | if (field.ast.type_expr != 0) { |
| 1090 | const ty = try typeExpr(mod, scope, some); | 1436 | const ty = try typeExpr(mod, scope, field.ast.type_expr); |
| 1091 | return addZIRInst(mod, scope, src, zir.Inst.ContainerFieldTyped, .{ | 1437 | return addZIRInst(mod, scope, src, zir.Inst.ContainerFieldTyped, .{ |
| 1092 | .bytes = name, | 1438 | .bytes = name, |
| 1093 | .ty = ty, | 1439 | .ty = ty, |
| ... | @@ -1099,9 +1445,11 @@ fn containerField( | ... | @@ -1099,9 +1445,11 @@ fn containerField( |
| 1099 | } | 1445 | } |
| 1100 | } | 1446 | } |
| 1101 | 1447 | ||
| 1102 | const ty = if (node.type_expr) |some| try typeExpr(mod, scope, some) else null; | 1448 | const ty = if (field.ast.type_expr != 0) try typeExpr(mod, scope, field.ast.type_expr) else null; |
| 1103 | const alignment = if (node.align_expr) |some| try expr(mod, scope, .none, some) else null; | 1449 | // TODO result location should be alignment type |
| 1104 | const init = if (node.value_expr) |some| try expr(mod, scope, .none, some) else null; | 1450 | const alignment = if (field.ast.align_expr != 0) try expr(mod, scope, .none, field.ast.align_expr) else null; |
| 1451 | // TODO result location should be the field type | ||
| 1452 | const init = if (field.ast.value_expr != 0) try expr(mod, scope, .none, field.ast.value_expr) else null; | ||
| 1105 | 1453 | ||
| 1106 | return addZIRInst(mod, scope, src, zir.Inst.ContainerField, .{ | 1454 | return addZIRInst(mod, scope, src, zir.Inst.ContainerField, .{ |
| 1107 | .bytes = name, | 1455 | .bytes = name, |
| ... | @@ -1109,13 +1457,22 @@ fn containerField( | ... | @@ -1109,13 +1457,22 @@ fn containerField( |
| 1109 | .ty = ty, | 1457 | .ty = ty, |
| 1110 | .init = init, | 1458 | .init = init, |
| 1111 | .alignment = alignment, | 1459 | .alignment = alignment, |
| 1112 | .is_comptime = node.comptime_token != null, | 1460 | .is_comptime = field.comptime_token != null, |
| 1113 | }); | 1461 | }); |
| 1114 | } | 1462 | } |
| 1115 | 1463 | ||
| 1116 | fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ContainerDecl) InnerError!*zir.Inst { | 1464 | fn containerDecl( |
| 1465 | mod: *Module, | ||
| 1466 | scope: *Scope, | ||
| 1467 | rl: ResultLoc, | ||
| 1468 | container_decl: ast.full.ContainerDecl, | ||
| 1469 | ) InnerError!*zir.Inst { | ||
| 1117 | const tree = scope.tree(); | 1470 | const tree = scope.tree(); |
| 1118 | const src = tree.token_locs[node.kind_token].start; | 1471 | const token_starts = tree.tokens.items(.start); |
| 1472 | const node_tags = tree.nodes.items(.tag); | ||
| 1473 | const token_tags = tree.tokens.items(.tag); | ||
| 1474 | |||
| 1475 | const src = token_starts[container_decl.ast.main_token]; | ||
| 1119 | 1476 | ||
| 1120 | var gen_scope: Scope.GenZIR = .{ | 1477 | var gen_scope: Scope.GenZIR = .{ |
| 1121 | .parent = scope, | 1478 | .parent = scope, |
| ... | @@ -1129,10 +1486,16 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con | ... | @@ -1129,10 +1486,16 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con |
| 1129 | var fields = std.ArrayList(*zir.Inst).init(mod.gpa); | 1486 | var fields = std.ArrayList(*zir.Inst).init(mod.gpa); |
| 1130 | defer fields.deinit(); | 1487 | defer fields.deinit(); |
| 1131 | 1488 | ||
| 1132 | for (node.fieldsAndDecls()) |fd| { | 1489 | for (container_decl.ast.members) |member| { |
| 1133 | if (fd.castTag(.ContainerField)) |f| { | 1490 | // TODO just handle these cases differently since they end up with different ZIR |
| 1134 | try fields.append(try containerField(mod, &gen_scope.base, f)); | 1491 | // instructions anyway. It will be simpler & have fewer branches. |
| 1135 | } | 1492 | const field = switch (node_tags[member]) { |
| 1493 | .container_field_init => try containerField(mod, &gen_scope.base, tree.containerFieldInit(member)), | ||
| 1494 | .container_field_align => try containerField(mod, &gen_scope.base, tree.containerFieldAlign(member)), | ||
| 1495 | .container_field => try containerField(mod, &gen_scope.base, tree.containerField(member)), | ||
| 1496 | else => continue, | ||
| 1497 | }; | ||
| 1498 | try fields.append(field); | ||
| 1136 | } | 1499 | } |
| 1137 | 1500 | ||
| 1138 | var decl_arena = std.heap.ArenaAllocator.init(mod.gpa); | 1501 | var decl_arena = std.heap.ArenaAllocator.init(mod.gpa); |
| ... | @@ -1140,19 +1503,22 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con | ... | @@ -1140,19 +1503,22 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con |
| 1140 | const arena = &decl_arena.allocator; | 1503 | const arena = &decl_arena.allocator; |
| 1141 | 1504 | ||
| 1142 | var layout: std.builtin.TypeInfo.ContainerLayout = .Auto; | 1505 | var layout: std.builtin.TypeInfo.ContainerLayout = .Auto; |
| 1143 | if (node.layout_token) |some| switch (tree.token_ids[some]) { | 1506 | if (container_decl.layout_token) |some| switch (token_tags[some]) { |
| 1144 | .Keyword_extern => layout = .Extern, | 1507 | .keyword_extern => layout = .Extern, |
| 1145 | .Keyword_packed => layout = .Packed, | 1508 | .keyword_packed => layout = .Packed, |
| 1146 | else => unreachable, | 1509 | else => unreachable, |
| 1147 | }; | 1510 | }; |
| 1148 | 1511 | ||
| 1149 | const container_type = switch (tree.token_ids[node.kind_token]) { | 1512 | // TODO this implementation is incorrect. The types must be created in semantic |
| 1150 | .Keyword_enum => blk: { | 1513 | // analysis, not astgen, because the same ZIR is re-used for multiple inline function calls, |
| 1151 | const tag_type: ?*zir.Inst = switch (node.init_arg_expr) { | 1514 | // comptime function calls, and generic function instantiations, and these |
| 1152 | .Type => |t| try typeExpr(mod, &gen_scope.base, t), | 1515 | // must result in different instances of container types. |
| 1153 | .None => null, | 1516 | const container_type = switch (token_tags[container_decl.ast.main_token]) { |
| 1154 | .Enum => unreachable, | 1517 | .keyword_enum => blk: { |
| 1155 | }; | 1518 | const tag_type: ?*zir.Inst = if (container_decl.ast.arg != 0) |
| 1519 | try typeExpr(mod, &gen_scope.base, container_decl.ast.arg) | ||
| 1520 | else | ||
| 1521 | null; | ||
| 1156 | const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.EnumType, .{ | 1522 | const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.EnumType, .{ |
| 1157 | .fields = try arena.dupe(*zir.Inst, fields.items), | 1523 | .fields = try arena.dupe(*zir.Inst, fields.items), |
| 1158 | }, .{ | 1524 | }, .{ |
| ... | @@ -1174,8 +1540,8 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con | ... | @@ -1174,8 +1540,8 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con |
| 1174 | }; | 1540 | }; |
| 1175 | break :blk Type.initPayload(&enum_type.base); | 1541 | break :blk Type.initPayload(&enum_type.base); |
| 1176 | }, | 1542 | }, |
| 1177 | .Keyword_struct => blk: { | 1543 | .keyword_struct => blk: { |
| 1178 | assert(node.init_arg_expr == .None); | 1544 | assert(container_decl.ast.arg == 0); |
| 1179 | const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.StructType, .{ | 1545 | const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.StructType, .{ |
| 1180 | .fields = try arena.dupe(*zir.Inst, fields.items), | 1546 | .fields = try arena.dupe(*zir.Inst, fields.items), |
| 1181 | }, .{ | 1547 | }, .{ |
| ... | @@ -1196,22 +1562,17 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con | ... | @@ -1196,22 +1562,17 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con |
| 1196 | }; | 1562 | }; |
| 1197 | break :blk Type.initPayload(&struct_type.base); | 1563 | break :blk Type.initPayload(&struct_type.base); |
| 1198 | }, | 1564 | }, |
| 1199 | .Keyword_union => blk: { | 1565 | .keyword_union => blk: { |
| 1200 | const init_inst = switch (node.init_arg_expr) { | 1566 | const init_inst: ?*zir.Inst = if (container_decl.ast.arg != 0) |
| 1201 | .Enum => |e| if (e) |t| try typeExpr(mod, &gen_scope.base, t) else null, | 1567 | try typeExpr(mod, &gen_scope.base, container_decl.ast.arg) |
| 1202 | .None => null, | 1568 | else |
| 1203 | .Type => |t| try typeExpr(mod, &gen_scope.base, t), | 1569 | null; |
| 1204 | }; | 1570 | const has_enum_token = container_decl.ast.enum_token != null; |
| 1205 | const init_kind: zir.Inst.UnionType.InitKind = switch (node.init_arg_expr) { | ||
| 1206 | .Enum => .enum_type, | ||
| 1207 | .None => .none, | ||
| 1208 | .Type => .tag_type, | ||
| 1209 | }; | ||
| 1210 | const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.UnionType, .{ | 1571 | const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.UnionType, .{ |
| 1211 | .fields = try arena.dupe(*zir.Inst, fields.items), | 1572 | .fields = try arena.dupe(*zir.Inst, fields.items), |
| 1212 | }, .{ | 1573 | }, .{ |
| 1213 | .layout = layout, | 1574 | .layout = layout, |
| 1214 | .init_kind = init_kind, | 1575 | .has_enum_token = has_enum_token, |
| 1215 | .init_inst = init_inst, | 1576 | .init_inst = init_inst, |
| 1216 | }); | 1577 | }); |
| 1217 | const union_type = try arena.create(Type.Payload.Union); | 1578 | const union_type = try arena.create(Type.Payload.Union); |
| ... | @@ -1229,7 +1590,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con | ... | @@ -1229,7 +1590,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con |
| 1229 | }; | 1590 | }; |
| 1230 | break :blk Type.initPayload(&union_type.base); | 1591 | break :blk Type.initPayload(&union_type.base); |
| 1231 | }, | 1592 | }, |
| 1232 | .Keyword_opaque => blk: { | 1593 | .keyword_opaque => blk: { |
| 1233 | if (fields.items.len > 0) { | 1594 | if (fields.items.len > 0) { |
| 1234 | return mod.fail(scope, fields.items[0].src, "opaque types cannot have fields", .{}); | 1595 | return mod.fail(scope, fields.items[0].src, "opaque types cannot have fields", .{}); |
| 1235 | } | 1596 | } |
| ... | @@ -1245,7 +1606,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con | ... | @@ -1245,7 +1606,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con |
| 1245 | else => unreachable, | 1606 | else => unreachable, |
| 1246 | }; | 1607 | }; |
| 1247 | const val = try Value.Tag.ty.create(arena, container_type); | 1608 | const val = try Value.Tag.ty.create(arena, container_type); |
| 1248 | const decl = try mod.createContainerDecl(scope, node.kind_token, &decl_arena, .{ | 1609 | const decl = try mod.createContainerDecl(scope, container_decl.ast.main_token, &decl_arena, .{ |
| 1249 | .ty = Type.initTag(.type), | 1610 | .ty = Type.initTag(.type), |
| 1250 | .val = val, | 1611 | .val = val, |
| 1251 | }); | 1612 | }); |
| ... | @@ -1258,101 +1619,69 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con | ... | @@ -1258,101 +1619,69 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con |
| 1258 | } | 1619 | } |
| 1259 | } | 1620 | } |
| 1260 | 1621 | ||
| 1261 | fn errorSetDecl(mod: *Module, scope: *Scope, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst { | 1622 | fn errorSetDecl( |
| 1262 | const tree = scope.tree(); | 1623 | mod: *Module, |
| 1263 | const src = tree.token_locs[node.error_token].start; | 1624 | scope: *Scope, |
| 1264 | const decls = node.decls(); | 1625 | rl: ResultLoc, |
| 1265 | const fields = try scope.arena().alloc([]const u8, decls.len); | 1626 | node: ast.Node.Index, |
| 1266 | 1627 | ) InnerError!*zir.Inst { | |
| 1267 | for (decls) |decl, i| { | ||
| 1268 | const tag = decl.castTag(.ErrorTag).?; | ||
| 1269 | fields[i] = try mod.identifierTokenString(scope, tag.name_token); | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | return addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{}); | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { | ||
| 1276 | const tree = scope.tree(); | 1628 | const tree = scope.tree(); |
| 1277 | const src = tree.token_locs[node.token].start; | 1629 | const main_tokens = tree.nodes.items(.main_token); |
| 1278 | return addZIRInstConst(mod, scope, src, .{ | 1630 | const token_tags = tree.tokens.items(.tag); |
| 1279 | .ty = Type.initTag(.type), | 1631 | const token_starts = tree.tokens.items(.start); |
| 1280 | .val = Value.initTag(.anyerror_type), | 1632 | |
| 1281 | }); | 1633 | // Count how many fields there are. |
| 1282 | } | 1634 | const error_token = main_tokens[node]; |
| 1635 | const count: usize = count: { | ||
| 1636 | var tok_i = error_token + 2; | ||
| 1637 | var count: usize = 0; | ||
| 1638 | while (true) : (tok_i += 1) { | ||
| 1639 | switch (token_tags[tok_i]) { | ||
| 1640 | .doc_comment, .comma => {}, | ||
| 1641 | .identifier => count += 1, | ||
| 1642 | .r_paren => break :count count, | ||
| 1643 | else => unreachable, | ||
| 1644 | } | ||
| 1645 | } else unreachable; // TODO should not need else unreachable here | ||
| 1646 | }; | ||
| 1283 | 1647 | ||
| 1284 | fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch) InnerError!*zir.Inst { | 1648 | const fields = try scope.arena().alloc([]const u8, count); |
| 1285 | switch (rl) { | 1649 | { |
| 1286 | .ref => return orelseCatchExpr( | 1650 | var tok_i = error_token + 2; |
| 1287 | mod, | 1651 | var field_i: usize = 0; |
| 1288 | scope, | 1652 | while (true) : (tok_i += 1) { |
| 1289 | rl, | 1653 | switch (token_tags[tok_i]) { |
| 1290 | node.lhs, | 1654 | .doc_comment, .comma => {}, |
| 1291 | node.op_token, | 1655 | .identifier => { |
| 1292 | .is_err_ptr, | 1656 | fields[field_i] = try mod.identifierTokenString(scope, tok_i); |
| 1293 | .err_union_payload_unsafe_ptr, | 1657 | field_i += 1; |
| 1294 | .err_union_code_ptr, | 1658 | }, |
| 1295 | node.rhs, | 1659 | .r_paren => break, |
| 1296 | node.payload, | 1660 | else => unreachable, |
| 1297 | ), | 1661 | } |
| 1298 | else => return orelseCatchExpr( | 1662 | } |
| 1299 | mod, | ||
| 1300 | scope, | ||
| 1301 | rl, | ||
| 1302 | node.lhs, | ||
| 1303 | node.op_token, | ||
| 1304 | .is_err, | ||
| 1305 | .err_union_payload_unsafe, | ||
| 1306 | .err_union_code, | ||
| 1307 | node.rhs, | ||
| 1308 | node.payload, | ||
| 1309 | ), | ||
| 1310 | } | ||
| 1311 | } | ||
| 1312 | |||
| 1313 | fn orelseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst { | ||
| 1314 | switch (rl) { | ||
| 1315 | .ref => return orelseCatchExpr( | ||
| 1316 | mod, | ||
| 1317 | scope, | ||
| 1318 | rl, | ||
| 1319 | node.lhs, | ||
| 1320 | node.op_token, | ||
| 1321 | .is_null_ptr, | ||
| 1322 | .optional_payload_unsafe_ptr, | ||
| 1323 | undefined, | ||
| 1324 | node.rhs, | ||
| 1325 | null, | ||
| 1326 | ), | ||
| 1327 | else => return orelseCatchExpr( | ||
| 1328 | mod, | ||
| 1329 | scope, | ||
| 1330 | rl, | ||
| 1331 | node.lhs, | ||
| 1332 | node.op_token, | ||
| 1333 | .is_null, | ||
| 1334 | .optional_payload_unsafe, | ||
| 1335 | undefined, | ||
| 1336 | node.rhs, | ||
| 1337 | null, | ||
| 1338 | ), | ||
| 1339 | } | 1663 | } |
| 1664 | const src = token_starts[error_token]; | ||
| 1665 | const result = try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{}); | ||
| 1666 | return rvalue(mod, scope, rl, result); | ||
| 1340 | } | 1667 | } |
| 1341 | 1668 | ||
| 1342 | fn orelseCatchExpr( | 1669 | fn orelseCatchExpr( |
| 1343 | mod: *Module, | 1670 | mod: *Module, |
| 1344 | scope: *Scope, | 1671 | scope: *Scope, |
| 1345 | rl: ResultLoc, | 1672 | rl: ResultLoc, |
| 1346 | lhs: *ast.Node, | 1673 | lhs: ast.Node.Index, |
| 1347 | op_token: ast.TokenIndex, | 1674 | op_token: ast.TokenIndex, |
| 1348 | cond_op: zir.Inst.Tag, | 1675 | cond_op: zir.Inst.Tag, |
| 1349 | unwrap_op: zir.Inst.Tag, | 1676 | unwrap_op: zir.Inst.Tag, |
| 1350 | unwrap_code_op: zir.Inst.Tag, | 1677 | unwrap_code_op: zir.Inst.Tag, |
| 1351 | rhs: *ast.Node, | 1678 | rhs: ast.Node.Index, |
| 1352 | payload_node: ?*ast.Node, | 1679 | payload_token: ?ast.TokenIndex, |
| 1353 | ) InnerError!*zir.Inst { | 1680 | ) InnerError!*zir.Inst { |
| 1354 | const tree = scope.tree(); | 1681 | const tree = scope.tree(); |
| 1355 | const src = tree.token_locs[op_token].start; | 1682 | const token_starts = tree.tokens.items(.start); |
| 1683 | |||
| 1684 | const src = token_starts[op_token]; | ||
| 1356 | 1685 | ||
| 1357 | var block_scope: Scope.GenZIR = .{ | 1686 | var block_scope: Scope.GenZIR = .{ |
| 1358 | .parent = scope, | 1687 | .parent = scope, |
| ... | @@ -1390,12 +1719,11 @@ fn orelseCatchExpr( | ... | @@ -1390,12 +1719,11 @@ fn orelseCatchExpr( |
| 1390 | 1719 | ||
| 1391 | var err_val_scope: Scope.LocalVal = undefined; | 1720 | var err_val_scope: Scope.LocalVal = undefined; |
| 1392 | const then_sub_scope = blk: { | 1721 | const then_sub_scope = blk: { |
| 1393 | const payload = payload_node orelse break :blk &then_scope.base; | 1722 | const payload = payload_token orelse break :blk &then_scope.base; |
| 1394 | 1723 | if (mem.eql(u8, tree.tokenSlice(payload), "_")) { | |
| 1395 | const err_name = tree.tokenSlice(payload.castTag(.Payload).?.error_symbol.firstToken()); | 1724 | return mod.failTok(&then_scope.base, payload, "discard of error capture; omit it instead", .{}); |
| 1396 | if (mem.eql(u8, err_name, "_")) | 1725 | } |
| 1397 | break :blk &then_scope.base; | 1726 | const err_name = try mod.identifierTokenString(scope, payload); |
| 1398 | |||
| 1399 | err_val_scope = .{ | 1727 | err_val_scope = .{ |
| 1400 | .parent = &then_scope.base, | 1728 | .parent = &then_scope.base, |
| 1401 | .gen_zir = &then_scope, | 1729 | .gen_zir = &then_scope, |
| ... | @@ -1524,124 +1852,121 @@ fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: as | ... | @@ -1524,124 +1852,121 @@ fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: as |
| 1524 | return mem.eql(u8, ident_name_1, ident_name_2); | 1852 | return mem.eql(u8, ident_name_1, ident_name_2); |
| 1525 | } | 1853 | } |
| 1526 | 1854 | ||
| 1527 | pub fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst { | 1855 | pub fn fieldAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst { |
| 1528 | const tree = scope.tree(); | 1856 | const tree = scope.tree(); |
| 1529 | const src = tree.token_locs[node.op_token].start; | 1857 | const token_starts = tree.tokens.items(.start); |
| 1530 | // TODO custom AST node for field access so that we don't have to go through a node cast here | 1858 | const main_tokens = tree.nodes.items(.main_token); |
| 1531 | const field_name = try mod.identifierTokenString(scope, node.rhs.castTag(.Identifier).?.token); | 1859 | const node_datas = tree.nodes.items(.data); |
| 1860 | |||
| 1861 | const dot_token = main_tokens[node]; | ||
| 1862 | const src = token_starts[dot_token]; | ||
| 1863 | const field_ident = dot_token + 1; | ||
| 1864 | const field_name = try mod.identifierTokenString(scope, field_ident); | ||
| 1532 | if (rl == .ref) { | 1865 | if (rl == .ref) { |
| 1533 | return addZirInstTag(mod, scope, src, .field_ptr, .{ | 1866 | return addZirInstTag(mod, scope, src, .field_ptr, .{ |
| 1534 | .object = try expr(mod, scope, .ref, node.lhs), | 1867 | .object = try expr(mod, scope, .ref, node_datas[node].lhs), |
| 1535 | .field_name = field_name, | 1868 | .field_name = field_name, |
| 1536 | }); | 1869 | }); |
| 1870 | } else { | ||
| 1871 | return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val, .{ | ||
| 1872 | .object = try expr(mod, scope, .none, node_datas[node].lhs), | ||
| 1873 | .field_name = field_name, | ||
| 1874 | })); | ||
| 1537 | } | 1875 | } |
| 1538 | return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val, .{ | ||
| 1539 | .object = try expr(mod, scope, .none, node.lhs), | ||
| 1540 | .field_name = field_name, | ||
| 1541 | })); | ||
| 1542 | } | 1876 | } |
| 1543 | 1877 | ||
| 1544 | fn namedField( | 1878 | fn arrayAccess( |
| 1545 | mod: *Module, | 1879 | mod: *Module, |
| 1546 | scope: *Scope, | 1880 | scope: *Scope, |
| 1547 | rl: ResultLoc, | 1881 | rl: ResultLoc, |
| 1548 | call: *ast.Node.BuiltinCall, | 1882 | node: ast.Node.Index, |
| 1549 | ) InnerError!*zir.Inst { | 1883 | ) InnerError!*zir.Inst { |
| 1550 | try ensureBuiltinParamCount(mod, scope, call, 2); | ||
| 1551 | |||
| 1552 | const tree = scope.tree(); | 1884 | const tree = scope.tree(); |
| 1553 | const src = tree.token_locs[call.builtin_token].start; | 1885 | const main_tokens = tree.nodes.items(.main_token); |
| 1554 | const params = call.params(); | 1886 | const token_starts = tree.tokens.items(.start); |
| 1555 | 1887 | const node_datas = tree.nodes.items(.data); | |
| 1556 | const string_type = try addZIRInstConst(mod, scope, src, .{ | ||
| 1557 | .ty = Type.initTag(.type), | ||
| 1558 | .val = Value.initTag(.const_slice_u8_type), | ||
| 1559 | }); | ||
| 1560 | const string_rl: ResultLoc = .{ .ty = string_type }; | ||
| 1561 | |||
| 1562 | if (rl == .ref) { | ||
| 1563 | return addZirInstTag(mod, scope, src, .field_ptr_named, .{ | ||
| 1564 | .object = try expr(mod, scope, .ref, params[0]), | ||
| 1565 | .field_name = try comptimeExpr(mod, scope, string_rl, params[1]), | ||
| 1566 | }); | ||
| 1567 | } | ||
| 1568 | return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val_named, .{ | ||
| 1569 | .object = try expr(mod, scope, .none, params[0]), | ||
| 1570 | .field_name = try comptimeExpr(mod, scope, string_rl, params[1]), | ||
| 1571 | })); | ||
| 1572 | } | ||
| 1573 | 1888 | ||
| 1574 | fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ArrayAccess) InnerError!*zir.Inst { | 1889 | const src = token_starts[main_tokens[node]]; |
| 1575 | const tree = scope.tree(); | ||
| 1576 | const src = tree.token_locs[node.rtoken].start; | ||
| 1577 | const usize_type = try addZIRInstConst(mod, scope, src, .{ | 1890 | const usize_type = try addZIRInstConst(mod, scope, src, .{ |
| 1578 | .ty = Type.initTag(.type), | 1891 | .ty = Type.initTag(.type), |
| 1579 | .val = Value.initTag(.usize_type), | 1892 | .val = Value.initTag(.usize_type), |
| 1580 | }); | 1893 | }); |
| 1581 | const index_rl: ResultLoc = .{ .ty = usize_type }; | 1894 | const index_rl: ResultLoc = .{ .ty = usize_type }; |
| 1582 | 1895 | switch (rl) { | |
| 1583 | if (rl == .ref) { | 1896 | .ref => return addZirInstTag(mod, scope, src, .elem_ptr, .{ |
| 1584 | return addZirInstTag(mod, scope, src, .elem_ptr, .{ | 1897 | .array = try expr(mod, scope, .ref, node_datas[node].lhs), |
| 1585 | .array = try expr(mod, scope, .ref, node.lhs), | 1898 | .index = try expr(mod, scope, index_rl, node_datas[node].rhs), |
| 1586 | .index = try expr(mod, scope, index_rl, node.index_expr), | 1899 | }), |
| 1587 | }); | 1900 | else => return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .elem_val, .{ |
| 1901 | .array = try expr(mod, scope, .none, node_datas[node].lhs), | ||
| 1902 | .index = try expr(mod, scope, index_rl, node_datas[node].rhs), | ||
| 1903 | })), | ||
| 1588 | } | 1904 | } |
| 1589 | return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .elem_val, .{ | ||
| 1590 | .array = try expr(mod, scope, .none, node.lhs), | ||
| 1591 | .index = try expr(mod, scope, index_rl, node.index_expr), | ||
| 1592 | })); | ||
| 1593 | } | 1905 | } |
| 1594 | 1906 | ||
| 1595 | fn sliceExpr(mod: *Module, scope: *Scope, node: *ast.Node.Slice) InnerError!*zir.Inst { | 1907 | fn sliceExpr( |
| 1908 | mod: *Module, | ||
| 1909 | scope: *Scope, | ||
| 1910 | rl: ResultLoc, | ||
| 1911 | slice: ast.full.Slice, | ||
| 1912 | ) InnerError!*zir.Inst { | ||
| 1596 | const tree = scope.tree(); | 1913 | const tree = scope.tree(); |
| 1597 | const src = tree.token_locs[node.rtoken].start; | 1914 | const token_starts = tree.tokens.items(.start); |
| 1915 | |||
| 1916 | const src = token_starts[slice.ast.lbracket]; | ||
| 1598 | 1917 | ||
| 1599 | const usize_type = try addZIRInstConst(mod, scope, src, .{ | 1918 | const usize_type = try addZIRInstConst(mod, scope, src, .{ |
| 1600 | .ty = Type.initTag(.type), | 1919 | .ty = Type.initTag(.type), |
| 1601 | .val = Value.initTag(.usize_type), | 1920 | .val = Value.initTag(.usize_type), |
| 1602 | }); | 1921 | }); |
| 1603 | 1922 | ||
| 1604 | const array_ptr = try expr(mod, scope, .ref, node.lhs); | 1923 | const array_ptr = try expr(mod, scope, .ref, slice.ast.sliced); |
| 1605 | const start = try expr(mod, scope, .{ .ty = usize_type }, node.start); | 1924 | const start = try expr(mod, scope, .{ .ty = usize_type }, slice.ast.start); |
| 1606 | 1925 | ||
| 1607 | if (node.end == null and node.sentinel == null) { | 1926 | if (slice.ast.sentinel == 0) { |
| 1608 | return try addZIRBinOp(mod, scope, src, .slice_start, array_ptr, start); | 1927 | if (slice.ast.end == 0) { |
| 1928 | const result = try addZIRBinOp(mod, scope, src, .slice_start, array_ptr, start); | ||
| 1929 | return rvalue(mod, scope, rl, result); | ||
| 1930 | } else { | ||
| 1931 | const end = try expr(mod, scope, .{ .ty = usize_type }, slice.ast.end); | ||
| 1932 | // TODO a ZIR slice_open instruction | ||
| 1933 | const result = try addZIRInst(mod, scope, src, zir.Inst.Slice, .{ | ||
| 1934 | .array_ptr = array_ptr, | ||
| 1935 | .start = start, | ||
| 1936 | }, .{ .end = end }); | ||
| 1937 | return rvalue(mod, scope, rl, result); | ||
| 1938 | } | ||
| 1609 | } | 1939 | } |
| 1610 | 1940 | ||
| 1611 | const end = if (node.end) |end| try expr(mod, scope, .{ .ty = usize_type }, end) else null; | 1941 | const end = try expr(mod, scope, .{ .ty = usize_type }, slice.ast.end); |
| 1612 | // we could get the child type here, but it is easier to just do it in semantic analysis. | 1942 | // TODO pass the proper result loc to this expression using a ZIR instruction |
| 1613 | const sentinel = if (node.sentinel) |sentinel| try expr(mod, scope, .none, sentinel) else null; | 1943 | // "get the child element type for a slice target". |
| 1614 | 1944 | const sentinel = try expr(mod, scope, .none, slice.ast.sentinel); | |
| 1615 | return try addZIRInst( | 1945 | const result = try addZIRInst(mod, scope, src, zir.Inst.Slice, .{ |
| 1616 | mod, | 1946 | .array_ptr = array_ptr, |
| 1617 | scope, | 1947 | .start = start, |
| 1618 | src, | 1948 | }, .{ |
| 1619 | zir.Inst.Slice, | 1949 | .end = end, |
| 1620 | .{ .array_ptr = array_ptr, .start = start }, | 1950 | .sentinel = sentinel, |
| 1621 | .{ .end = end, .sentinel = sentinel }, | 1951 | }); |
| 1622 | ); | 1952 | return rvalue(mod, scope, rl, result); |
| 1623 | } | ||
| 1624 | |||
| 1625 | fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst { | ||
| 1626 | const tree = scope.tree(); | ||
| 1627 | const src = tree.token_locs[node.rtoken].start; | ||
| 1628 | const lhs = try expr(mod, scope, .none, node.lhs); | ||
| 1629 | return addZIRUnOp(mod, scope, src, .deref, lhs); | ||
| 1630 | } | 1953 | } |
| 1631 | 1954 | ||
| 1632 | fn simpleBinOp( | 1955 | fn simpleBinOp( |
| 1633 | mod: *Module, | 1956 | mod: *Module, |
| 1634 | scope: *Scope, | 1957 | scope: *Scope, |
| 1635 | rl: ResultLoc, | 1958 | rl: ResultLoc, |
| 1636 | infix_node: *ast.Node.SimpleInfixOp, | 1959 | infix_node: ast.Node.Index, |
| 1637 | op_inst_tag: zir.Inst.Tag, | 1960 | op_inst_tag: zir.Inst.Tag, |
| 1638 | ) InnerError!*zir.Inst { | 1961 | ) InnerError!*zir.Inst { |
| 1639 | const tree = scope.tree(); | 1962 | const tree = scope.tree(); |
| 1640 | const src = tree.token_locs[infix_node.op_token].start; | 1963 | const node_datas = tree.nodes.items(.data); |
| 1641 | 1964 | const main_tokens = tree.nodes.items(.main_token); | |
| 1642 | const lhs = try expr(mod, scope, .none, infix_node.lhs); | 1965 | const token_starts = tree.tokens.items(.start); |
| 1643 | const rhs = try expr(mod, scope, .none, infix_node.rhs); | ||
| 1644 | 1966 | ||
| 1967 | const lhs = try expr(mod, scope, .none, node_datas[infix_node].lhs); | ||
| 1968 | const rhs = try expr(mod, scope, .none, node_datas[infix_node].rhs); | ||
| 1969 | const src = token_starts[main_tokens[infix_node]]; | ||
| 1645 | const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); | 1970 | const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); |
| 1646 | return rvalue(mod, scope, rl, result); | 1971 | return rvalue(mod, scope, rl, result); |
| 1647 | } | 1972 | } |
| ... | @@ -1650,10 +1975,15 @@ fn boolBinOp( | ... | @@ -1650,10 +1975,15 @@ fn boolBinOp( |
| 1650 | mod: *Module, | 1975 | mod: *Module, |
| 1651 | scope: *Scope, | 1976 | scope: *Scope, |
| 1652 | rl: ResultLoc, | 1977 | rl: ResultLoc, |
| 1653 | infix_node: *ast.Node.SimpleInfixOp, | 1978 | infix_node: ast.Node.Index, |
| 1979 | is_bool_and: bool, | ||
| 1654 | ) InnerError!*zir.Inst { | 1980 | ) InnerError!*zir.Inst { |
| 1655 | const tree = scope.tree(); | 1981 | const tree = scope.tree(); |
| 1656 | const src = tree.token_locs[infix_node.op_token].start; | 1982 | const node_datas = tree.nodes.items(.data); |
| 1983 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1984 | const token_starts = tree.tokens.items(.start); | ||
| 1985 | |||
| 1986 | const src = token_starts[main_tokens[infix_node]]; | ||
| 1657 | const bool_type = try addZIRInstConst(mod, scope, src, .{ | 1987 | const bool_type = try addZIRInstConst(mod, scope, src, .{ |
| 1658 | .ty = Type.initTag(.type), | 1988 | .ty = Type.initTag(.type), |
| 1659 | .val = Value.initTag(.bool_type), | 1989 | .val = Value.initTag(.bool_type), |
| ... | @@ -1668,7 +1998,7 @@ fn boolBinOp( | ... | @@ -1668,7 +1998,7 @@ fn boolBinOp( |
| 1668 | }; | 1998 | }; |
| 1669 | defer block_scope.instructions.deinit(mod.gpa); | 1999 | defer block_scope.instructions.deinit(mod.gpa); |
| 1670 | 2000 | ||
| 1671 | const lhs = try expr(mod, scope, .{ .ty = bool_type }, infix_node.lhs); | 2001 | const lhs = try expr(mod, scope, .{ .ty = bool_type }, node_datas[infix_node].lhs); |
| 1672 | const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{ | 2002 | const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{ |
| 1673 | .condition = lhs, | 2003 | .condition = lhs, |
| 1674 | .then_body = undefined, // populated below | 2004 | .then_body = undefined, // populated below |
| ... | @@ -1688,7 +2018,7 @@ fn boolBinOp( | ... | @@ -1688,7 +2018,7 @@ fn boolBinOp( |
| 1688 | }; | 2018 | }; |
| 1689 | defer rhs_scope.instructions.deinit(mod.gpa); | 2019 | defer rhs_scope.instructions.deinit(mod.gpa); |
| 1690 | 2020 | ||
| 1691 | const rhs = try expr(mod, &rhs_scope.base, .{ .ty = bool_type }, infix_node.rhs); | 2021 | const rhs = try expr(mod, &rhs_scope.base, .{ .ty = bool_type }, node_datas[infix_node].rhs); |
| 1692 | _ = try addZIRInst(mod, &rhs_scope.base, src, zir.Inst.Break, .{ | 2022 | _ = try addZIRInst(mod, &rhs_scope.base, src, zir.Inst.Break, .{ |
| 1693 | .block = block, | 2023 | .block = block, |
| 1694 | .operand = rhs, | 2024 | .operand = rhs, |
| ... | @@ -1703,7 +2033,6 @@ fn boolBinOp( | ... | @@ -1703,7 +2033,6 @@ fn boolBinOp( |
| 1703 | }; | 2033 | }; |
| 1704 | defer const_scope.instructions.deinit(mod.gpa); | 2034 | defer const_scope.instructions.deinit(mod.gpa); |
| 1705 | 2035 | ||
| 1706 | const is_bool_and = infix_node.base.tag == .BoolAnd; | ||
| 1707 | _ = try addZIRInst(mod, &const_scope.base, src, zir.Inst.Break, .{ | 2036 | _ = try addZIRInst(mod, &const_scope.base, src, zir.Inst.Break, .{ |
| 1708 | .block = block, | 2037 | .block = block, |
| 1709 | .operand = try addZIRInstConst(mod, &const_scope.base, src, .{ | 2038 | .operand = try addZIRInstConst(mod, &const_scope.base, src, .{ |
| ... | @@ -1731,96 +2060,42 @@ fn boolBinOp( | ... | @@ -1731,96 +2060,42 @@ fn boolBinOp( |
| 1731 | return rvalue(mod, scope, rl, &block.base); | 2060 | return rvalue(mod, scope, rl, &block.base); |
| 1732 | } | 2061 | } |
| 1733 | 2062 | ||
| 1734 | const CondKind = union(enum) { | 2063 | fn ifExpr( |
| 1735 | bool, | 2064 | mod: *Module, |
| 1736 | optional: ?*zir.Inst, | 2065 | scope: *Scope, |
| 1737 | err_union: ?*zir.Inst, | 2066 | rl: ResultLoc, |
| 2067 | if_full: ast.full.If, | ||
| 2068 | ) InnerError!*zir.Inst { | ||
| 2069 | var block_scope: Scope.GenZIR = .{ | ||
| 2070 | .parent = scope, | ||
| 2071 | .decl = scope.ownerDecl().?, | ||
| 2072 | .arena = scope.arena(), | ||
| 2073 | .force_comptime = scope.isComptime(), | ||
| 2074 | .instructions = .{}, | ||
| 2075 | }; | ||
| 2076 | setBlockResultLoc(&block_scope, rl); | ||
| 2077 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 1738 | 2078 | ||
| 1739 | fn cond(self: *CondKind, mod: *Module, block_scope: *Scope.GenZIR, src: usize, cond_node: *ast.Node) !*zir.Inst { | 2079 | const tree = scope.tree(); |
| 1740 | switch (self.*) { | 2080 | const main_tokens = tree.nodes.items(.main_token); |
| 1741 | .bool => { | 2081 | const token_starts = tree.tokens.items(.start); |
| 1742 | const bool_type = try addZIRInstConst(mod, &block_scope.base, src, .{ | ||
| 1743 | .ty = Type.initTag(.type), | ||
| 1744 | .val = Value.initTag(.bool_type), | ||
| 1745 | }); | ||
| 1746 | return try expr(mod, &block_scope.base, .{ .ty = bool_type }, cond_node); | ||
| 1747 | }, | ||
| 1748 | .optional => { | ||
| 1749 | const cond_ptr = try expr(mod, &block_scope.base, .ref, cond_node); | ||
| 1750 | self.* = .{ .optional = cond_ptr }; | ||
| 1751 | const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, cond_ptr); | ||
| 1752 | return try addZIRUnOp(mod, &block_scope.base, src, .is_non_null, result); | ||
| 1753 | }, | ||
| 1754 | .err_union => { | ||
| 1755 | const err_ptr = try expr(mod, &block_scope.base, .ref, cond_node); | ||
| 1756 | self.* = .{ .err_union = err_ptr }; | ||
| 1757 | const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, err_ptr); | ||
| 1758 | return try addZIRUnOp(mod, &block_scope.base, src, .is_err, result); | ||
| 1759 | }, | ||
| 1760 | } | ||
| 1761 | } | ||
| 1762 | |||
| 1763 | fn thenSubScope(self: CondKind, mod: *Module, then_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope { | ||
| 1764 | if (self == .bool) return &then_scope.base; | ||
| 1765 | |||
| 1766 | const payload = payload_node.?.castTag(.PointerPayload) orelse { | ||
| 1767 | // condition is error union and payload is not explicitly ignored | ||
| 1768 | _ = try addZIRUnOp(mod, &then_scope.base, src, .ensure_err_payload_void, self.err_union.?); | ||
| 1769 | return &then_scope.base; | ||
| 1770 | }; | ||
| 1771 | const is_ptr = payload.ptr_token != null; | ||
| 1772 | const ident_node = payload.value_symbol.castTag(.Identifier).?; | ||
| 1773 | |||
| 1774 | // This intentionally does not support @"_" syntax. | ||
| 1775 | const ident_name = then_scope.base.tree().tokenSlice(ident_node.token); | ||
| 1776 | if (mem.eql(u8, ident_name, "_")) { | ||
| 1777 | if (is_ptr) | ||
| 1778 | return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{}); | ||
| 1779 | return &then_scope.base; | ||
| 1780 | } | ||
| 1781 | |||
| 1782 | return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement payload symbols", .{}); | ||
| 1783 | } | ||
| 1784 | |||
| 1785 | fn elseSubScope(self: CondKind, mod: *Module, else_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope { | ||
| 1786 | if (self != .err_union) return &else_scope.base; | ||
| 1787 | |||
| 1788 | const payload_ptr = try addZIRUnOp(mod, &else_scope.base, src, .err_union_payload_unsafe_ptr, self.err_union.?); | ||
| 1789 | |||
| 1790 | const payload = payload_node.?.castTag(.Payload).?; | ||
| 1791 | const ident_node = payload.error_symbol.castTag(.Identifier).?; | ||
| 1792 | |||
| 1793 | // This intentionally does not support @"_" syntax. | ||
| 1794 | const ident_name = else_scope.base.tree().tokenSlice(ident_node.token); | ||
| 1795 | if (mem.eql(u8, ident_name, "_")) { | ||
| 1796 | return &else_scope.base; | ||
| 1797 | } | ||
| 1798 | 2082 | ||
| 1799 | return mod.failNode(&else_scope.base, payload.error_symbol, "TODO implement payload symbols", .{}); | 2083 | const if_src = token_starts[if_full.ast.if_token]; |
| 1800 | } | ||
| 1801 | }; | ||
| 1802 | 2084 | ||
| 1803 | fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst { | 2085 | const cond = c: { |
| 1804 | var cond_kind: CondKind = .bool; | 2086 | // TODO https://github.com/ziglang/zig/issues/7929 |
| 1805 | if (if_node.payload) |_| cond_kind = .{ .optional = null }; | 2087 | if (if_full.error_token) |error_token| { |
| 1806 | if (if_node.@"else") |else_node| { | 2088 | return mod.failTok(scope, error_token, "TODO implement if error union", .{}); |
| 1807 | if (else_node.payload) |payload| { | 2089 | } else if (if_full.payload_token) |payload_token| { |
| 1808 | cond_kind = .{ .err_union = null }; | 2090 | return mod.failTok(scope, payload_token, "TODO implement if optional", .{}); |
| 2091 | } else { | ||
| 2092 | const bool_type = try addZIRInstConst(mod, &block_scope.base, if_src, .{ | ||
| 2093 | .ty = Type.initTag(.type), | ||
| 2094 | .val = Value.initTag(.bool_type), | ||
| 2095 | }); | ||
| 2096 | break :c try expr(mod, &block_scope.base, .{ .ty = bool_type }, if_full.ast.cond_expr); | ||
| 1809 | } | 2097 | } |
| 1810 | } | ||
| 1811 | var block_scope: Scope.GenZIR = .{ | ||
| 1812 | .parent = scope, | ||
| 1813 | .decl = scope.ownerDecl().?, | ||
| 1814 | .arena = scope.arena(), | ||
| 1815 | .force_comptime = scope.isComptime(), | ||
| 1816 | .instructions = .{}, | ||
| 1817 | }; | 2098 | }; |
| 1818 | setBlockResultLoc(&block_scope, rl); | ||
| 1819 | defer block_scope.instructions.deinit(mod.gpa); | ||
| 1820 | |||
| 1821 | const tree = scope.tree(); | ||
| 1822 | const if_src = tree.token_locs[if_node.if_token].start; | ||
| 1823 | const cond = try cond_kind.cond(mod, &block_scope, if_src, if_node.condition); | ||
| 1824 | 2099 | ||
| 1825 | const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{ | 2100 | const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{ |
| 1826 | .condition = cond, | 2101 | .condition = cond, |
| ... | @@ -1832,7 +2107,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn | ... | @@ -1832,7 +2107,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 1832 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), | 2107 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), |
| 1833 | }); | 2108 | }); |
| 1834 | 2109 | ||
| 1835 | const then_src = tree.token_locs[if_node.body.lastToken()].start; | 2110 | const then_src = token_starts[tree.lastToken(if_full.ast.then_expr)]; |
| 1836 | var then_scope: Scope.GenZIR = .{ | 2111 | var then_scope: Scope.GenZIR = .{ |
| 1837 | .parent = scope, | 2112 | .parent = scope, |
| 1838 | .decl = block_scope.decl, | 2113 | .decl = block_scope.decl, |
| ... | @@ -1843,10 +2118,10 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn | ... | @@ -1843,10 +2118,10 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 1843 | defer then_scope.instructions.deinit(mod.gpa); | 2118 | defer then_scope.instructions.deinit(mod.gpa); |
| 1844 | 2119 | ||
| 1845 | // declare payload to the then_scope | 2120 | // declare payload to the then_scope |
| 1846 | const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, if_node.payload); | 2121 | const then_sub_scope = &then_scope.base; |
| 1847 | 2122 | ||
| 1848 | block_scope.break_count += 1; | 2123 | block_scope.break_count += 1; |
| 1849 | const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, if_node.body); | 2124 | const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, if_full.ast.then_expr); |
| 1850 | // We hold off on the break instructions as well as copying the then/else | 2125 | // We hold off on the break instructions as well as copying the then/else |
| 1851 | // instructions into place until we know whether to keep store_to_block_ptr | 2126 | // instructions into place until we know whether to keep store_to_block_ptr |
| 1852 | // instructions or not. | 2127 | // instructions or not. |
| ... | @@ -1860,20 +2135,19 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn | ... | @@ -1860,20 +2135,19 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 1860 | }; | 2135 | }; |
| 1861 | defer else_scope.instructions.deinit(mod.gpa); | 2136 | defer else_scope.instructions.deinit(mod.gpa); |
| 1862 | 2137 | ||
| 1863 | var else_src: usize = undefined; | 2138 | const else_node = if_full.ast.else_expr; |
| 1864 | var else_sub_scope: *Module.Scope = undefined; | 2139 | const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: { |
| 1865 | const else_result: ?*zir.Inst = if (if_node.@"else") |else_node| blk: { | ||
| 1866 | else_src = tree.token_locs[else_node.body.lastToken()].start; | ||
| 1867 | // declare payload to the then_scope | ||
| 1868 | else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload); | ||
| 1869 | |||
| 1870 | block_scope.break_count += 1; | 2140 | block_scope.break_count += 1; |
| 1871 | break :blk try expr(mod, else_sub_scope, block_scope.break_result_loc, else_node.body); | 2141 | const sub_scope = &else_scope.base; |
| 1872 | } else blk: { | 2142 | break :blk .{ |
| 1873 | else_src = tree.token_locs[if_node.lastToken()].start; | 2143 | .src = token_starts[tree.lastToken(else_node)], |
| 1874 | else_sub_scope = &else_scope.base; | 2144 | .result = try expr(mod, sub_scope, block_scope.break_result_loc, else_node), |
| 1875 | break :blk null; | 2145 | }; |
| 1876 | }; | 2146 | } else |
| 2147 | .{ | ||
| 2148 | .src = token_starts[tree.lastToken(if_full.ast.then_expr)], | ||
| 2149 | .result = null, | ||
| 2150 | }; | ||
| 1877 | 2151 | ||
| 1878 | return finishThenElseBlock( | 2152 | return finishThenElseBlock( |
| 1879 | mod, | 2153 | mod, |
| ... | @@ -1885,9 +2159,9 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn | ... | @@ -1885,9 +2159,9 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 1885 | &condbr.positionals.then_body, | 2159 | &condbr.positionals.then_body, |
| 1886 | &condbr.positionals.else_body, | 2160 | &condbr.positionals.else_body, |
| 1887 | then_src, | 2161 | then_src, |
| 1888 | else_src, | 2162 | else_info.src, |
| 1889 | then_result, | 2163 | then_result, |
| 1890 | else_result, | 2164 | else_info.result, |
| 1891 | block, | 2165 | block, |
| 1892 | block, | 2166 | block, |
| 1893 | ); | 2167 | ); |
| ... | @@ -1918,23 +2192,15 @@ fn whileExpr( | ... | @@ -1918,23 +2192,15 @@ fn whileExpr( |
| 1918 | mod: *Module, | 2192 | mod: *Module, |
| 1919 | scope: *Scope, | 2193 | scope: *Scope, |
| 1920 | rl: ResultLoc, | 2194 | rl: ResultLoc, |
| 1921 | while_node: *ast.Node.While, | 2195 | while_full: ast.full.While, |
| 1922 | ) InnerError!*zir.Inst { | 2196 | ) InnerError!*zir.Inst { |
| 1923 | var cond_kind: CondKind = .bool; | 2197 | if (while_full.label_token) |label_token| { |
| 1924 | if (while_node.payload) |_| cond_kind = .{ .optional = null }; | 2198 | try checkLabelRedefinition(mod, scope, label_token); |
| 1925 | if (while_node.@"else") |else_node| { | ||
| 1926 | if (else_node.payload) |payload| { | ||
| 1927 | cond_kind = .{ .err_union = null }; | ||
| 1928 | } | ||
| 1929 | } | 2199 | } |
| 1930 | 2200 | if (while_full.inline_token) |inline_token| { | |
| 1931 | if (while_node.label) |label| { | 2201 | return mod.failTok(scope, inline_token, "TODO inline while", .{}); |
| 1932 | try checkLabelRedefinition(mod, scope, label); | ||
| 1933 | } | 2202 | } |
| 1934 | 2203 | ||
| 1935 | if (while_node.inline_token) |tok| | ||
| 1936 | return mod.failTok(scope, tok, "TODO inline while", .{}); | ||
| 1937 | |||
| 1938 | var loop_scope: Scope.GenZIR = .{ | 2204 | var loop_scope: Scope.GenZIR = .{ |
| 1939 | .parent = scope, | 2205 | .parent = scope, |
| 1940 | .decl = scope.ownerDecl().?, | 2206 | .decl = scope.ownerDecl().?, |
| ... | @@ -1955,12 +2221,28 @@ fn whileExpr( | ... | @@ -1955,12 +2221,28 @@ fn whileExpr( |
| 1955 | defer continue_scope.instructions.deinit(mod.gpa); | 2221 | defer continue_scope.instructions.deinit(mod.gpa); |
| 1956 | 2222 | ||
| 1957 | const tree = scope.tree(); | 2223 | const tree = scope.tree(); |
| 1958 | const while_src = tree.token_locs[while_node.while_token].start; | 2224 | const main_tokens = tree.nodes.items(.main_token); |
| 2225 | const token_starts = tree.tokens.items(.start); | ||
| 2226 | |||
| 2227 | const while_src = token_starts[while_full.ast.while_token]; | ||
| 1959 | const void_type = try addZIRInstConst(mod, scope, while_src, .{ | 2228 | const void_type = try addZIRInstConst(mod, scope, while_src, .{ |
| 1960 | .ty = Type.initTag(.type), | 2229 | .ty = Type.initTag(.type), |
| 1961 | .val = Value.initTag(.void_type), | 2230 | .val = Value.initTag(.void_type), |
| 1962 | }); | 2231 | }); |
| 1963 | const cond = try cond_kind.cond(mod, &continue_scope, while_src, while_node.condition); | 2232 | const cond = c: { |
| 2233 | // TODO https://github.com/ziglang/zig/issues/7929 | ||
| 2234 | if (while_full.error_token) |error_token| { | ||
| 2235 | return mod.failTok(scope, error_token, "TODO implement while error union", .{}); | ||
| 2236 | } else if (while_full.payload_token) |payload_token| { | ||
| 2237 | return mod.failTok(scope, payload_token, "TODO implement while optional", .{}); | ||
| 2238 | } else { | ||
| 2239 | const bool_type = try addZIRInstConst(mod, &continue_scope.base, while_src, .{ | ||
| 2240 | .ty = Type.initTag(.type), | ||
| 2241 | .val = Value.initTag(.bool_type), | ||
| 2242 | }); | ||
| 2243 | break :c try expr(mod, &continue_scope.base, .{ .ty = bool_type }, while_full.ast.cond_expr); | ||
| 2244 | } | ||
| 2245 | }; | ||
| 1964 | 2246 | ||
| 1965 | const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{ | 2247 | const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{ |
| 1966 | .condition = cond, | 2248 | .condition = cond, |
| ... | @@ -1974,8 +2256,8 @@ fn whileExpr( | ... | @@ -1974,8 +2256,8 @@ fn whileExpr( |
| 1974 | // are no jumps to it. This happens when the last statement of a while body is noreturn | 2256 | // are no jumps to it. This happens when the last statement of a while body is noreturn |
| 1975 | // and there are no `continue` statements. | 2257 | // and there are no `continue` statements. |
| 1976 | // The "repeat" at the end of a loop body is implied. | 2258 | // The "repeat" at the end of a loop body is implied. |
| 1977 | if (while_node.continue_expr) |cont_expr| { | 2259 | if (while_full.ast.cont_expr != 0) { |
| 1978 | _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, cont_expr); | 2260 | _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, while_full.ast.cont_expr); |
| 1979 | } | 2261 | } |
| 1980 | const loop = try scope.arena().create(zir.Inst.Loop); | 2262 | const loop = try scope.arena().create(zir.Inst.Loop); |
| 1981 | loop.* = .{ | 2263 | loop.* = .{ |
| ... | @@ -1995,14 +2277,14 @@ fn whileExpr( | ... | @@ -1995,14 +2277,14 @@ fn whileExpr( |
| 1995 | }); | 2277 | }); |
| 1996 | loop_scope.break_block = while_block; | 2278 | loop_scope.break_block = while_block; |
| 1997 | loop_scope.continue_block = cond_block; | 2279 | loop_scope.continue_block = cond_block; |
| 1998 | if (while_node.label) |some| { | 2280 | if (while_full.label_token) |label_token| { |
| 1999 | loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ | 2281 | loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ |
| 2000 | .token = some, | 2282 | .token = label_token, |
| 2001 | .block_inst = while_block, | 2283 | .block_inst = while_block, |
| 2002 | }); | 2284 | }); |
| 2003 | } | 2285 | } |
| 2004 | 2286 | ||
| 2005 | const then_src = tree.token_locs[while_node.body.lastToken()].start; | 2287 | const then_src = token_starts[tree.lastToken(while_full.ast.then_expr)]; |
| 2006 | var then_scope: Scope.GenZIR = .{ | 2288 | var then_scope: Scope.GenZIR = .{ |
| 2007 | .parent = &continue_scope.base, | 2289 | .parent = &continue_scope.base, |
| 2008 | .decl = continue_scope.decl, | 2290 | .decl = continue_scope.decl, |
| ... | @@ -2012,11 +2294,10 @@ fn whileExpr( | ... | @@ -2012,11 +2294,10 @@ fn whileExpr( |
| 2012 | }; | 2294 | }; |
| 2013 | defer then_scope.instructions.deinit(mod.gpa); | 2295 | defer then_scope.instructions.deinit(mod.gpa); |
| 2014 | 2296 | ||
| 2015 | // declare payload to the then_scope | 2297 | const then_sub_scope = &then_scope.base; |
| 2016 | const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, while_node.payload); | ||
| 2017 | 2298 | ||
| 2018 | loop_scope.break_count += 1; | 2299 | loop_scope.break_count += 1; |
| 2019 | const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, while_node.body); | 2300 | const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr); |
| 2020 | 2301 | ||
| 2021 | var else_scope: Scope.GenZIR = .{ | 2302 | var else_scope: Scope.GenZIR = .{ |
| 2022 | .parent = &continue_scope.base, | 2303 | .parent = &continue_scope.base, |
| ... | @@ -2027,21 +2308,23 @@ fn whileExpr( | ... | @@ -2027,21 +2308,23 @@ fn whileExpr( |
| 2027 | }; | 2308 | }; |
| 2028 | defer else_scope.instructions.deinit(mod.gpa); | 2309 | defer else_scope.instructions.deinit(mod.gpa); |
| 2029 | 2310 | ||
| 2030 | var else_src: usize = undefined; | 2311 | const else_node = while_full.ast.else_expr; |
| 2031 | const else_result: ?*zir.Inst = if (while_node.@"else") |else_node| blk: { | 2312 | const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: { |
| 2032 | else_src = tree.token_locs[else_node.body.lastToken()].start; | ||
| 2033 | // declare payload to the then_scope | ||
| 2034 | const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload); | ||
| 2035 | |||
| 2036 | loop_scope.break_count += 1; | 2313 | loop_scope.break_count += 1; |
| 2037 | break :blk try expr(mod, else_sub_scope, loop_scope.break_result_loc, else_node.body); | 2314 | const sub_scope = &else_scope.base; |
| 2038 | } else blk: { | 2315 | break :blk .{ |
| 2039 | else_src = tree.token_locs[while_node.lastToken()].start; | 2316 | .src = token_starts[tree.lastToken(else_node)], |
| 2040 | break :blk null; | 2317 | .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node), |
| 2041 | }; | 2318 | }; |
| 2319 | } else | ||
| 2320 | .{ | ||
| 2321 | .src = token_starts[tree.lastToken(while_full.ast.then_expr)], | ||
| 2322 | .result = null, | ||
| 2323 | }; | ||
| 2324 | |||
| 2042 | if (loop_scope.label) |some| { | 2325 | if (loop_scope.label) |some| { |
| 2043 | if (!some.used) { | 2326 | if (!some.used) { |
| 2044 | return mod.fail(scope, tree.token_locs[some.token].start, "unused while label", .{}); | 2327 | return mod.fail(scope, token_starts[some.token], "unused while loop label", .{}); |
| 2045 | } | 2328 | } |
| 2046 | } | 2329 | } |
| 2047 | return finishThenElseBlock( | 2330 | return finishThenElseBlock( |
| ... | @@ -2054,9 +2337,9 @@ fn whileExpr( | ... | @@ -2054,9 +2337,9 @@ fn whileExpr( |
| 2054 | &condbr.positionals.then_body, | 2337 | &condbr.positionals.then_body, |
| 2055 | &condbr.positionals.else_body, | 2338 | &condbr.positionals.else_body, |
| 2056 | then_src, | 2339 | then_src, |
| 2057 | else_src, | 2340 | else_info.src, |
| 2058 | then_result, | 2341 | then_result, |
| 2059 | else_result, | 2342 | else_info.result, |
| 2060 | while_block, | 2343 | while_block, |
| 2061 | cond_block, | 2344 | cond_block, |
| 2062 | ); | 2345 | ); |
| ... | @@ -2066,18 +2349,23 @@ fn forExpr( | ... | @@ -2066,18 +2349,23 @@ fn forExpr( |
| 2066 | mod: *Module, | 2349 | mod: *Module, |
| 2067 | scope: *Scope, | 2350 | scope: *Scope, |
| 2068 | rl: ResultLoc, | 2351 | rl: ResultLoc, |
| 2069 | for_node: *ast.Node.For, | 2352 | for_full: ast.full.While, |
| 2070 | ) InnerError!*zir.Inst { | 2353 | ) InnerError!*zir.Inst { |
| 2071 | if (for_node.label) |label| { | 2354 | if (for_full.label_token) |label_token| { |
| 2072 | try checkLabelRedefinition(mod, scope, label); | 2355 | try checkLabelRedefinition(mod, scope, label_token); |
| 2073 | } | 2356 | } |
| 2074 | 2357 | ||
| 2075 | if (for_node.inline_token) |tok| | 2358 | if (for_full.inline_token) |inline_token| { |
| 2076 | return mod.failTok(scope, tok, "TODO inline for", .{}); | 2359 | return mod.failTok(scope, inline_token, "TODO inline for", .{}); |
| 2360 | } | ||
| 2077 | 2361 | ||
| 2078 | // setup variables and constants | 2362 | // Set up variables and constants. |
| 2079 | const tree = scope.tree(); | 2363 | const tree = scope.tree(); |
| 2080 | const for_src = tree.token_locs[for_node.for_token].start; | 2364 | const main_tokens = tree.nodes.items(.main_token); |
| 2365 | const token_starts = tree.tokens.items(.start); | ||
| 2366 | const token_tags = tree.tokens.items(.tag); | ||
| 2367 | |||
| 2368 | const for_src = token_starts[for_full.ast.while_token]; | ||
| 2081 | const index_ptr = blk: { | 2369 | const index_ptr = blk: { |
| 2082 | const usize_type = try addZIRInstConst(mod, scope, for_src, .{ | 2370 | const usize_type = try addZIRInstConst(mod, scope, for_src, .{ |
| 2083 | .ty = Type.initTag(.type), | 2371 | .ty = Type.initTag(.type), |
| ... | @@ -2092,8 +2380,8 @@ fn forExpr( | ... | @@ -2092,8 +2380,8 @@ fn forExpr( |
| 2092 | _ = try addZIRBinOp(mod, scope, for_src, .store, index_ptr, zero); | 2380 | _ = try addZIRBinOp(mod, scope, for_src, .store, index_ptr, zero); |
| 2093 | break :blk index_ptr; | 2381 | break :blk index_ptr; |
| 2094 | }; | 2382 | }; |
| 2095 | const array_ptr = try expr(mod, scope, .ref, for_node.array_expr); | 2383 | const array_ptr = try expr(mod, scope, .ref, for_full.ast.cond_expr); |
| 2096 | const cond_src = tree.token_locs[for_node.array_expr.firstToken()].start; | 2384 | const cond_src = token_starts[tree.firstToken(for_full.ast.cond_expr)]; |
| 2097 | const len = try addZIRUnOp(mod, scope, cond_src, .indexable_ptr_len, array_ptr); | 2385 | const len = try addZIRUnOp(mod, scope, cond_src, .indexable_ptr_len, array_ptr); |
| 2098 | 2386 | ||
| 2099 | var loop_scope: Scope.GenZIR = .{ | 2387 | var loop_scope: Scope.GenZIR = .{ |
| ... | @@ -2155,15 +2443,15 @@ fn forExpr( | ... | @@ -2155,15 +2443,15 @@ fn forExpr( |
| 2155 | }); | 2443 | }); |
| 2156 | loop_scope.break_block = for_block; | 2444 | loop_scope.break_block = for_block; |
| 2157 | loop_scope.continue_block = cond_block; | 2445 | loop_scope.continue_block = cond_block; |
| 2158 | if (for_node.label) |some| { | 2446 | if (for_full.label_token) |label_token| { |
| 2159 | loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ | 2447 | loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ |
| 2160 | .token = some, | 2448 | .token = label_token, |
| 2161 | .block_inst = for_block, | 2449 | .block_inst = for_block, |
| 2162 | }); | 2450 | }); |
| 2163 | } | 2451 | } |
| 2164 | 2452 | ||
| 2165 | // while body | 2453 | // while body |
| 2166 | const then_src = tree.token_locs[for_node.body.lastToken()].start; | 2454 | const then_src = token_starts[tree.lastToken(for_full.ast.then_expr)]; |
| 2167 | var then_scope: Scope.GenZIR = .{ | 2455 | var then_scope: Scope.GenZIR = .{ |
| 2168 | .parent = &cond_scope.base, | 2456 | .parent = &cond_scope.base, |
| 2169 | .decl = cond_scope.decl, | 2457 | .decl = cond_scope.decl, |
| ... | @@ -2175,23 +2463,27 @@ fn forExpr( | ... | @@ -2175,23 +2463,27 @@ fn forExpr( |
| 2175 | 2463 | ||
| 2176 | var index_scope: Scope.LocalPtr = undefined; | 2464 | var index_scope: Scope.LocalPtr = undefined; |
| 2177 | const then_sub_scope = blk: { | 2465 | const then_sub_scope = blk: { |
| 2178 | const payload = for_node.payload.castTag(.PointerIndexPayload).?; | 2466 | const payload_token = for_full.payload_token.?; |
| 2179 | const is_ptr = payload.ptr_token != null; | 2467 | const ident = if (token_tags[payload_token] == .asterisk) |
| 2180 | const value_name = tree.tokenSlice(payload.value_symbol.firstToken()); | 2468 | payload_token + 1 |
| 2469 | else | ||
| 2470 | payload_token; | ||
| 2471 | const is_ptr = ident != payload_token; | ||
| 2472 | const value_name = tree.tokenSlice(ident); | ||
| 2181 | if (!mem.eql(u8, value_name, "_")) { | 2473 | if (!mem.eql(u8, value_name, "_")) { |
| 2182 | return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement for value payload", .{}); | 2474 | return mod.failNode(&then_scope.base, ident, "TODO implement for loop value payload", .{}); |
| 2183 | } else if (is_ptr) { | 2475 | } else if (is_ptr) { |
| 2184 | return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{}); | 2476 | return mod.failTok(&then_scope.base, payload_token, "pointer modifier invalid on discard", .{}); |
| 2185 | } | 2477 | } |
| 2186 | 2478 | ||
| 2187 | const index_symbol_node = payload.index_symbol orelse | 2479 | const index_token = if (token_tags[ident + 1] == .comma) |
| 2188 | break :blk &then_scope.base; | 2480 | ident + 2 |
| 2189 | 2481 | else | |
| 2190 | const index_name = tree.tokenSlice(index_symbol_node.firstToken()); | ||
| 2191 | if (mem.eql(u8, index_name, "_")) { | ||
| 2192 | break :blk &then_scope.base; | 2482 | break :blk &then_scope.base; |
| 2483 | if (mem.eql(u8, tree.tokenSlice(index_token), "_")) { | ||
| 2484 | return mod.failTok(&then_scope.base, index_token, "discard of index capture; omit it instead", .{}); | ||
| 2193 | } | 2485 | } |
| 2194 | // TODO make this const without an extra copy? | 2486 | const index_name = try mod.identifierTokenString(&then_scope.base, index_token); |
| 2195 | index_scope = .{ | 2487 | index_scope = .{ |
| 2196 | .parent = &then_scope.base, | 2488 | .parent = &then_scope.base, |
| 2197 | .gen_zir = &then_scope, | 2489 | .gen_zir = &then_scope, |
| ... | @@ -2202,7 +2494,7 @@ fn forExpr( | ... | @@ -2202,7 +2494,7 @@ fn forExpr( |
| 2202 | }; | 2494 | }; |
| 2203 | 2495 | ||
| 2204 | loop_scope.break_count += 1; | 2496 | loop_scope.break_count += 1; |
| 2205 | const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, for_node.body); | 2497 | const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr); |
| 2206 | 2498 | ||
| 2207 | // else branch | 2499 | // else branch |
| 2208 | var else_scope: Scope.GenZIR = .{ | 2500 | var else_scope: Scope.GenZIR = .{ |
| ... | @@ -2214,18 +2506,23 @@ fn forExpr( | ... | @@ -2214,18 +2506,23 @@ fn forExpr( |
| 2214 | }; | 2506 | }; |
| 2215 | defer else_scope.instructions.deinit(mod.gpa); | 2507 | defer else_scope.instructions.deinit(mod.gpa); |
| 2216 | 2508 | ||
| 2217 | var else_src: usize = undefined; | 2509 | const else_node = for_full.ast.else_expr; |
| 2218 | const else_result: ?*zir.Inst = if (for_node.@"else") |else_node| blk: { | 2510 | const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: { |
| 2219 | else_src = tree.token_locs[else_node.body.lastToken()].start; | ||
| 2220 | loop_scope.break_count += 1; | 2511 | loop_scope.break_count += 1; |
| 2221 | break :blk try expr(mod, &else_scope.base, loop_scope.break_result_loc, else_node.body); | 2512 | const sub_scope = &else_scope.base; |
| 2222 | } else blk: { | 2513 | break :blk .{ |
| 2223 | else_src = tree.token_locs[for_node.lastToken()].start; | 2514 | .src = token_starts[tree.lastToken(else_node)], |
| 2224 | break :blk null; | 2515 | .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node), |
| 2225 | }; | 2516 | }; |
| 2517 | } else | ||
| 2518 | .{ | ||
| 2519 | .src = token_starts[tree.lastToken(for_full.ast.then_expr)], | ||
| 2520 | .result = null, | ||
| 2521 | }; | ||
| 2522 | |||
| 2226 | if (loop_scope.label) |some| { | 2523 | if (loop_scope.label) |some| { |
| 2227 | if (!some.used) { | 2524 | if (!some.used) { |
| 2228 | return mod.fail(scope, tree.token_locs[some.token].start, "unused for label", .{}); | 2525 | return mod.fail(scope, token_starts[some.token], "unused for loop label", .{}); |
| 2229 | } | 2526 | } |
| 2230 | } | 2527 | } |
| 2231 | return finishThenElseBlock( | 2528 | return finishThenElseBlock( |
| ... | @@ -2238,39 +2535,48 @@ fn forExpr( | ... | @@ -2238,39 +2535,48 @@ fn forExpr( |
| 2238 | &condbr.positionals.then_body, | 2535 | &condbr.positionals.then_body, |
| 2239 | &condbr.positionals.else_body, | 2536 | &condbr.positionals.else_body, |
| 2240 | then_src, | 2537 | then_src, |
| 2241 | else_src, | 2538 | else_info.src, |
| 2242 | then_result, | 2539 | then_result, |
| 2243 | else_result, | 2540 | else_info.result, |
| 2244 | for_block, | 2541 | for_block, |
| 2245 | cond_block, | 2542 | cond_block, |
| 2246 | ); | 2543 | ); |
| 2247 | } | 2544 | } |
| 2248 | 2545 | ||
| 2249 | fn switchCaseUsesRef(node: *ast.Node.Switch) bool { | 2546 | fn getRangeNode( |
| 2250 | for (node.cases()) |uncasted_case| { | 2547 | node_tags: []const ast.Node.Tag, |
| 2251 | const case = uncasted_case.castTag(.SwitchCase).?; | 2548 | node_datas: []const ast.Node.Data, |
| 2252 | const uncasted_payload = case.payload orelse continue; | 2549 | start_node: ast.Node.Index, |
| 2253 | const payload = uncasted_payload.castTag(.PointerPayload).?; | 2550 | ) ?ast.Node.Index { |
| 2254 | if (payload.ptr_token) |_| return true; | 2551 | var node = start_node; |
| 2255 | } | ||
| 2256 | return false; | ||
| 2257 | } | ||
| 2258 | |||
| 2259 | fn getRangeNode(node: *ast.Node) ?*ast.Node.SimpleInfixOp { | ||
| 2260 | var cur = node; | ||
| 2261 | while (true) { | 2552 | while (true) { |
| 2262 | switch (cur.tag) { | 2553 | switch (node_tags[node]) { |
| 2263 | .Range => return @fieldParentPtr(ast.Node.SimpleInfixOp, "base", cur), | 2554 | .switch_range => return node, |
| 2264 | .GroupedExpression => cur = @fieldParentPtr(ast.Node.GroupedExpression, "base", cur).expr, | 2555 | .grouped_expression => node = node_datas[node].lhs, |
| 2265 | else => return null, | 2556 | else => return null, |
| 2266 | } | 2557 | } |
| 2267 | } | 2558 | } |
| 2268 | } | 2559 | } |
| 2269 | 2560 | ||
| 2270 | fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.Switch) InnerError!*zir.Inst { | 2561 | fn switchExpr( |
| 2562 | mod: *Module, | ||
| 2563 | scope: *Scope, | ||
| 2564 | rl: ResultLoc, | ||
| 2565 | switch_node: ast.Node.Index, | ||
| 2566 | ) InnerError!*zir.Inst { | ||
| 2271 | const tree = scope.tree(); | 2567 | const tree = scope.tree(); |
| 2272 | const switch_src = tree.token_locs[switch_node.switch_token].start; | 2568 | const node_datas = tree.nodes.items(.data); |
| 2273 | const use_ref = switchCaseUsesRef(switch_node); | 2569 | const main_tokens = tree.nodes.items(.main_token); |
| 2570 | const token_tags = tree.tokens.items(.tag); | ||
| 2571 | const token_starts = tree.tokens.items(.start); | ||
| 2572 | const node_tags = tree.nodes.items(.tag); | ||
| 2573 | |||
| 2574 | const switch_token = main_tokens[switch_node]; | ||
| 2575 | const target_node = node_datas[switch_node].lhs; | ||
| 2576 | const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange); | ||
| 2577 | const case_nodes = tree.extra_data[extra.start..extra.end]; | ||
| 2578 | |||
| 2579 | const switch_src = token_starts[switch_token]; | ||
| 2274 | 2580 | ||
| 2275 | var block_scope: Scope.GenZIR = .{ | 2581 | var block_scope: Scope.GenZIR = .{ |
| 2276 | .parent = scope, | 2582 | .parent = scope, |
| ... | @@ -2285,18 +2591,26 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node | ... | @@ -2285,18 +2591,26 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 2285 | var items = std.ArrayList(*zir.Inst).init(mod.gpa); | 2591 | var items = std.ArrayList(*zir.Inst).init(mod.gpa); |
| 2286 | defer items.deinit(); | 2592 | defer items.deinit(); |
| 2287 | 2593 | ||
| 2288 | // first we gather all the switch items and check else/'_' prongs | 2594 | // First we gather all the switch items and check else/'_' prongs. |
| 2289 | var else_src: ?usize = null; | 2595 | var else_src: ?usize = null; |
| 2290 | var underscore_src: ?usize = null; | 2596 | var underscore_src: ?usize = null; |
| 2291 | var first_range: ?*zir.Inst = null; | 2597 | var first_range: ?*zir.Inst = null; |
| 2292 | var simple_case_count: usize = 0; | 2598 | var simple_case_count: usize = 0; |
| 2293 | for (switch_node.cases()) |uncasted_case| { | 2599 | var any_payload_is_ref = false; |
| 2294 | const case = uncasted_case.castTag(.SwitchCase).?; | 2600 | for (case_nodes) |case_node| { |
| 2295 | const case_src = tree.token_locs[case.firstToken()].start; | 2601 | const case = switch (node_tags[case_node]) { |
| 2296 | assert(case.items_len != 0); | 2602 | .switch_case_one => tree.switchCaseOne(case_node), |
| 2297 | 2603 | .switch_case => tree.switchCase(case_node), | |
| 2604 | else => unreachable, | ||
| 2605 | }; | ||
| 2606 | if (case.payload_token) |payload_token| { | ||
| 2607 | if (token_tags[payload_token] == .asterisk) { | ||
| 2608 | any_payload_is_ref = true; | ||
| 2609 | } | ||
| 2610 | } | ||
| 2298 | // Check for else/_ prong, those are handled last. | 2611 | // Check for else/_ prong, those are handled last. |
| 2299 | if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) { | 2612 | if (case.ast.values.len == 0) { |
| 2613 | const case_src = token_starts[case.ast.arrow_token - 1]; | ||
| 2300 | if (else_src) |src| { | 2614 | if (else_src) |src| { |
| 2301 | const msg = msg: { | 2615 | const msg = msg: { |
| 2302 | const msg = try mod.errMsg( | 2616 | const msg = try mod.errMsg( |
| ... | @@ -2313,9 +2627,11 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node | ... | @@ -2313,9 +2627,11 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 2313 | } | 2627 | } |
| 2314 | else_src = case_src; | 2628 | else_src = case_src; |
| 2315 | continue; | 2629 | continue; |
| 2316 | } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and | 2630 | } else if (case.ast.values.len == 1 and |
| 2317 | mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) | 2631 | node_tags[case.ast.values[0]] == .identifier and |
| 2632 | mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")) | ||
| 2318 | { | 2633 | { |
| 2634 | const case_src = token_starts[case.ast.arrow_token - 1]; | ||
| 2319 | if (underscore_src) |src| { | 2635 | if (underscore_src) |src| { |
| 2320 | const msg = msg: { | 2636 | const msg = msg: { |
| 2321 | const msg = try mod.errMsg( | 2637 | const msg = try mod.errMsg( |
| ... | @@ -2352,14 +2668,18 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node | ... | @@ -2352,14 +2668,18 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 2352 | } | 2668 | } |
| 2353 | } | 2669 | } |
| 2354 | 2670 | ||
| 2355 | if (case.items_len == 1 and getRangeNode(case.items()[0]) == null) simple_case_count += 1; | 2671 | if (case.ast.values.len == 1 and |
| 2672 | getRangeNode(node_tags, node_datas, case.ast.values[0]) == null) | ||
| 2673 | { | ||
| 2674 | simple_case_count += 1; | ||
| 2675 | } | ||
| 2356 | 2676 | ||
| 2357 | // generate all the switch items as comptime expressions | 2677 | // Generate all the switch items as comptime expressions. |
| 2358 | for (case.items()) |item| { | 2678 | for (case.ast.values) |item| { |
| 2359 | if (getRangeNode(item)) |range| { | 2679 | if (getRangeNode(node_tags, node_datas, item)) |range| { |
| 2360 | const start = try comptimeExpr(mod, &block_scope.base, .none, range.lhs); | 2680 | const start = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].lhs); |
| 2361 | const end = try comptimeExpr(mod, &block_scope.base, .none, range.rhs); | 2681 | const end = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].rhs); |
| 2362 | const range_src = tree.token_locs[range.op_token].start; | 2682 | const range_src = token_starts[main_tokens[range]]; |
| 2363 | const range_inst = try addZIRBinOp(mod, &block_scope.base, range_src, .switch_range, start, end); | 2683 | const range_inst = try addZIRBinOp(mod, &block_scope.base, range_src, .switch_range, start, end); |
| 2364 | try items.append(range_inst); | 2684 | try items.append(range_inst); |
| 2365 | } else { | 2685 | } else { |
| ... | @@ -2374,21 +2694,25 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node | ... | @@ -2374,21 +2694,25 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 2374 | if (underscore_src != null) special_prong = .underscore; | 2694 | if (underscore_src != null) special_prong = .underscore; |
| 2375 | var cases = try block_scope.arena.alloc(zir.Inst.SwitchBr.Case, simple_case_count); | 2695 | var cases = try block_scope.arena.alloc(zir.Inst.SwitchBr.Case, simple_case_count); |
| 2376 | 2696 | ||
| 2377 | const target_ptr = if (use_ref) try expr(mod, &block_scope.base, .ref, switch_node.expr) else null; | 2697 | const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref) |
| 2378 | const target = if (target_ptr) |some| | 2698 | .{ |
| 2379 | try addZIRUnOp(mod, &block_scope.base, some.src, .deref, some) | 2699 | .rl = .ref, |
| 2700 | .tag = .switchbr_ref, | ||
| 2701 | } | ||
| 2380 | else | 2702 | else |
| 2381 | try expr(mod, &block_scope.base, .none, switch_node.expr); | 2703 | .{ |
| 2382 | const switch_inst = try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{ | 2704 | .rl = .none, |
| 2705 | .tag = .switchbr, | ||
| 2706 | }; | ||
| 2707 | const target = try expr(mod, &block_scope.base, rl_and_tag.rl, target_node); | ||
| 2708 | const switch_inst = try addZirInstT(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, rl_and_tag.tag, .{ | ||
| 2383 | .target = target, | 2709 | .target = target, |
| 2384 | .cases = cases, | 2710 | .cases = cases, |
| 2385 | .items = try block_scope.arena.dupe(*zir.Inst, items.items), | 2711 | .items = try block_scope.arena.dupe(*zir.Inst, items.items), |
| 2386 | .else_body = undefined, // populated below | 2712 | .else_body = undefined, // populated below |
| 2387 | }, .{ | ||
| 2388 | .range = first_range, | 2713 | .range = first_range, |
| 2389 | .special_prong = special_prong, | 2714 | .special_prong = special_prong, |
| 2390 | }); | 2715 | }); |
| 2391 | |||
| 2392 | const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{ | 2716 | const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{ |
| 2393 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), | 2717 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), |
| 2394 | }); | 2718 | }); |
| ... | @@ -2411,32 +2735,38 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node | ... | @@ -2411,32 +2735,38 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 2411 | }; | 2735 | }; |
| 2412 | defer else_scope.instructions.deinit(mod.gpa); | 2736 | defer else_scope.instructions.deinit(mod.gpa); |
| 2413 | 2737 | ||
| 2414 | // Now generate all but the special cases | 2738 | // Now generate all but the special cases. |
| 2415 | var special_case: ?*ast.Node.SwitchCase = null; | 2739 | var special_case: ?ast.full.SwitchCase = null; |
| 2416 | var items_index: usize = 0; | 2740 | var items_index: usize = 0; |
| 2417 | var case_index: usize = 0; | 2741 | var case_index: usize = 0; |
| 2418 | for (switch_node.cases()) |uncasted_case| { | 2742 | for (case_nodes) |case_node| { |
| 2419 | const case = uncasted_case.castTag(.SwitchCase).?; | 2743 | const case = switch (node_tags[case_node]) { |
| 2420 | const case_src = tree.token_locs[case.firstToken()].start; | 2744 | .switch_case_one => tree.switchCaseOne(case_node), |
| 2421 | // reset without freeing to reduce allocations. | 2745 | .switch_case => tree.switchCase(case_node), |
| 2422 | case_scope.instructions.items.len = 0; | 2746 | else => unreachable, |
| 2747 | }; | ||
| 2748 | const case_src = token_starts[main_tokens[case_node]]; | ||
| 2749 | case_scope.instructions.shrinkRetainingCapacity(0); | ||
| 2423 | 2750 | ||
| 2424 | // Check for else/_ prong, those are handled last. | 2751 | // Check for else/_ prong, those are handled last. |
| 2425 | if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) { | 2752 | if (case.ast.values.len == 0) { |
| 2426 | special_case = case; | 2753 | special_case = case; |
| 2427 | continue; | 2754 | continue; |
| 2428 | } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and | 2755 | } else if (case.ast.values.len == 1 and |
| 2429 | mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) | 2756 | node_tags[case.ast.values[0]] == .identifier and |
| 2757 | mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")) | ||
| 2430 | { | 2758 | { |
| 2431 | special_case = case; | 2759 | special_case = case; |
| 2432 | continue; | 2760 | continue; |
| 2433 | } | 2761 | } |
| 2434 | 2762 | ||
| 2435 | // If this is a simple one item prong then it is handled by the switchbr. | 2763 | // If this is a simple one item prong then it is handled by the switchbr. |
| 2436 | if (case.items_len == 1 and getRangeNode(case.items()[0]) == null) { | 2764 | if (case.ast.values.len == 1 and |
| 2765 | getRangeNode(node_tags, node_datas, case.ast.values[0]) == null) | ||
| 2766 | { | ||
| 2437 | const item = items.items[items_index]; | 2767 | const item = items.items[items_index]; |
| 2438 | items_index += 1; | 2768 | items_index += 1; |
| 2439 | try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target, target_ptr); | 2769 | try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target); |
| 2440 | 2770 | ||
| 2441 | cases[case_index] = .{ | 2771 | cases[case_index] = .{ |
| 2442 | .item = item, | 2772 | .item = item, |
| ... | @@ -2446,16 +2776,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node | ... | @@ -2446,16 +2776,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 2446 | continue; | 2776 | continue; |
| 2447 | } | 2777 | } |
| 2448 | 2778 | ||
| 2449 | // TODO if the case has few items and no ranges it might be better | ||
| 2450 | // to just handle them as switch prongs. | ||
| 2451 | |||
| 2452 | // Check if the target matches any of the items. | 2779 | // Check if the target matches any of the items. |
| 2453 | // 1, 2, 3..6 will result in | 2780 | // 1, 2, 3..6 will result in |
| 2454 | // target == 1 or target == 2 or (target >= 3 and target <= 6) | 2781 | // target == 1 or target == 2 or (target >= 3 and target <= 6) |
| 2782 | // TODO handle multiple items as switch prongs rather than along with ranges. | ||
| 2455 | var any_ok: ?*zir.Inst = null; | 2783 | var any_ok: ?*zir.Inst = null; |
| 2456 | for (case.items()) |item| { | 2784 | for (case.ast.values) |item| { |
| 2457 | if (getRangeNode(item)) |range| { | 2785 | if (getRangeNode(node_tags, node_datas, item)) |range| { |
| 2458 | const range_src = tree.token_locs[range.op_token].start; | 2786 | const range_src = token_starts[main_tokens[range]]; |
| 2459 | const range_inst = items.items[items_index].castTag(.switch_range).?; | 2787 | const range_inst = items.items[items_index].castTag(.switch_range).?; |
| 2460 | items_index += 1; | 2788 | items_index += 1; |
| 2461 | 2789 | ||
| ... | @@ -2494,7 +2822,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node | ... | @@ -2494,7 +2822,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 2494 | 2822 | ||
| 2495 | // reset cond_scope for then_body | 2823 | // reset cond_scope for then_body |
| 2496 | case_scope.instructions.items.len = 0; | 2824 | case_scope.instructions.items.len = 0; |
| 2497 | try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target, target_ptr); | 2825 | try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target); |
| 2498 | condbr.positionals.then_body = .{ | 2826 | condbr.positionals.then_body = .{ |
| 2499 | .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), | 2827 | .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), |
| 2500 | }; | 2828 | }; |
| ... | @@ -2511,12 +2839,12 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node | ... | @@ -2511,12 +2839,12 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 2511 | 2839 | ||
| 2512 | // Finally generate else block or a break. | 2840 | // Finally generate else block or a break. |
| 2513 | if (special_case) |case| { | 2841 | if (special_case) |case| { |
| 2514 | try switchCaseExpr(mod, &else_scope.base, block_scope.break_result_loc, block, case, target, target_ptr); | 2842 | try switchCaseExpr(mod, &else_scope.base, block_scope.break_result_loc, block, case, target); |
| 2515 | } else { | 2843 | } else { |
| 2516 | // Not handling all possible cases is a compile error. | 2844 | // Not handling all possible cases is a compile error. |
| 2517 | _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe); | 2845 | _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe); |
| 2518 | } | 2846 | } |
| 2519 | switch_inst.castTag(.switchbr).?.positionals.else_body = .{ | 2847 | switch_inst.positionals.else_body = .{ |
| 2520 | .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items), | 2848 | .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items), |
| 2521 | }; | 2849 | }; |
| 2522 | 2850 | ||
| ... | @@ -2528,27 +2856,34 @@ fn switchCaseExpr( | ... | @@ -2528,27 +2856,34 @@ fn switchCaseExpr( |
| 2528 | scope: *Scope, | 2856 | scope: *Scope, |
| 2529 | rl: ResultLoc, | 2857 | rl: ResultLoc, |
| 2530 | block: *zir.Inst.Block, | 2858 | block: *zir.Inst.Block, |
| 2531 | case: *ast.Node.SwitchCase, | 2859 | case: ast.full.SwitchCase, |
| 2532 | target: *zir.Inst, | 2860 | target: *zir.Inst, |
| 2533 | target_ptr: ?*zir.Inst, | ||
| 2534 | ) !void { | 2861 | ) !void { |
| 2535 | const tree = scope.tree(); | 2862 | const tree = scope.tree(); |
| 2536 | const case_src = tree.token_locs[case.firstToken()].start; | 2863 | const node_datas = tree.nodes.items(.data); |
| 2864 | const main_tokens = tree.nodes.items(.main_token); | ||
| 2865 | const token_starts = tree.tokens.items(.start); | ||
| 2866 | const token_tags = tree.tokens.items(.tag); | ||
| 2867 | |||
| 2868 | const case_src = token_starts[case.ast.arrow_token]; | ||
| 2537 | const sub_scope = blk: { | 2869 | const sub_scope = blk: { |
| 2538 | const uncasted_payload = case.payload orelse break :blk scope; | 2870 | const payload_token = case.payload_token orelse break :blk scope; |
| 2539 | const payload = uncasted_payload.castTag(.PointerPayload).?; | 2871 | const ident = if (token_tags[payload_token] == .asterisk) |
| 2540 | const is_ptr = payload.ptr_token != null; | 2872 | payload_token + 1 |
| 2541 | const value_name = tree.tokenSlice(payload.value_symbol.firstToken()); | 2873 | else |
| 2874 | payload_token; | ||
| 2875 | const is_ptr = ident != payload_token; | ||
| 2876 | const value_name = tree.tokenSlice(ident); | ||
| 2542 | if (mem.eql(u8, value_name, "_")) { | 2877 | if (mem.eql(u8, value_name, "_")) { |
| 2543 | if (is_ptr) { | 2878 | if (is_ptr) { |
| 2544 | return mod.failTok(scope, payload.ptr_token.?, "pointer modifier invalid on discard", .{}); | 2879 | return mod.failTok(scope, payload_token, "pointer modifier invalid on discard", .{}); |
| 2545 | } | 2880 | } |
| 2546 | break :blk scope; | 2881 | break :blk scope; |
| 2547 | } | 2882 | } |
| 2548 | return mod.failNode(scope, payload.value_symbol, "TODO implement switch value payload", .{}); | 2883 | return mod.failTok(scope, ident, "TODO implement switch value payload", .{}); |
| 2549 | }; | 2884 | }; |
| 2550 | 2885 | ||
| 2551 | const case_body = try expr(mod, sub_scope, rl, case.expr); | 2886 | const case_body = try expr(mod, sub_scope, rl, case.ast.target_expr); |
| 2552 | if (!case_body.tag.isNoReturn()) { | 2887 | if (!case_body.tag.isNoReturn()) { |
| 2553 | _ = try addZIRInst(mod, sub_scope, case_src, zir.Inst.Break, .{ | 2888 | _ = try addZIRInst(mod, sub_scope, case_src, zir.Inst.Break, .{ |
| 2554 | .block = block, | 2889 | .block = block, |
| ... | @@ -2557,11 +2892,16 @@ fn switchCaseExpr( | ... | @@ -2557,11 +2892,16 @@ fn switchCaseExpr( |
| 2557 | } | 2892 | } |
| 2558 | } | 2893 | } |
| 2559 | 2894 | ||
| 2560 | fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { | 2895 | fn ret(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst { |
| 2561 | const tree = scope.tree(); | 2896 | const tree = scope.tree(); |
| 2562 | const src = tree.token_locs[cfe.ltoken].start; | 2897 | const node_datas = tree.nodes.items(.data); |
| 2563 | if (cfe.getRHS()) |rhs_node| { | 2898 | const main_tokens = tree.nodes.items(.main_token); |
| 2564 | if (nodeMayNeedMemoryLocation(rhs_node, scope)) { | 2899 | const token_starts = tree.tokens.items(.start); |
| 2900 | |||
| 2901 | const src = token_starts[main_tokens[node]]; | ||
| 2902 | const rhs_node = node_datas[node].lhs; | ||
| 2903 | if (rhs_node != 0) { | ||
| 2904 | if (nodeMayNeedMemoryLocation(scope, rhs_node)) { | ||
| 2565 | const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr); | 2905 | const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr); |
| 2566 | const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node); | 2906 | const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node); |
| 2567 | return addZIRUnOp(mod, scope, src, .@"return", operand); | 2907 | return addZIRUnOp(mod, scope, src, .@"return", operand); |
| ... | @@ -2575,19 +2915,31 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE | ... | @@ -2575,19 +2915,31 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE |
| 2575 | } | 2915 | } |
| 2576 | } | 2916 | } |
| 2577 | 2917 | ||
| 2578 | fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneToken) InnerError!*zir.Inst { | 2918 | fn identifier( |
| 2919 | mod: *Module, | ||
| 2920 | scope: *Scope, | ||
| 2921 | rl: ResultLoc, | ||
| 2922 | ident: ast.Node.Index, | ||
| 2923 | ) InnerError!*zir.Inst { | ||
| 2579 | const tracy = trace(@src()); | 2924 | const tracy = trace(@src()); |
| 2580 | defer tracy.end(); | 2925 | defer tracy.end(); |
| 2581 | 2926 | ||
| 2582 | const tree = scope.tree(); | 2927 | const tree = scope.tree(); |
| 2583 | const ident_name = try mod.identifierTokenString(scope, ident.token); | 2928 | const main_tokens = tree.nodes.items(.main_token); |
| 2584 | const src = tree.token_locs[ident.token].start; | 2929 | const token_starts = tree.tokens.items(.start); |
| 2930 | |||
| 2931 | const ident_token = main_tokens[ident]; | ||
| 2932 | const ident_name = try mod.identifierTokenString(scope, ident_token); | ||
| 2933 | const src = token_starts[ident_token]; | ||
| 2585 | if (mem.eql(u8, ident_name, "_")) { | 2934 | if (mem.eql(u8, ident_name, "_")) { |
| 2586 | return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{}); | 2935 | return mod.failNode(scope, ident, "TODO implement '_' identifier", .{}); |
| 2587 | } | 2936 | } |
| 2588 | 2937 | ||
| 2589 | if (getSimplePrimitiveValue(ident_name)) |typed_value| { | 2938 | if (simple_types.get(ident_name)) |val_tag| { |
| 2590 | const result = try addZIRInstConst(mod, scope, src, typed_value); | 2939 | const result = try addZIRInstConst(mod, scope, src, TypedValue{ |
| 2940 | .ty = Type.initTag(.type), | ||
| 2941 | .val = Value.initTag(val_tag), | ||
| 2942 | }); | ||
| 2591 | return rvalue(mod, scope, rl, result); | 2943 | return rvalue(mod, scope, rl, result); |
| 2592 | } | 2944 | } |
| 2593 | 2945 | ||
| ... | @@ -2598,7 +2950,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo | ... | @@ -2598,7 +2950,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo |
| 2598 | const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) { | 2950 | const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) { |
| 2599 | error.Overflow => return mod.failNode( | 2951 | error.Overflow => return mod.failNode( |
| 2600 | scope, | 2952 | scope, |
| 2601 | &ident.base, | 2953 | ident, |
| 2602 | "primitive integer type '{s}' exceeds maximum bit width of 65535", | 2954 | "primitive integer type '{s}' exceeds maximum bit width of 65535", |
| 2603 | .{ident_name}, | 2955 | .{ident_name}, |
| 2604 | ), | 2956 | ), |
| ... | @@ -2662,59 +3014,104 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo | ... | @@ -2662,59 +3014,104 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo |
| 2662 | } | 3014 | } |
| 2663 | } | 3015 | } |
| 2664 | 3016 | ||
| 2665 | return mod.failNode(scope, &ident.base, "use of undeclared identifier '{s}'", .{ident_name}); | 3017 | return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name}); |
| 2666 | } | 3018 | } |
| 2667 | 3019 | ||
| 2668 | fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) InnerError!*zir.Inst { | 3020 | fn parseStringLiteral(mod: *Module, scope: *Scope, token: ast.TokenIndex) ![]u8 { |
| 2669 | const tree = scope.tree(); | 3021 | const tree = scope.tree(); |
| 2670 | const unparsed_bytes = tree.tokenSlice(str_lit.token); | 3022 | const token_tags = tree.tokens.items(.tag); |
| 3023 | const token_starts = tree.tokens.items(.start); | ||
| 3024 | assert(token_tags[token] == .string_literal); | ||
| 3025 | const unparsed = tree.tokenSlice(token); | ||
| 2671 | const arena = scope.arena(); | 3026 | const arena = scope.arena(); |
| 2672 | |||
| 2673 | var bad_index: usize = undefined; | 3027 | var bad_index: usize = undefined; |
| 2674 | const bytes = std.zig.parseStringLiteral(arena, unparsed_bytes, &bad_index) catch |err| switch (err) { | 3028 | const bytes = std.zig.parseStringLiteral(arena, unparsed, &bad_index) catch |err| switch (err) { |
| 2675 | error.InvalidCharacter => { | 3029 | error.InvalidCharacter => { |
| 2676 | const bad_byte = unparsed_bytes[bad_index]; | 3030 | const bad_byte = unparsed[bad_index]; |
| 2677 | const src = tree.token_locs[str_lit.token].start; | 3031 | const src = token_starts[token]; |
| 2678 | return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte}); | 3032 | return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'", .{ |
| 3033 | bad_byte, | ||
| 3034 | }); | ||
| 2679 | }, | 3035 | }, |
| 2680 | else => |e| return e, | 3036 | else => |e| return e, |
| 2681 | }; | 3037 | }; |
| 2682 | 3038 | return bytes; | |
| 2683 | const src = tree.token_locs[str_lit.token].start; | ||
| 2684 | return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{}); | ||
| 2685 | } | 3039 | } |
| 2686 | 3040 | ||
| 2687 | fn multilineStrLiteral(mod: *Module, scope: *Scope, node: *ast.Node.MultilineStringLiteral) !*zir.Inst { | 3041 | fn stringLiteral( |
| 3042 | mod: *Module, | ||
| 3043 | scope: *Scope, | ||
| 3044 | rl: ResultLoc, | ||
| 3045 | str_lit: ast.Node.Index, | ||
| 3046 | ) InnerError!*zir.Inst { | ||
| 2688 | const tree = scope.tree(); | 3047 | const tree = scope.tree(); |
| 2689 | const lines = node.linesConst(); | 3048 | const main_tokens = tree.nodes.items(.main_token); |
| 2690 | const src = tree.token_locs[lines[0]].start; | 3049 | const token_starts = tree.tokens.items(.start); |
| 2691 | 3050 | ||
| 2692 | // line lengths and new lines | 3051 | const str_lit_token = main_tokens[str_lit]; |
| 2693 | var len = lines.len - 1; | 3052 | const bytes = try parseStringLiteral(mod, scope, str_lit_token); |
| 2694 | for (lines) |line| { | 3053 | const src = token_starts[str_lit_token]; |
| 2695 | // 2 for the '//' + 1 for '\n' | 3054 | const str_inst = try addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{}); |
| 2696 | len += tree.tokenSlice(line).len - 3; | 3055 | return rvalue(mod, scope, rl, str_inst); |
| 2697 | } | 3056 | } |
| 2698 | 3057 | ||
| 2699 | const bytes = try scope.arena().alloc(u8, len); | 3058 | fn multilineStringLiteral( |
| 2700 | var i: usize = 0; | 3059 | mod: *Module, |
| 2701 | for (lines) |line, line_i| { | 3060 | scope: *Scope, |
| 2702 | if (line_i != 0) { | 3061 | rl: ResultLoc, |
| 2703 | bytes[i] = '\n'; | 3062 | str_lit: ast.Node.Index, |
| 2704 | i += 1; | 3063 | ) InnerError!*zir.Inst { |
| 3064 | const tree = scope.tree(); | ||
| 3065 | const node_datas = tree.nodes.items(.data); | ||
| 3066 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3067 | const token_starts = tree.tokens.items(.start); | ||
| 3068 | |||
| 3069 | const start = node_datas[str_lit].lhs; | ||
| 3070 | const end = node_datas[str_lit].rhs; | ||
| 3071 | |||
| 3072 | // Count the number of bytes to allocate. | ||
| 3073 | const len: usize = len: { | ||
| 3074 | var tok_i = start; | ||
| 3075 | var len: usize = end - start + 1; | ||
| 3076 | while (tok_i <= end) : (tok_i += 1) { | ||
| 3077 | // 2 for the '//' + 1 for '\n' | ||
| 3078 | len += tree.tokenSlice(tok_i).len - 3; | ||
| 2705 | } | 3079 | } |
| 2706 | const slice = tree.tokenSlice(line); | 3080 | break :len len; |
| 2707 | mem.copy(u8, bytes[i..], slice[2 .. slice.len - 1]); | 3081 | }; |
| 2708 | i += slice.len - 3; | 3082 | const bytes = try scope.arena().alloc(u8, len); |
| 3083 | // First line: do not append a newline. | ||
| 3084 | var byte_i: usize = 0; | ||
| 3085 | var tok_i = start; | ||
| 3086 | { | ||
| 3087 | const slice = tree.tokenSlice(tok_i); | ||
| 3088 | const line_bytes = slice[2 .. slice.len - 1]; | ||
| 3089 | mem.copy(u8, bytes[byte_i..], line_bytes); | ||
| 3090 | byte_i += line_bytes.len; | ||
| 3091 | tok_i += 1; | ||
| 2709 | } | 3092 | } |
| 2710 | 3093 | // Following lines: each line prepends a newline. | |
| 2711 | return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{}); | 3094 | while (tok_i <= end) : (tok_i += 1) { |
| 3095 | bytes[byte_i] = '\n'; | ||
| 3096 | byte_i += 1; | ||
| 3097 | const slice = tree.tokenSlice(tok_i); | ||
| 3098 | const line_bytes = slice[2 .. slice.len - 1]; | ||
| 3099 | mem.copy(u8, bytes[byte_i..], line_bytes); | ||
| 3100 | byte_i += line_bytes.len; | ||
| 3101 | } | ||
| 3102 | const src = token_starts[start]; | ||
| 3103 | const str_inst = try addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{}); | ||
| 3104 | return rvalue(mod, scope, rl, str_inst); | ||
| 2712 | } | 3105 | } |
| 2713 | 3106 | ||
| 2714 | fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst { | 3107 | fn charLiteral(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !*zir.Inst { |
| 2715 | const tree = scope.tree(); | 3108 | const tree = scope.tree(); |
| 2716 | const src = tree.token_locs[node.token].start; | 3109 | const main_tokens = tree.nodes.items(.main_token); |
| 2717 | const slice = tree.tokenSlice(node.token); | 3110 | const main_token = main_tokens[node]; |
| 3111 | const token_starts = tree.tokens.items(.start); | ||
| 3112 | |||
| 3113 | const src = token_starts[main_token]; | ||
| 3114 | const slice = tree.tokenSlice(main_token); | ||
| 2718 | 3115 | ||
| 2719 | var bad_index: usize = undefined; | 3116 | var bad_index: usize = undefined; |
| 2720 | const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) { | 3117 | const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) { |
| ... | @@ -2723,18 +3120,27 @@ fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst | ... | @@ -2723,18 +3120,27 @@ fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst |
| 2723 | return mod.fail(scope, src + bad_index, "invalid character: '{c}'\n", .{bad_byte}); | 3120 | return mod.fail(scope, src + bad_index, "invalid character: '{c}'\n", .{bad_byte}); |
| 2724 | }, | 3121 | }, |
| 2725 | }; | 3122 | }; |
| 2726 | 3123 | const result = try addZIRInstConst(mod, scope, src, .{ | |
| 2727 | return addZIRInstConst(mod, scope, src, .{ | ||
| 2728 | .ty = Type.initTag(.comptime_int), | 3124 | .ty = Type.initTag(.comptime_int), |
| 2729 | .val = try Value.Tag.int_u64.create(scope.arena(), value), | 3125 | .val = try Value.Tag.int_u64.create(scope.arena(), value), |
| 2730 | }); | 3126 | }); |
| 3127 | return rvalue(mod, scope, rl, result); | ||
| 2731 | } | 3128 | } |
| 2732 | 3129 | ||
| 2733 | fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) InnerError!*zir.Inst { | 3130 | fn integerLiteral( |
| 3131 | mod: *Module, | ||
| 3132 | scope: *Scope, | ||
| 3133 | rl: ResultLoc, | ||
| 3134 | int_lit: ast.Node.Index, | ||
| 3135 | ) InnerError!*zir.Inst { | ||
| 2734 | const arena = scope.arena(); | 3136 | const arena = scope.arena(); |
| 2735 | const tree = scope.tree(); | 3137 | const tree = scope.tree(); |
| 2736 | const prefixed_bytes = tree.tokenSlice(int_lit.token); | 3138 | const main_tokens = tree.nodes.items(.main_token); |
| 2737 | const base = if (mem.startsWith(u8, prefixed_bytes, "0x")) | 3139 | const token_starts = tree.tokens.items(.start); |
| 3140 | |||
| 3141 | const int_token = main_tokens[int_lit]; | ||
| 3142 | const prefixed_bytes = tree.tokenSlice(int_token); | ||
| 3143 | const base: u8 = if (mem.startsWith(u8, prefixed_bytes, "0x")) | ||
| 2738 | 16 | 3144 | 16 |
| 2739 | else if (mem.startsWith(u8, prefixed_bytes, "0o")) | 3145 | else if (mem.startsWith(u8, prefixed_bytes, "0o")) |
| 2740 | 8 | 3146 | 8 |
| ... | @@ -2749,90 +3155,70 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) Inne | ... | @@ -2749,90 +3155,70 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) Inne |
| 2749 | prefixed_bytes[2..]; | 3155 | prefixed_bytes[2..]; |
| 2750 | 3156 | ||
| 2751 | if (std.fmt.parseInt(u64, bytes, base)) |small_int| { | 3157 | if (std.fmt.parseInt(u64, bytes, base)) |small_int| { |
| 2752 | const src = tree.token_locs[int_lit.token].start; | 3158 | const src = token_starts[int_token]; |
| 2753 | return addZIRInstConst(mod, scope, src, .{ | 3159 | const result = try addZIRInstConst(mod, scope, src, .{ |
| 2754 | .ty = Type.initTag(.comptime_int), | 3160 | .ty = Type.initTag(.comptime_int), |
| 2755 | .val = try Value.Tag.int_u64.create(arena, small_int), | 3161 | .val = try Value.Tag.int_u64.create(arena, small_int), |
| 2756 | }); | 3162 | }); |
| 3163 | return rvalue(mod, scope, rl, result); | ||
| 2757 | } else |err| { | 3164 | } else |err| { |
| 2758 | return mod.failTok(scope, int_lit.token, "TODO implement int literals that don't fit in a u64", .{}); | 3165 | return mod.failTok(scope, int_token, "TODO implement int literals that don't fit in a u64", .{}); |
| 2759 | } | 3166 | } |
| 2760 | } | 3167 | } |
| 2761 | 3168 | ||
| 2762 | fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) InnerError!*zir.Inst { | 3169 | fn floatLiteral( |
| 3170 | mod: *Module, | ||
| 3171 | scope: *Scope, | ||
| 3172 | rl: ResultLoc, | ||
| 3173 | float_lit: ast.Node.Index, | ||
| 3174 | ) InnerError!*zir.Inst { | ||
| 2763 | const arena = scope.arena(); | 3175 | const arena = scope.arena(); |
| 2764 | const tree = scope.tree(); | 3176 | const tree = scope.tree(); |
| 2765 | const bytes = tree.tokenSlice(float_lit.token); | 3177 | const main_tokens = tree.nodes.items(.main_token); |
| 3178 | const token_starts = tree.tokens.items(.start); | ||
| 3179 | |||
| 3180 | const main_token = main_tokens[float_lit]; | ||
| 3181 | const bytes = tree.tokenSlice(main_token); | ||
| 2766 | if (bytes.len > 2 and bytes[1] == 'x') { | 3182 | if (bytes.len > 2 and bytes[1] == 'x') { |
| 2767 | return mod.failTok(scope, float_lit.token, "TODO hex floats", .{}); | 3183 | return mod.failTok(scope, main_token, "TODO implement hex floats", .{}); |
| 2768 | } | 3184 | } |
| 2769 | |||
| 2770 | const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) { | 3185 | const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) { |
| 2771 | error.InvalidCharacter => unreachable, // validated by tokenizer | 3186 | error.InvalidCharacter => unreachable, // validated by tokenizer |
| 2772 | }; | 3187 | }; |
| 2773 | const src = tree.token_locs[float_lit.token].start; | 3188 | const src = token_starts[main_token]; |
| 2774 | return addZIRInstConst(mod, scope, src, .{ | 3189 | const result = try addZIRInstConst(mod, scope, src, .{ |
| 2775 | .ty = Type.initTag(.comptime_float), | 3190 | .ty = Type.initTag(.comptime_float), |
| 2776 | .val = try Value.Tag.float_128.create(arena, float_number), | 3191 | .val = try Value.Tag.float_128.create(arena, float_number), |
| 2777 | }); | 3192 | }); |
| 3193 | return rvalue(mod, scope, rl, result); | ||
| 2778 | } | 3194 | } |
| 2779 | 3195 | ||
| 2780 | fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { | 3196 | fn asmExpr(mod: *Module, scope: *Scope, rl: ResultLoc, full: ast.full.Asm) InnerError!*zir.Inst { |
| 2781 | const arena = scope.arena(); | ||
| 2782 | const tree = scope.tree(); | ||
| 2783 | const src = tree.token_locs[node.token].start; | ||
| 2784 | return addZIRInstConst(mod, scope, src, .{ | ||
| 2785 | .ty = Type.initTag(.@"undefined"), | ||
| 2786 | .val = Value.initTag(.undef), | ||
| 2787 | }); | ||
| 2788 | } | ||
| 2789 | |||
| 2790 | fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { | ||
| 2791 | const arena = scope.arena(); | ||
| 2792 | const tree = scope.tree(); | ||
| 2793 | const src = tree.token_locs[node.token].start; | ||
| 2794 | return addZIRInstConst(mod, scope, src, .{ | ||
| 2795 | .ty = Type.initTag(.bool), | ||
| 2796 | .val = switch (tree.token_ids[node.token]) { | ||
| 2797 | .Keyword_true => Value.initTag(.bool_true), | ||
| 2798 | .Keyword_false => Value.initTag(.bool_false), | ||
| 2799 | else => unreachable, | ||
| 2800 | }, | ||
| 2801 | }); | ||
| 2802 | } | ||
| 2803 | |||
| 2804 | fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { | ||
| 2805 | const arena = scope.arena(); | 3197 | const arena = scope.arena(); |
| 2806 | const tree = scope.tree(); | 3198 | const tree = scope.tree(); |
| 2807 | const src = tree.token_locs[node.token].start; | 3199 | const main_tokens = tree.nodes.items(.main_token); |
| 2808 | return addZIRInstConst(mod, scope, src, .{ | 3200 | const token_starts = tree.tokens.items(.start); |
| 2809 | .ty = Type.initTag(.@"null"), | 3201 | const node_datas = tree.nodes.items(.data); |
| 2810 | .val = Value.initTag(.null_value), | ||
| 2811 | }); | ||
| 2812 | } | ||
| 2813 | 3202 | ||
| 2814 | fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst { | 3203 | if (full.outputs.len != 0) { |
| 2815 | if (asm_node.outputs.len != 0) { | 3204 | return mod.failTok(scope, full.ast.asm_token, "TODO implement asm with an output", .{}); |
| 2816 | return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{}); | ||
| 2817 | } | 3205 | } |
| 2818 | const arena = scope.arena(); | ||
| 2819 | const tree = scope.tree(); | ||
| 2820 | 3206 | ||
| 2821 | const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len); | 3207 | const inputs = try arena.alloc([]const u8, full.inputs.len); |
| 2822 | const args = try arena.alloc(*zir.Inst, asm_node.inputs.len); | 3208 | const args = try arena.alloc(*zir.Inst, full.inputs.len); |
| 2823 | |||
| 2824 | const src = tree.token_locs[asm_node.asm_token].start; | ||
| 2825 | 3209 | ||
| 3210 | const src = token_starts[full.ast.asm_token]; | ||
| 2826 | const str_type = try addZIRInstConst(mod, scope, src, .{ | 3211 | const str_type = try addZIRInstConst(mod, scope, src, .{ |
| 2827 | .ty = Type.initTag(.type), | 3212 | .ty = Type.initTag(.type), |
| 2828 | .val = Value.initTag(.const_slice_u8_type), | 3213 | .val = Value.initTag(.const_slice_u8_type), |
| 2829 | }); | 3214 | }); |
| 2830 | const str_type_rl: ResultLoc = .{ .ty = str_type }; | 3215 | const str_type_rl: ResultLoc = .{ .ty = str_type }; |
| 2831 | 3216 | ||
| 2832 | for (asm_node.inputs) |input, i| { | 3217 | for (full.inputs) |input, i| { |
| 2833 | // TODO semantically analyze constraints | 3218 | // TODO semantically analyze constraints |
| 2834 | inputs[i] = try expr(mod, scope, str_type_rl, input.constraint); | 3219 | const constraint_token = main_tokens[input] + 2; |
| 2835 | args[i] = try expr(mod, scope, .none, input.expr); | 3220 | inputs[i] = try parseStringLiteral(mod, scope, constraint_token); |
| 3221 | args[i] = try expr(mod, scope, .none, node_datas[input].lhs); | ||
| 2836 | } | 3222 | } |
| 2837 | 3223 | ||
| 2838 | const return_type = try addZIRInstConst(mod, scope, src, .{ | 3224 | const return_type = try addZIRInstConst(mod, scope, src, .{ |
| ... | @@ -2840,81 +3226,47 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi | ... | @@ -2840,81 +3226,47 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi |
| 2840 | .val = Value.initTag(.void_type), | 3226 | .val = Value.initTag(.void_type), |
| 2841 | }); | 3227 | }); |
| 2842 | const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{ | 3228 | const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{ |
| 2843 | .asm_source = try expr(mod, scope, str_type_rl, asm_node.template), | 3229 | .asm_source = try expr(mod, scope, str_type_rl, full.ast.template), |
| 2844 | .return_type = return_type, | 3230 | .return_type = return_type, |
| 2845 | }, .{ | 3231 | }, .{ |
| 2846 | .@"volatile" = asm_node.volatile_token != null, | 3232 | .@"volatile" = full.volatile_token != null, |
| 2847 | //.clobbers = TODO handle clobbers | 3233 | //.clobbers = TODO handle clobbers |
| 2848 | .inputs = inputs, | 3234 | .inputs = inputs, |
| 2849 | .args = args, | 3235 | .args = args, |
| 2850 | }); | 3236 | }); |
| 2851 | return asm_inst; | 3237 | return rvalue(mod, scope, rl, asm_inst); |
| 2852 | } | ||
| 2853 | |||
| 2854 | fn ensureBuiltinParamCount(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall, count: u32) !void { | ||
| 2855 | if (call.params_len == count) | ||
| 2856 | return; | ||
| 2857 | |||
| 2858 | const s = if (count == 1) "" else "s"; | ||
| 2859 | return mod.failTok(scope, call.builtin_token, "expected {d} parameter{s}, found {d}", .{ count, s, call.params_len }); | ||
| 2860 | } | ||
| 2861 | |||
| 2862 | fn simpleCast( | ||
| 2863 | mod: *Module, | ||
| 2864 | scope: *Scope, | ||
| 2865 | rl: ResultLoc, | ||
| 2866 | call: *ast.Node.BuiltinCall, | ||
| 2867 | inst_tag: zir.Inst.Tag, | ||
| 2868 | ) InnerError!*zir.Inst { | ||
| 2869 | try ensureBuiltinParamCount(mod, scope, call, 2); | ||
| 2870 | const tree = scope.tree(); | ||
| 2871 | const src = tree.token_locs[call.builtin_token].start; | ||
| 2872 | const params = call.params(); | ||
| 2873 | const dest_type = try typeExpr(mod, scope, params[0]); | ||
| 2874 | const rhs = try expr(mod, scope, .none, params[1]); | ||
| 2875 | const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs); | ||
| 2876 | return rvalue(mod, scope, rl, result); | ||
| 2877 | } | ||
| 2878 | |||
| 2879 | fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | ||
| 2880 | try ensureBuiltinParamCount(mod, scope, call, 1); | ||
| 2881 | const operand = try expr(mod, scope, .none, call.params()[0]); | ||
| 2882 | const tree = scope.tree(); | ||
| 2883 | const src = tree.token_locs[call.builtin_token].start; | ||
| 2884 | return addZIRUnOp(mod, scope, src, .ptrtoint, operand); | ||
| 2885 | } | 3238 | } |
| 2886 | 3239 | ||
| 2887 | fn as( | 3240 | fn as( |
| 2888 | mod: *Module, | 3241 | mod: *Module, |
| 2889 | scope: *Scope, | 3242 | scope: *Scope, |
| 2890 | rl: ResultLoc, | 3243 | rl: ResultLoc, |
| 2891 | call: *ast.Node.BuiltinCall, | 3244 | builtin_token: ast.TokenIndex, |
| 3245 | src: usize, | ||
| 3246 | lhs: ast.Node.Index, | ||
| 3247 | rhs: ast.Node.Index, | ||
| 2892 | ) InnerError!*zir.Inst { | 3248 | ) InnerError!*zir.Inst { |
| 2893 | try ensureBuiltinParamCount(mod, scope, call, 2); | 3249 | const dest_type = try typeExpr(mod, scope, lhs); |
| 2894 | const tree = scope.tree(); | ||
| 2895 | const src = tree.token_locs[call.builtin_token].start; | ||
| 2896 | const params = call.params(); | ||
| 2897 | const dest_type = try typeExpr(mod, scope, params[0]); | ||
| 2898 | switch (rl) { | 3250 | switch (rl) { |
| 2899 | .none, .discard, .ref, .ty => { | 3251 | .none, .discard, .ref, .ty => { |
| 2900 | const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]); | 3252 | const result = try expr(mod, scope, .{ .ty = dest_type }, rhs); |
| 2901 | return rvalue(mod, scope, rl, result); | 3253 | return rvalue(mod, scope, rl, result); |
| 2902 | }, | 3254 | }, |
| 2903 | 3255 | ||
| 2904 | .ptr => |result_ptr| { | 3256 | .ptr => |result_ptr| { |
| 2905 | return asRlPtr(mod, scope, rl, src, result_ptr, params[1], dest_type); | 3257 | return asRlPtr(mod, scope, rl, src, result_ptr, rhs, dest_type); |
| 2906 | }, | 3258 | }, |
| 2907 | .block_ptr => |block_scope| { | 3259 | .block_ptr => |block_scope| { |
| 2908 | return asRlPtr(mod, scope, rl, src, block_scope.rl_ptr.?, params[1], dest_type); | 3260 | return asRlPtr(mod, scope, rl, src, block_scope.rl_ptr.?, rhs, dest_type); |
| 2909 | }, | 3261 | }, |
| 2910 | 3262 | ||
| 2911 | .bitcasted_ptr => |bitcasted_ptr| { | 3263 | .bitcasted_ptr => |bitcasted_ptr| { |
| 2912 | // TODO here we should be able to resolve the inference; we now have a type for the result. | 3264 | // TODO here we should be able to resolve the inference; we now have a type for the result. |
| 2913 | return mod.failTok(scope, call.builtin_token, "TODO implement @as with result location @bitCast", .{}); | 3265 | return mod.failTok(scope, builtin_token, "TODO implement @as with result location @bitCast", .{}); |
| 2914 | }, | 3266 | }, |
| 2915 | .inferred_ptr => |result_alloc| { | 3267 | .inferred_ptr => |result_alloc| { |
| 2916 | // TODO here we should be able to resolve the inference; we now have a type for the result. | 3268 | // TODO here we should be able to resolve the inference; we now have a type for the result. |
| 2917 | return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{}); | 3269 | return mod.failTok(scope, builtin_token, "TODO implement @as with inferred-type result location pointer", .{}); |
| 2918 | }, | 3270 | }, |
| 2919 | } | 3271 | } |
| 2920 | } | 3272 | } |
| ... | @@ -2925,7 +3277,7 @@ fn asRlPtr( | ... | @@ -2925,7 +3277,7 @@ fn asRlPtr( |
| 2925 | rl: ResultLoc, | 3277 | rl: ResultLoc, |
| 2926 | src: usize, | 3278 | src: usize, |
| 2927 | result_ptr: *zir.Inst, | 3279 | result_ptr: *zir.Inst, |
| 2928 | operand_node: *ast.Node, | 3280 | operand_node: ast.Node.Index, |
| 2929 | dest_type: *zir.Inst, | 3281 | dest_type: *zir.Inst, |
| 2930 | ) InnerError!*zir.Inst { | 3282 | ) InnerError!*zir.Inst { |
| 2931 | // Detect whether this expr() call goes into rvalue() to store the result into the | 3283 | // Detect whether this expr() call goes into rvalue() to store the result into the |
| ... | @@ -2963,155 +3315,295 @@ fn asRlPtr( | ... | @@ -2963,155 +3315,295 @@ fn asRlPtr( |
| 2963 | } | 3315 | } |
| 2964 | } | 3316 | } |
| 2965 | 3317 | ||
| 2966 | fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | 3318 | fn bitCast( |
| 2967 | try ensureBuiltinParamCount(mod, scope, call, 2); | 3319 | mod: *Module, |
| 2968 | const tree = scope.tree(); | 3320 | scope: *Scope, |
| 2969 | const src = tree.token_locs[call.builtin_token].start; | 3321 | rl: ResultLoc, |
| 2970 | const params = call.params(); | 3322 | builtin_token: ast.TokenIndex, |
| 2971 | const dest_type = try typeExpr(mod, scope, params[0]); | 3323 | src: usize, |
| 3324 | lhs: ast.Node.Index, | ||
| 3325 | rhs: ast.Node.Index, | ||
| 3326 | ) InnerError!*zir.Inst { | ||
| 3327 | const dest_type = try typeExpr(mod, scope, lhs); | ||
| 2972 | switch (rl) { | 3328 | switch (rl) { |
| 2973 | .none => { | 3329 | .none => { |
| 2974 | const operand = try expr(mod, scope, .none, params[1]); | 3330 | const operand = try expr(mod, scope, .none, rhs); |
| 2975 | return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand); | 3331 | return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand); |
| 2976 | }, | 3332 | }, |
| 2977 | .discard => { | 3333 | .discard => { |
| 2978 | const operand = try expr(mod, scope, .none, params[1]); | 3334 | const operand = try expr(mod, scope, .none, rhs); |
| 2979 | const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand); | 3335 | const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand); |
| 2980 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); | 3336 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); |
| 2981 | return result; | 3337 | return result; |
| 2982 | }, | 3338 | }, |
| 2983 | .ref => { | 3339 | .ref => { |
| 2984 | const operand = try expr(mod, scope, .ref, params[1]); | 3340 | const operand = try expr(mod, scope, .ref, rhs); |
| 2985 | const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand); | 3341 | const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand); |
| 2986 | return result; | 3342 | return result; |
| 2987 | }, | 3343 | }, |
| 2988 | .ty => |result_ty| { | 3344 | .ty => |result_ty| { |
| 2989 | const result = try expr(mod, scope, .none, params[1]); | 3345 | const result = try expr(mod, scope, .none, rhs); |
| 2990 | const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result); | 3346 | const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result); |
| 2991 | return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted); | 3347 | return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted); |
| 2992 | }, | 3348 | }, |
| 2993 | .ptr => |result_ptr| { | 3349 | .ptr => |result_ptr| { |
| 2994 | const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr); | 3350 | const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr); |
| 2995 | return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]); | 3351 | return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, rhs); |
| 2996 | }, | 3352 | }, |
| 2997 | .bitcasted_ptr => |bitcasted_ptr| { | 3353 | .bitcasted_ptr => |bitcasted_ptr| { |
| 2998 | return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location another @bitCast", .{}); | 3354 | return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location another @bitCast", .{}); |
| 2999 | }, | 3355 | }, |
| 3000 | .block_ptr => |block_ptr| { | 3356 | .block_ptr => |block_ptr| { |
| 3001 | return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location inferred peer types", .{}); | 3357 | return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location inferred peer types", .{}); |
| 3002 | }, | 3358 | }, |
| 3003 | .inferred_ptr => |result_alloc| { | 3359 | .inferred_ptr => |result_alloc| { |
| 3004 | // TODO here we should be able to resolve the inference; we now have a type for the result. | 3360 | // TODO here we should be able to resolve the inference; we now have a type for the result. |
| 3005 | return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{}); | 3361 | return mod.failTok(scope, builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{}); |
| 3006 | }, | 3362 | }, |
| 3007 | } | 3363 | } |
| 3008 | } | 3364 | } |
| 3009 | 3365 | ||
| 3010 | fn import(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | 3366 | fn typeOf( |
| 3011 | try ensureBuiltinParamCount(mod, scope, call, 1); | 3367 | mod: *Module, |
| 3012 | const tree = scope.tree(); | 3368 | scope: *Scope, |
| 3013 | const src = tree.token_locs[call.builtin_token].start; | 3369 | rl: ResultLoc, |
| 3014 | const params = call.params(); | 3370 | builtin_token: ast.TokenIndex, |
| 3015 | const target = try expr(mod, scope, .none, params[0]); | 3371 | src: usize, |
| 3016 | return addZIRUnOp(mod, scope, src, .import, target); | 3372 | params: []const ast.Node.Index, |
| 3017 | } | 3373 | ) InnerError!*zir.Inst { |
| 3018 | |||
| 3019 | fn compileError(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | ||
| 3020 | try ensureBuiltinParamCount(mod, scope, call, 1); | ||
| 3021 | const tree = scope.tree(); | ||
| 3022 | const src = tree.token_locs[call.builtin_token].start; | ||
| 3023 | const params = call.params(); | ||
| 3024 | const target = try expr(mod, scope, .none, params[0]); | ||
| 3025 | return addZIRUnOp(mod, scope, src, .compile_error, target); | ||
| 3026 | } | ||
| 3027 | |||
| 3028 | fn setEvalBranchQuota(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | ||
| 3029 | try ensureBuiltinParamCount(mod, scope, call, 1); | ||
| 3030 | const tree = scope.tree(); | ||
| 3031 | const src = tree.token_locs[call.builtin_token].start; | ||
| 3032 | const params = call.params(); | ||
| 3033 | const u32_type = try addZIRInstConst(mod, scope, src, .{ | ||
| 3034 | .ty = Type.initTag(.type), | ||
| 3035 | .val = Value.initTag(.u32_type), | ||
| 3036 | }); | ||
| 3037 | const quota = try expr(mod, scope, .{ .ty = u32_type }, params[0]); | ||
| 3038 | return addZIRUnOp(mod, scope, src, .set_eval_branch_quota, quota); | ||
| 3039 | } | ||
| 3040 | |||
| 3041 | fn typeOf(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | ||
| 3042 | const tree = scope.tree(); | ||
| 3043 | const arena = scope.arena(); | ||
| 3044 | const src = tree.token_locs[call.builtin_token].start; | ||
| 3045 | const params = call.params(); | ||
| 3046 | if (params.len < 1) { | 3374 | if (params.len < 1) { |
| 3047 | return mod.failTok(scope, call.builtin_token, "expected at least 1 argument, found 0", .{}); | 3375 | return mod.failTok(scope, builtin_token, "expected at least 1 argument, found 0", .{}); |
| 3048 | } | 3376 | } |
| 3049 | if (params.len == 1) { | 3377 | if (params.len == 1) { |
| 3050 | return rvalue(mod, scope, rl, try addZIRUnOp(mod, scope, src, .typeof, try expr(mod, scope, .none, params[0]))); | 3378 | return rvalue(mod, scope, rl, try addZIRUnOp(mod, scope, src, .typeof, try expr(mod, scope, .none, params[0]))); |
| 3051 | } | 3379 | } |
| 3380 | const arena = scope.arena(); | ||
| 3052 | var items = try arena.alloc(*zir.Inst, params.len); | 3381 | var items = try arena.alloc(*zir.Inst, params.len); |
| 3053 | for (params) |param, param_i| | 3382 | for (params) |param, param_i| |
| 3054 | items[param_i] = try expr(mod, scope, .none, param); | 3383 | items[param_i] = try expr(mod, scope, .none, param); |
| 3055 | return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.TypeOfPeer, .{ .items = items }, .{})); | 3384 | return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.TypeOfPeer, .{ .items = items }, .{})); |
| 3056 | } | 3385 | } |
| 3057 | fn compileLog(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | ||
| 3058 | const tree = scope.tree(); | ||
| 3059 | const arena = scope.arena(); | ||
| 3060 | const src = tree.token_locs[call.builtin_token].start; | ||
| 3061 | const params = call.params(); | ||
| 3062 | var targets = try arena.alloc(*zir.Inst, params.len); | ||
| 3063 | for (params) |param, param_i| | ||
| 3064 | targets[param_i] = try expr(mod, scope, .none, param); | ||
| 3065 | return addZIRInst(mod, scope, src, zir.Inst.CompileLog, .{ .to_log = targets }, .{}); | ||
| 3066 | } | ||
| 3067 | 3386 | ||
| 3068 | fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | 3387 | fn builtinCall( |
| 3388 | mod: *Module, | ||
| 3389 | scope: *Scope, | ||
| 3390 | rl: ResultLoc, | ||
| 3391 | call: ast.Node.Index, | ||
| 3392 | params: []const ast.Node.Index, | ||
| 3393 | ) InnerError!*zir.Inst { | ||
| 3069 | const tree = scope.tree(); | 3394 | const tree = scope.tree(); |
| 3070 | const builtin_name = tree.tokenSlice(call.builtin_token); | 3395 | const main_tokens = tree.nodes.items(.main_token); |
| 3396 | const token_starts = tree.tokens.items(.start); | ||
| 3397 | |||
| 3398 | const builtin_token = main_tokens[call]; | ||
| 3399 | const builtin_name = tree.tokenSlice(builtin_token); | ||
| 3071 | 3400 | ||
| 3072 | // We handle the different builtins manually because they have different semantics depending | 3401 | // We handle the different builtins manually because they have different semantics depending |
| 3073 | // on the function. For example, `@as` and others participate in result location semantics, | 3402 | // on the function. For example, `@as` and others participate in result location semantics, |
| 3074 | // and `@cImport` creates a special scope that collects a .c source code text buffer. | 3403 | // and `@cImport` creates a special scope that collects a .c source code text buffer. |
| 3075 | // Also, some builtins have a variable number of parameters. | 3404 | // Also, some builtins have a variable number of parameters. |
| 3076 | 3405 | ||
| 3077 | if (mem.eql(u8, builtin_name, "@ptrToInt")) { | 3406 | const info = BuiltinFn.list.get(builtin_name) orelse { |
| 3078 | return rvalue(mod, scope, rl, try ptrToInt(mod, scope, call)); | 3407 | return mod.failTok(scope, builtin_token, "invalid builtin function: '{s}'", .{ |
| 3079 | } else if (mem.eql(u8, builtin_name, "@as")) { | 3408 | builtin_name, |
| 3080 | return as(mod, scope, rl, call); | 3409 | }); |
| 3081 | } else if (mem.eql(u8, builtin_name, "@floatCast")) { | 3410 | }; |
| 3082 | return simpleCast(mod, scope, rl, call, .floatcast); | 3411 | if (info.param_count) |expected| { |
| 3083 | } else if (mem.eql(u8, builtin_name, "@intCast")) { | 3412 | if (expected != params.len) { |
| 3084 | return simpleCast(mod, scope, rl, call, .intcast); | 3413 | const s = if (expected == 1) "" else "s"; |
| 3085 | } else if (mem.eql(u8, builtin_name, "@bitCast")) { | 3414 | return mod.failTok(scope, builtin_token, "expected {d} parameter{s}, found {d}", .{ |
| 3086 | return bitCast(mod, scope, rl, call); | 3415 | expected, s, params.len, |
| 3087 | } else if (mem.eql(u8, builtin_name, "@TypeOf")) { | 3416 | }); |
| 3088 | return typeOf(mod, scope, rl, call); | 3417 | } |
| 3089 | } else if (mem.eql(u8, builtin_name, "@breakpoint")) { | 3418 | } |
| 3090 | const src = tree.token_locs[call.builtin_token].start; | 3419 | const src = token_starts[builtin_token]; |
| 3091 | return rvalue(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint)); | 3420 | |
| 3092 | } else if (mem.eql(u8, builtin_name, "@import")) { | 3421 | switch (info.tag) { |
| 3093 | return rvalue(mod, scope, rl, try import(mod, scope, call)); | 3422 | .ptr_to_int => { |
| 3094 | } else if (mem.eql(u8, builtin_name, "@compileError")) { | 3423 | const operand = try expr(mod, scope, .none, params[0]); |
| 3095 | return compileError(mod, scope, call); | 3424 | const result = try addZIRUnOp(mod, scope, src, .ptrtoint, operand); |
| 3096 | } else if (mem.eql(u8, builtin_name, "@setEvalBranchQuota")) { | 3425 | return rvalue(mod, scope, rl, result); |
| 3097 | return setEvalBranchQuota(mod, scope, call); | 3426 | }, |
| 3098 | } else if (mem.eql(u8, builtin_name, "@compileLog")) { | 3427 | .float_cast => { |
| 3099 | return compileLog(mod, scope, call); | 3428 | const dest_type = try typeExpr(mod, scope, params[0]); |
| 3100 | } else if (mem.eql(u8, builtin_name, "@field")) { | 3429 | const rhs = try expr(mod, scope, .none, params[1]); |
| 3101 | return namedField(mod, scope, rl, call); | 3430 | const result = try addZIRBinOp(mod, scope, src, .floatcast, dest_type, rhs); |
| 3102 | } else { | 3431 | return rvalue(mod, scope, rl, result); |
| 3103 | return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{s}'", .{builtin_name}); | 3432 | }, |
| 3433 | .int_cast => { | ||
| 3434 | const dest_type = try typeExpr(mod, scope, params[0]); | ||
| 3435 | const rhs = try expr(mod, scope, .none, params[1]); | ||
| 3436 | const result = try addZIRBinOp(mod, scope, src, .intcast, dest_type, rhs); | ||
| 3437 | return rvalue(mod, scope, rl, result); | ||
| 3438 | }, | ||
| 3439 | .breakpoint => { | ||
| 3440 | const result = try addZIRNoOp(mod, scope, src, .breakpoint); | ||
| 3441 | return rvalue(mod, scope, rl, result); | ||
| 3442 | }, | ||
| 3443 | .import => { | ||
| 3444 | const target = try expr(mod, scope, .none, params[0]); | ||
| 3445 | const result = try addZIRUnOp(mod, scope, src, .import, target); | ||
| 3446 | return rvalue(mod, scope, rl, result); | ||
| 3447 | }, | ||
| 3448 | .compile_error => { | ||
| 3449 | const target = try expr(mod, scope, .none, params[0]); | ||
| 3450 | const result = try addZIRUnOp(mod, scope, src, .compile_error, target); | ||
| 3451 | return rvalue(mod, scope, rl, result); | ||
| 3452 | }, | ||
| 3453 | .set_eval_branch_quota => { | ||
| 3454 | const u32_type = try addZIRInstConst(mod, scope, src, .{ | ||
| 3455 | .ty = Type.initTag(.type), | ||
| 3456 | .val = Value.initTag(.u32_type), | ||
| 3457 | }); | ||
| 3458 | const quota = try expr(mod, scope, .{ .ty = u32_type }, params[0]); | ||
| 3459 | const result = try addZIRUnOp(mod, scope, src, .set_eval_branch_quota, quota); | ||
| 3460 | return rvalue(mod, scope, rl, result); | ||
| 3461 | }, | ||
| 3462 | .compile_log => { | ||
| 3463 | const arena = scope.arena(); | ||
| 3464 | var targets = try arena.alloc(*zir.Inst, params.len); | ||
| 3465 | for (params) |param, param_i| | ||
| 3466 | targets[param_i] = try expr(mod, scope, .none, param); | ||
| 3467 | const result = try addZIRInst(mod, scope, src, zir.Inst.CompileLog, .{ .to_log = targets }, .{}); | ||
| 3468 | return rvalue(mod, scope, rl, result); | ||
| 3469 | }, | ||
| 3470 | .field => { | ||
| 3471 | const string_type = try addZIRInstConst(mod, scope, src, .{ | ||
| 3472 | .ty = Type.initTag(.type), | ||
| 3473 | .val = Value.initTag(.const_slice_u8_type), | ||
| 3474 | }); | ||
| 3475 | const string_rl: ResultLoc = .{ .ty = string_type }; | ||
| 3476 | |||
| 3477 | if (rl == .ref) { | ||
| 3478 | return addZirInstTag(mod, scope, src, .field_ptr_named, .{ | ||
| 3479 | .object = try expr(mod, scope, .ref, params[0]), | ||
| 3480 | .field_name = try comptimeExpr(mod, scope, string_rl, params[1]), | ||
| 3481 | }); | ||
| 3482 | } | ||
| 3483 | return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val_named, .{ | ||
| 3484 | .object = try expr(mod, scope, .none, params[0]), | ||
| 3485 | .field_name = try comptimeExpr(mod, scope, string_rl, params[1]), | ||
| 3486 | })); | ||
| 3487 | }, | ||
| 3488 | .as => return as(mod, scope, rl, builtin_token, src, params[0], params[1]), | ||
| 3489 | .bit_cast => return bitCast(mod, scope, rl, builtin_token, src, params[0], params[1]), | ||
| 3490 | .TypeOf => return typeOf(mod, scope, rl, builtin_token, src, params), | ||
| 3491 | |||
| 3492 | .add_with_overflow, | ||
| 3493 | .align_cast, | ||
| 3494 | .align_of, | ||
| 3495 | .async_call, | ||
| 3496 | .atomic_load, | ||
| 3497 | .atomic_rmw, | ||
| 3498 | .atomic_store, | ||
| 3499 | .bit_offset_of, | ||
| 3500 | .bool_to_int, | ||
| 3501 | .bit_size_of, | ||
| 3502 | .mul_add, | ||
| 3503 | .byte_swap, | ||
| 3504 | .bit_reverse, | ||
| 3505 | .byte_offset_of, | ||
| 3506 | .call, | ||
| 3507 | .c_define, | ||
| 3508 | .c_import, | ||
| 3509 | .c_include, | ||
| 3510 | .clz, | ||
| 3511 | .cmpxchg_strong, | ||
| 3512 | .cmpxchg_weak, | ||
| 3513 | .ctz, | ||
| 3514 | .c_undef, | ||
| 3515 | .div_exact, | ||
| 3516 | .div_floor, | ||
| 3517 | .div_trunc, | ||
| 3518 | .embed_file, | ||
| 3519 | .enum_to_int, | ||
| 3520 | .error_name, | ||
| 3521 | .error_return_trace, | ||
| 3522 | .error_to_int, | ||
| 3523 | .err_set_cast, | ||
| 3524 | .@"export", | ||
| 3525 | .fence, | ||
| 3526 | .field_parent_ptr, | ||
| 3527 | .float_to_int, | ||
| 3528 | .frame, | ||
| 3529 | .Frame, | ||
| 3530 | .frame_address, | ||
| 3531 | .frame_size, | ||
| 3532 | .has_decl, | ||
| 3533 | .has_field, | ||
| 3534 | .int_to_enum, | ||
| 3535 | .int_to_error, | ||
| 3536 | .int_to_float, | ||
| 3537 | .int_to_ptr, | ||
| 3538 | .memcpy, | ||
| 3539 | .memset, | ||
| 3540 | .wasm_memory_size, | ||
| 3541 | .wasm_memory_grow, | ||
| 3542 | .mod, | ||
| 3543 | .mul_with_overflow, | ||
| 3544 | .panic, | ||
| 3545 | .pop_count, | ||
| 3546 | .ptr_cast, | ||
| 3547 | .rem, | ||
| 3548 | .return_address, | ||
| 3549 | .set_align_stack, | ||
| 3550 | .set_cold, | ||
| 3551 | .set_float_mode, | ||
| 3552 | .set_runtime_safety, | ||
| 3553 | .shl_exact, | ||
| 3554 | .shl_with_overflow, | ||
| 3555 | .shr_exact, | ||
| 3556 | .shuffle, | ||
| 3557 | .size_of, | ||
| 3558 | .splat, | ||
| 3559 | .reduce, | ||
| 3560 | .src, | ||
| 3561 | .sqrt, | ||
| 3562 | .sin, | ||
| 3563 | .cos, | ||
| 3564 | .exp, | ||
| 3565 | .exp2, | ||
| 3566 | .log, | ||
| 3567 | .log2, | ||
| 3568 | .log10, | ||
| 3569 | .fabs, | ||
| 3570 | .floor, | ||
| 3571 | .ceil, | ||
| 3572 | .trunc, | ||
| 3573 | .round, | ||
| 3574 | .sub_with_overflow, | ||
| 3575 | .tag_name, | ||
| 3576 | .This, | ||
| 3577 | .truncate, | ||
| 3578 | .Type, | ||
| 3579 | .type_info, | ||
| 3580 | .type_name, | ||
| 3581 | .union_init, | ||
| 3582 | => return mod.failTok(scope, builtin_token, "TODO: implement builtin function {s}", .{ | ||
| 3583 | builtin_name, | ||
| 3584 | }), | ||
| 3104 | } | 3585 | } |
| 3105 | } | 3586 | } |
| 3106 | 3587 | ||
| 3107 | fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) InnerError!*zir.Inst { | 3588 | fn callExpr( |
| 3589 | mod: *Module, | ||
| 3590 | scope: *Scope, | ||
| 3591 | rl: ResultLoc, | ||
| 3592 | call: ast.full.Call, | ||
| 3593 | ) InnerError!*zir.Inst { | ||
| 3594 | if (call.async_token) |async_token| { | ||
| 3595 | return mod.failTok(scope, async_token, "TODO implement async fn call", .{}); | ||
| 3596 | } | ||
| 3597 | |||
| 3108 | const tree = scope.tree(); | 3598 | const tree = scope.tree(); |
| 3109 | const lhs = try expr(mod, scope, .none, node.lhs); | 3599 | const main_tokens = tree.nodes.items(.main_token); |
| 3600 | const token_starts = tree.tokens.items(.start); | ||
| 3110 | 3601 | ||
| 3111 | const param_nodes = node.params(); | 3602 | const lhs = try expr(mod, scope, .none, call.ast.fn_expr); |
| 3112 | const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len); | 3603 | |
| 3113 | for (param_nodes) |param_node, i| { | 3604 | const args = try scope.getGenZIR().arena.alloc(*zir.Inst, call.ast.params.len); |
| 3114 | const param_src = tree.token_locs[param_node.firstToken()].start; | 3605 | for (call.ast.params) |param_node, i| { |
| 3606 | const param_src = token_starts[tree.firstToken(param_node)]; | ||
| 3115 | const param_type = try addZIRInst(mod, scope, param_src, zir.Inst.ParamType, .{ | 3607 | const param_type = try addZIRInst(mod, scope, param_src, zir.Inst.ParamType, .{ |
| 3116 | .func = lhs, | 3608 | .func = lhs, |
| 3117 | .arg_index = i, | 3609 | .arg_index = i, |
| ... | @@ -3119,7 +3611,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In | ... | @@ -3119,7 +3611,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In |
| 3119 | args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node); | 3611 | args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node); |
| 3120 | } | 3612 | } |
| 3121 | 3613 | ||
| 3122 | const src = tree.token_locs[node.lhs.firstToken()].start; | 3614 | const src = token_starts[call.ast.lparen]; |
| 3123 | const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{ | 3615 | const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{ |
| 3124 | .func = lhs, | 3616 | .func = lhs, |
| 3125 | .args = args, | 3617 | .args = args, |
| ... | @@ -3128,288 +3620,244 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In | ... | @@ -3128,288 +3620,244 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In |
| 3128 | return rvalue(mod, scope, rl, result); | 3620 | return rvalue(mod, scope, rl, result); |
| 3129 | } | 3621 | } |
| 3130 | 3622 | ||
| 3131 | fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst { | 3623 | pub const simple_types = std.ComptimeStringMap(Value.Tag, .{ |
| 3624 | .{ "u8", .u8_type }, | ||
| 3625 | .{ "i8", .i8_type }, | ||
| 3626 | .{ "isize", .isize_type }, | ||
| 3627 | .{ "usize", .usize_type }, | ||
| 3628 | .{ "c_short", .c_short_type }, | ||
| 3629 | .{ "c_ushort", .c_ushort_type }, | ||
| 3630 | .{ "c_int", .c_int_type }, | ||
| 3631 | .{ "c_uint", .c_uint_type }, | ||
| 3632 | .{ "c_long", .c_long_type }, | ||
| 3633 | .{ "c_ulong", .c_ulong_type }, | ||
| 3634 | .{ "c_longlong", .c_longlong_type }, | ||
| 3635 | .{ "c_ulonglong", .c_ulonglong_type }, | ||
| 3636 | .{ "c_longdouble", .c_longdouble_type }, | ||
| 3637 | .{ "f16", .f16_type }, | ||
| 3638 | .{ "f32", .f32_type }, | ||
| 3639 | .{ "f64", .f64_type }, | ||
| 3640 | .{ "f128", .f128_type }, | ||
| 3641 | .{ "c_void", .c_void_type }, | ||
| 3642 | .{ "bool", .bool_type }, | ||
| 3643 | .{ "void", .void_type }, | ||
| 3644 | .{ "type", .type_type }, | ||
| 3645 | .{ "anyerror", .anyerror_type }, | ||
| 3646 | .{ "comptime_int", .comptime_int_type }, | ||
| 3647 | .{ "comptime_float", .comptime_float_type }, | ||
| 3648 | .{ "noreturn", .noreturn_type }, | ||
| 3649 | }); | ||
| 3650 | |||
| 3651 | fn nodeMayNeedMemoryLocation(scope: *Scope, start_node: ast.Node.Index) bool { | ||
| 3132 | const tree = scope.tree(); | 3652 | const tree = scope.tree(); |
| 3133 | const src = tree.token_locs[unreach_node.token].start; | 3653 | const node_tags = tree.nodes.items(.tag); |
| 3134 | return addZIRNoOp(mod, scope, src, .unreachable_safe); | 3654 | const node_datas = tree.nodes.items(.data); |
| 3135 | } | 3655 | const main_tokens = tree.nodes.items(.main_token); |
| 3136 | 3656 | const token_tags = tree.tokens.items(.tag); | |
| 3137 | fn getSimplePrimitiveValue(name: []const u8) ?TypedValue { | ||
| 3138 | const simple_types = std.ComptimeStringMap(Value.Tag, .{ | ||
| 3139 | .{ "u8", .u8_type }, | ||
| 3140 | .{ "i8", .i8_type }, | ||
| 3141 | .{ "isize", .isize_type }, | ||
| 3142 | .{ "usize", .usize_type }, | ||
| 3143 | .{ "c_short", .c_short_type }, | ||
| 3144 | .{ "c_ushort", .c_ushort_type }, | ||
| 3145 | .{ "c_int", .c_int_type }, | ||
| 3146 | .{ "c_uint", .c_uint_type }, | ||
| 3147 | .{ "c_long", .c_long_type }, | ||
| 3148 | .{ "c_ulong", .c_ulong_type }, | ||
| 3149 | .{ "c_longlong", .c_longlong_type }, | ||
| 3150 | .{ "c_ulonglong", .c_ulonglong_type }, | ||
| 3151 | .{ "c_longdouble", .c_longdouble_type }, | ||
| 3152 | .{ "f16", .f16_type }, | ||
| 3153 | .{ "f32", .f32_type }, | ||
| 3154 | .{ "f64", .f64_type }, | ||
| 3155 | .{ "f128", .f128_type }, | ||
| 3156 | .{ "c_void", .c_void_type }, | ||
| 3157 | .{ "bool", .bool_type }, | ||
| 3158 | .{ "void", .void_type }, | ||
| 3159 | .{ "type", .type_type }, | ||
| 3160 | .{ "anyerror", .anyerror_type }, | ||
| 3161 | .{ "comptime_int", .comptime_int_type }, | ||
| 3162 | .{ "comptime_float", .comptime_float_type }, | ||
| 3163 | .{ "noreturn", .noreturn_type }, | ||
| 3164 | }); | ||
| 3165 | if (simple_types.get(name)) |tag| { | ||
| 3166 | return TypedValue{ | ||
| 3167 | .ty = Type.initTag(.type), | ||
| 3168 | .val = Value.initTag(tag), | ||
| 3169 | }; | ||
| 3170 | } | ||
| 3171 | return null; | ||
| 3172 | } | ||
| 3173 | 3657 | ||
| 3174 | fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool { | ||
| 3175 | var node = start_node; | 3658 | var node = start_node; |
| 3176 | while (true) { | 3659 | while (true) { |
| 3177 | switch (node.tag) { | 3660 | switch (node_tags[node]) { |
| 3178 | .Root, | 3661 | .root, |
| 3179 | .Use, | 3662 | .@"usingnamespace", |
| 3180 | .TestDecl, | 3663 | .test_decl, |
| 3181 | .DocComment, | 3664 | .switch_case, |
| 3182 | .SwitchCase, | 3665 | .switch_case_one, |
| 3183 | .SwitchElse, | 3666 | .container_field_init, |
| 3184 | .Else, | 3667 | .container_field_align, |
| 3185 | .Payload, | 3668 | .container_field, |
| 3186 | .PointerPayload, | 3669 | .asm_output, |
| 3187 | .PointerIndexPayload, | 3670 | .asm_input, |
| 3188 | .ContainerField, | ||
| 3189 | .ErrorTag, | ||
| 3190 | .FieldInitializer, | ||
| 3191 | => unreachable, | 3671 | => unreachable, |
| 3192 | 3672 | ||
| 3193 | .Return, | 3673 | .@"return", |
| 3194 | .Break, | 3674 | .@"break", |
| 3195 | .Continue, | 3675 | .@"continue", |
| 3196 | .BitNot, | 3676 | .bit_not, |
| 3197 | .BoolNot, | 3677 | .bool_not, |
| 3198 | .VarDecl, | 3678 | .global_var_decl, |
| 3199 | .Defer, | 3679 | .local_var_decl, |
| 3200 | .AddressOf, | 3680 | .simple_var_decl, |
| 3201 | .OptionalType, | 3681 | .aligned_var_decl, |
| 3202 | .Negation, | 3682 | .@"defer", |
| 3203 | .NegationWrap, | 3683 | .@"errdefer", |
| 3204 | .Resume, | 3684 | .address_of, |
| 3205 | .ArrayType, | 3685 | .optional_type, |
| 3206 | .ArrayTypeSentinel, | 3686 | .negation, |
| 3207 | .PtrType, | 3687 | .negation_wrap, |
| 3208 | .SliceType, | 3688 | .@"resume", |
| 3209 | .Suspend, | 3689 | .array_type, |
| 3210 | .AnyType, | 3690 | .array_type_sentinel, |
| 3211 | .ErrorType, | 3691 | .ptr_type_aligned, |
| 3212 | .FnProto, | 3692 | .ptr_type_sentinel, |
| 3213 | .AnyFrameType, | 3693 | .ptr_type, |
| 3214 | .IntegerLiteral, | 3694 | .ptr_type_bit_range, |
| 3215 | .FloatLiteral, | 3695 | .@"suspend", |
| 3216 | .EnumLiteral, | 3696 | .@"anytype", |
| 3217 | .StringLiteral, | 3697 | .fn_proto_simple, |
| 3218 | .MultilineStringLiteral, | 3698 | .fn_proto_multi, |
| 3219 | .CharLiteral, | 3699 | .fn_proto_one, |
| 3220 | .BoolLiteral, | 3700 | .fn_proto, |
| 3221 | .NullLiteral, | 3701 | .fn_decl, |
| 3222 | .UndefinedLiteral, | 3702 | .anyframe_type, |
| 3223 | .Unreachable, | 3703 | .anyframe_literal, |
| 3224 | .Identifier, | 3704 | .integer_literal, |
| 3225 | .ErrorSetDecl, | 3705 | .float_literal, |
| 3226 | .ContainerDecl, | 3706 | .enum_literal, |
| 3227 | .Asm, | 3707 | .string_literal, |
| 3228 | .Add, | 3708 | .multiline_string_literal, |
| 3229 | .AddWrap, | 3709 | .char_literal, |
| 3230 | .ArrayCat, | 3710 | .true_literal, |
| 3231 | .ArrayMult, | 3711 | .false_literal, |
| 3232 | .Assign, | 3712 | .null_literal, |
| 3233 | .AssignBitAnd, | 3713 | .undefined_literal, |
| 3234 | .AssignBitOr, | 3714 | .unreachable_literal, |
| 3235 | .AssignBitShiftLeft, | 3715 | .identifier, |
| 3236 | .AssignBitShiftRight, | 3716 | .error_set_decl, |
| 3237 | .AssignBitXor, | 3717 | .container_decl, |
| 3238 | .AssignDiv, | 3718 | .container_decl_trailing, |
| 3239 | .AssignSub, | 3719 | .container_decl_two, |
| 3240 | .AssignSubWrap, | 3720 | .container_decl_two_trailing, |
| 3241 | .AssignMod, | 3721 | .container_decl_arg, |
| 3242 | .AssignAdd, | 3722 | .container_decl_arg_trailing, |
| 3243 | .AssignAddWrap, | 3723 | .tagged_union, |
| 3244 | .AssignMul, | 3724 | .tagged_union_trailing, |
| 3245 | .AssignMulWrap, | 3725 | .tagged_union_two, |
| 3246 | .BangEqual, | 3726 | .tagged_union_two_trailing, |
| 3247 | .BitAnd, | 3727 | .tagged_union_enum_tag, |
| 3248 | .BitOr, | 3728 | .tagged_union_enum_tag_trailing, |
| 3249 | .BitShiftLeft, | 3729 | .@"asm", |
| 3250 | .BitShiftRight, | 3730 | .asm_simple, |
| 3251 | .BitXor, | 3731 | .add, |
| 3252 | .BoolAnd, | 3732 | .add_wrap, |
| 3253 | .BoolOr, | 3733 | .array_cat, |
| 3254 | .Div, | 3734 | .array_mult, |
| 3255 | .EqualEqual, | 3735 | .assign, |
| 3256 | .ErrorUnion, | 3736 | .assign_bit_and, |
| 3257 | .GreaterOrEqual, | 3737 | .assign_bit_or, |
| 3258 | .GreaterThan, | 3738 | .assign_bit_shift_left, |
| 3259 | .LessOrEqual, | 3739 | .assign_bit_shift_right, |
| 3260 | .LessThan, | 3740 | .assign_bit_xor, |
| 3261 | .MergeErrorSets, | 3741 | .assign_div, |
| 3262 | .Mod, | 3742 | .assign_sub, |
| 3263 | .Mul, | 3743 | .assign_sub_wrap, |
| 3264 | .MulWrap, | 3744 | .assign_mod, |
| 3265 | .Range, | 3745 | .assign_add, |
| 3266 | .Period, | 3746 | .assign_add_wrap, |
| 3267 | .Sub, | 3747 | .assign_mul, |
| 3268 | .SubWrap, | 3748 | .assign_mul_wrap, |
| 3269 | .Slice, | 3749 | .bang_equal, |
| 3270 | .Deref, | 3750 | .bit_and, |
| 3271 | .ArrayAccess, | 3751 | .bit_or, |
| 3272 | .Block, | 3752 | .bit_shift_left, |
| 3753 | .bit_shift_right, | ||
| 3754 | .bit_xor, | ||
| 3755 | .bool_and, | ||
| 3756 | .bool_or, | ||
| 3757 | .div, | ||
| 3758 | .equal_equal, | ||
| 3759 | .error_union, | ||
| 3760 | .greater_or_equal, | ||
| 3761 | .greater_than, | ||
| 3762 | .less_or_equal, | ||
| 3763 | .less_than, | ||
| 3764 | .merge_error_sets, | ||
| 3765 | .mod, | ||
| 3766 | .mul, | ||
| 3767 | .mul_wrap, | ||
| 3768 | .switch_range, | ||
| 3769 | .field_access, | ||
| 3770 | .sub, | ||
| 3771 | .sub_wrap, | ||
| 3772 | .slice, | ||
| 3773 | .slice_open, | ||
| 3774 | .slice_sentinel, | ||
| 3775 | .deref, | ||
| 3776 | .array_access, | ||
| 3777 | .error_value, | ||
| 3778 | .while_simple, // This variant cannot have an else expression. | ||
| 3779 | .while_cont, // This variant cannot have an else expression. | ||
| 3780 | .for_simple, // This variant cannot have an else expression. | ||
| 3781 | .if_simple, // This variant cannot have an else expression. | ||
| 3273 | => return false, | 3782 | => return false, |
| 3274 | 3783 | ||
| 3275 | // Forward the question to a sub-expression. | 3784 | // Forward the question to the LHS sub-expression. |
| 3276 | .GroupedExpression => node = node.castTag(.GroupedExpression).?.expr, | 3785 | .grouped_expression, |
| 3277 | .Try => node = node.castTag(.Try).?.rhs, | 3786 | .@"try", |
| 3278 | .Await => node = node.castTag(.Await).?.rhs, | 3787 | .@"await", |
| 3279 | .Catch => node = node.castTag(.Catch).?.rhs, | 3788 | .@"comptime", |
| 3280 | .OrElse => node = node.castTag(.OrElse).?.rhs, | 3789 | .@"nosuspend", |
| 3281 | .Comptime => node = node.castTag(.Comptime).?.expr, | 3790 | .unwrap_optional, |
| 3282 | .Nosuspend => node = node.castTag(.Nosuspend).?.expr, | 3791 | => node = node_datas[node].lhs, |
| 3283 | .UnwrapOptional => node = node.castTag(.UnwrapOptional).?.lhs, | 3792 | |
| 3793 | // Forward the question to the RHS sub-expression. | ||
| 3794 | .@"catch", | ||
| 3795 | .@"orelse", | ||
| 3796 | => node = node_datas[node].rhs, | ||
| 3284 | 3797 | ||
| 3285 | // True because these are exactly the expressions we need memory locations for. | 3798 | // True because these are exactly the expressions we need memory locations for. |
| 3286 | .ArrayInitializer, | 3799 | .array_init_one, |
| 3287 | .ArrayInitializerDot, | 3800 | .array_init_one_comma, |
| 3288 | .StructInitializer, | 3801 | .array_init_dot_two, |
| 3289 | .StructInitializerDot, | 3802 | .array_init_dot_two_comma, |
| 3803 | .array_init_dot, | ||
| 3804 | .array_init_dot_comma, | ||
| 3805 | .array_init, | ||
| 3806 | .array_init_comma, | ||
| 3807 | .struct_init_one, | ||
| 3808 | .struct_init_one_comma, | ||
| 3809 | .struct_init_dot_two, | ||
| 3810 | .struct_init_dot_two_comma, | ||
| 3811 | .struct_init_dot, | ||
| 3812 | .struct_init_dot_comma, | ||
| 3813 | .struct_init, | ||
| 3814 | .struct_init_comma, | ||
| 3290 | => return true, | 3815 | => return true, |
| 3291 | 3816 | ||
| 3292 | // True because depending on comptime conditions, sub-expressions | 3817 | // True because depending on comptime conditions, sub-expressions |
| 3293 | // may be the kind that need memory locations. | 3818 | // may be the kind that need memory locations. |
| 3294 | .While, | 3819 | .@"while", // This variant always has an else expression. |
| 3295 | .For, | 3820 | .@"if", // This variant always has an else expression. |
| 3296 | .Switch, | 3821 | .@"for", // This variant always has an else expression. |
| 3297 | .Call, | 3822 | .@"switch", |
| 3298 | .LabeledBlock, | 3823 | .switch_comma, |
| 3824 | .call_one, | ||
| 3825 | .call_one_comma, | ||
| 3826 | .async_call_one, | ||
| 3827 | .async_call_one_comma, | ||
| 3828 | .call, | ||
| 3829 | .call_comma, | ||
| 3830 | .async_call, | ||
| 3831 | .async_call_comma, | ||
| 3299 | => return true, | 3832 | => return true, |
| 3300 | 3833 | ||
| 3301 | .BuiltinCall => { | 3834 | .block_two, |
| 3302 | @setEvalBranchQuota(5000); | 3835 | .block_two_semicolon, |
| 3303 | const builtin_needs_mem_loc = std.ComptimeStringMap(bool, .{ | 3836 | .block, |
| 3304 | .{ "@addWithOverflow", false }, | 3837 | .block_semicolon, |
| 3305 | .{ "@alignCast", false }, | 3838 | => { |
| 3306 | .{ "@alignOf", false }, | 3839 | const lbrace = main_tokens[node]; |
| 3307 | .{ "@as", true }, | 3840 | if (token_tags[lbrace - 1] == .colon) { |
| 3308 | .{ "@asyncCall", false }, | 3841 | // Labeled blocks may need a memory location to forward |
| 3309 | .{ "@atomicLoad", false }, | 3842 | // to their break statements. |
| 3310 | .{ "@atomicRmw", false }, | 3843 | return true; |
| 3311 | .{ "@atomicStore", false }, | 3844 | } else { |
| 3312 | .{ "@bitCast", true }, | 3845 | return false; |
| 3313 | .{ "@bitOffsetOf", false }, | 3846 | } |
| 3314 | .{ "@boolToInt", false }, | ||
| 3315 | .{ "@bitSizeOf", false }, | ||
| 3316 | .{ "@breakpoint", false }, | ||
| 3317 | .{ "@mulAdd", false }, | ||
| 3318 | .{ "@byteSwap", false }, | ||
| 3319 | .{ "@bitReverse", false }, | ||
| 3320 | .{ "@byteOffsetOf", false }, | ||
| 3321 | .{ "@call", true }, | ||
| 3322 | .{ "@cDefine", false }, | ||
| 3323 | .{ "@cImport", false }, | ||
| 3324 | .{ "@cInclude", false }, | ||
| 3325 | .{ "@clz", false }, | ||
| 3326 | .{ "@cmpxchgStrong", false }, | ||
| 3327 | .{ "@cmpxchgWeak", false }, | ||
| 3328 | .{ "@compileError", false }, | ||
| 3329 | .{ "@compileLog", false }, | ||
| 3330 | .{ "@ctz", false }, | ||
| 3331 | .{ "@cUndef", false }, | ||
| 3332 | .{ "@divExact", false }, | ||
| 3333 | .{ "@divFloor", false }, | ||
| 3334 | .{ "@divTrunc", false }, | ||
| 3335 | .{ "@embedFile", false }, | ||
| 3336 | .{ "@enumToInt", false }, | ||
| 3337 | .{ "@errorName", false }, | ||
| 3338 | .{ "@errorReturnTrace", false }, | ||
| 3339 | .{ "@errorToInt", false }, | ||
| 3340 | .{ "@errSetCast", false }, | ||
| 3341 | .{ "@export", false }, | ||
| 3342 | .{ "@fence", false }, | ||
| 3343 | .{ "@field", true }, | ||
| 3344 | .{ "@fieldParentPtr", false }, | ||
| 3345 | .{ "@floatCast", false }, | ||
| 3346 | .{ "@floatToInt", false }, | ||
| 3347 | .{ "@frame", false }, | ||
| 3348 | .{ "@Frame", false }, | ||
| 3349 | .{ "@frameAddress", false }, | ||
| 3350 | .{ "@frameSize", false }, | ||
| 3351 | .{ "@hasDecl", false }, | ||
| 3352 | .{ "@hasField", false }, | ||
| 3353 | .{ "@import", false }, | ||
| 3354 | .{ "@intCast", false }, | ||
| 3355 | .{ "@intToEnum", false }, | ||
| 3356 | .{ "@intToError", false }, | ||
| 3357 | .{ "@intToFloat", false }, | ||
| 3358 | .{ "@intToPtr", false }, | ||
| 3359 | .{ "@memcpy", false }, | ||
| 3360 | .{ "@memset", false }, | ||
| 3361 | .{ "@wasmMemorySize", false }, | ||
| 3362 | .{ "@wasmMemoryGrow", false }, | ||
| 3363 | .{ "@mod", false }, | ||
| 3364 | .{ "@mulWithOverflow", false }, | ||
| 3365 | .{ "@panic", false }, | ||
| 3366 | .{ "@popCount", false }, | ||
| 3367 | .{ "@ptrCast", false }, | ||
| 3368 | .{ "@ptrToInt", false }, | ||
| 3369 | .{ "@rem", false }, | ||
| 3370 | .{ "@returnAddress", false }, | ||
| 3371 | .{ "@setAlignStack", false }, | ||
| 3372 | .{ "@setCold", false }, | ||
| 3373 | .{ "@setEvalBranchQuota", false }, | ||
| 3374 | .{ "@setFloatMode", false }, | ||
| 3375 | .{ "@setRuntimeSafety", false }, | ||
| 3376 | .{ "@shlExact", false }, | ||
| 3377 | .{ "@shlWithOverflow", false }, | ||
| 3378 | .{ "@shrExact", false }, | ||
| 3379 | .{ "@shuffle", false }, | ||
| 3380 | .{ "@sizeOf", false }, | ||
| 3381 | .{ "@splat", true }, | ||
| 3382 | .{ "@reduce", false }, | ||
| 3383 | .{ "@src", true }, | ||
| 3384 | .{ "@sqrt", false }, | ||
| 3385 | .{ "@sin", false }, | ||
| 3386 | .{ "@cos", false }, | ||
| 3387 | .{ "@exp", false }, | ||
| 3388 | .{ "@exp2", false }, | ||
| 3389 | .{ "@log", false }, | ||
| 3390 | .{ "@log2", false }, | ||
| 3391 | .{ "@log10", false }, | ||
| 3392 | .{ "@fabs", false }, | ||
| 3393 | .{ "@floor", false }, | ||
| 3394 | .{ "@ceil", false }, | ||
| 3395 | .{ "@trunc", false }, | ||
| 3396 | .{ "@round", false }, | ||
| 3397 | .{ "@subWithOverflow", false }, | ||
| 3398 | .{ "@tagName", false }, | ||
| 3399 | .{ "@This", false }, | ||
| 3400 | .{ "@truncate", false }, | ||
| 3401 | .{ "@Type", false }, | ||
| 3402 | .{ "@typeInfo", false }, | ||
| 3403 | .{ "@typeName", false }, | ||
| 3404 | .{ "@TypeOf", false }, | ||
| 3405 | .{ "@unionInit", true }, | ||
| 3406 | }); | ||
| 3407 | const name = scope.tree().tokenSlice(node.castTag(.BuiltinCall).?.builtin_token); | ||
| 3408 | return builtin_needs_mem_loc.get(name).?; | ||
| 3409 | }, | 3847 | }, |
| 3410 | 3848 | ||
| 3411 | // Depending on AST properties, they may need memory locations. | 3849 | .builtin_call, |
| 3412 | .If => return node.castTag(.If).?.@"else" != null, | 3850 | .builtin_call_comma, |
| 3851 | .builtin_call_two, | ||
| 3852 | .builtin_call_two_comma, | ||
| 3853 | => { | ||
| 3854 | const builtin_token = main_tokens[node]; | ||
| 3855 | const builtin_name = tree.tokenSlice(builtin_token); | ||
| 3856 | // If the builtin is an invalid name, we don't cause an error here; instead | ||
| 3857 | // let it pass, and the error will be "invalid builtin function" later. | ||
| 3858 | const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false; | ||
| 3859 | return builtin_info.needs_mem_loc; | ||
| 3860 | }, | ||
| 3413 | } | 3861 | } |
| 3414 | } | 3862 | } |
| 3415 | } | 3863 | } |
| ... | @@ -3450,8 +3898,18 @@ fn rvalue(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr | ... | @@ -3450,8 +3898,18 @@ fn rvalue(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr |
| 3450 | } | 3898 | } |
| 3451 | } | 3899 | } |
| 3452 | 3900 | ||
| 3453 | fn rvalueVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, result: void) InnerError!*zir.Inst { | 3901 | /// TODO when reworking ZIR memory layout, make the void value correspond to a hard coded |
| 3454 | const src = scope.tree().token_locs[node.firstToken()].start; | 3902 | /// index; that way this does not actually need to allocate anything. |
| 3903 | fn rvalueVoid( | ||
| 3904 | mod: *Module, | ||
| 3905 | scope: *Scope, | ||
| 3906 | rl: ResultLoc, | ||
| 3907 | node: ast.Node.Index, | ||
| 3908 | result: void, | ||
| 3909 | ) InnerError!*zir.Inst { | ||
| 3910 | const tree = scope.tree(); | ||
| 3911 | const main_tokens = tree.nodes.items(.main_token); | ||
| 3912 | const src = tree.tokens.items(.start)[tree.firstToken(node)]; | ||
| 3455 | const void_inst = try addZIRInstConst(mod, scope, src, .{ | 3913 | const void_inst = try addZIRInstConst(mod, scope, src, .{ |
| 3456 | .ty = Type.initTag(.void), | 3914 | .ty = Type.initTag(.void), |
| 3457 | .val = Value.initTag(.void_value), | 3915 | .val = Value.initTag(.void_value), |
| ... | @@ -3547,6 +4005,29 @@ pub fn addZirInstTag( | ... | @@ -3547,6 +4005,29 @@ pub fn addZirInstTag( |
| 3547 | return &inst.base; | 4005 | return &inst.base; |
| 3548 | } | 4006 | } |
| 3549 | 4007 | ||
| 4008 | pub fn addZirInstT( | ||
| 4009 | mod: *Module, | ||
| 4010 | scope: *Scope, | ||
| 4011 | src: usize, | ||
| 4012 | comptime T: type, | ||
| 4013 | tag: zir.Inst.Tag, | ||
| 4014 | positionals: std.meta.fieldInfo(T, .positionals).field_type, | ||
| 4015 | ) !*T { | ||
| 4016 | const gen_zir = scope.getGenZIR(); | ||
| 4017 | try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1); | ||
| 4018 | const inst = try gen_zir.arena.create(T); | ||
| 4019 | inst.* = .{ | ||
| 4020 | .base = .{ | ||
| 4021 | .tag = tag, | ||
| 4022 | .src = src, | ||
| 4023 | }, | ||
| 4024 | .positionals = positionals, | ||
| 4025 | .kw_args = .{}, | ||
| 4026 | }; | ||
| 4027 | gen_zir.instructions.appendAssumeCapacity(&inst.base); | ||
| 4028 | return inst; | ||
| 4029 | } | ||
| 4030 | |||
| 3550 | pub fn addZIRInstSpecial( | 4031 | pub fn addZIRInstSpecial( |
| 3551 | mod: *Module, | 4032 | mod: *Module, |
| 3552 | scope: *Scope, | 4033 | scope: *Scope, |
src/clang.zig+7-6| ... | @@ -127,6 +127,9 @@ pub const APSInt = opaque { | ... | @@ -127,6 +127,9 @@ pub const APSInt = opaque { |
| 127 | 127 | ||
| 128 | pub const getNumWords = ZigClangAPSInt_getNumWords; | 128 | pub const getNumWords = ZigClangAPSInt_getNumWords; |
| 129 | extern fn ZigClangAPSInt_getNumWords(*const APSInt) c_uint; | 129 | extern fn ZigClangAPSInt_getNumWords(*const APSInt) c_uint; |
| 130 | |||
| 131 | pub const lessThanEqual = ZigClangAPSInt_lessThanEqual; | ||
| 132 | extern fn ZigClangAPSInt_lessThanEqual(*const APSInt, rhs: u64) bool; | ||
| 130 | }; | 133 | }; |
| 131 | 134 | ||
| 132 | pub const ASTContext = opaque { | 135 | pub const ASTContext = opaque { |
| ... | @@ -270,12 +273,12 @@ pub const CompoundAssignOperator = opaque { | ... | @@ -270,12 +273,12 @@ pub const CompoundAssignOperator = opaque { |
| 270 | 273 | ||
| 271 | pub const CompoundStmt = opaque { | 274 | pub const CompoundStmt = opaque { |
| 272 | pub const body_begin = ZigClangCompoundStmt_body_begin; | 275 | pub const body_begin = ZigClangCompoundStmt_body_begin; |
| 273 | extern fn ZigClangCompoundStmt_body_begin(*const CompoundStmt) const_body_iterator; | 276 | extern fn ZigClangCompoundStmt_body_begin(*const CompoundStmt) ConstBodyIterator; |
| 274 | 277 | ||
| 275 | pub const body_end = ZigClangCompoundStmt_body_end; | 278 | pub const body_end = ZigClangCompoundStmt_body_end; |
| 276 | extern fn ZigClangCompoundStmt_body_end(*const CompoundStmt) const_body_iterator; | 279 | extern fn ZigClangCompoundStmt_body_end(*const CompoundStmt) ConstBodyIterator; |
| 277 | 280 | ||
| 278 | pub const const_body_iterator = [*]const *Stmt; | 281 | pub const ConstBodyIterator = [*]const *Stmt; |
| 279 | }; | 282 | }; |
| 280 | 283 | ||
| 281 | pub const ConditionalOperator = opaque {}; | 284 | pub const ConditionalOperator = opaque {}; |
| ... | @@ -407,7 +410,7 @@ pub const Expr = opaque { | ... | @@ -407,7 +410,7 @@ pub const Expr = opaque { |
| 407 | pub const getBeginLoc = ZigClangExpr_getBeginLoc; | 410 | pub const getBeginLoc = ZigClangExpr_getBeginLoc; |
| 408 | extern fn ZigClangExpr_getBeginLoc(*const Expr) SourceLocation; | 411 | extern fn ZigClangExpr_getBeginLoc(*const Expr) SourceLocation; |
| 409 | 412 | ||
| 410 | pub const EvaluateAsConstantExpr = ZigClangExpr_EvaluateAsConstantExpr; | 413 | pub const evaluateAsConstantExpr = ZigClangExpr_EvaluateAsConstantExpr; |
| 411 | extern fn ZigClangExpr_EvaluateAsConstantExpr(*const Expr, *ExprEvalResult, Expr_ConstExprUsage, *const ASTContext) bool; | 414 | extern fn ZigClangExpr_EvaluateAsConstantExpr(*const Expr, *ExprEvalResult, Expr_ConstExprUsage, *const ASTContext) bool; |
| 412 | }; | 415 | }; |
| 413 | 416 | ||
| ... | @@ -694,8 +697,6 @@ pub const ReturnStmt = opaque { | ... | @@ -694,8 +697,6 @@ pub const ReturnStmt = opaque { |
| 694 | extern fn ZigClangReturnStmt_getRetValue(*const ReturnStmt) ?*const Expr; | 697 | extern fn ZigClangReturnStmt_getRetValue(*const ReturnStmt) ?*const Expr; |
| 695 | }; | 698 | }; |
| 696 | 699 | ||
| 697 | pub const SkipFunctionBodiesScope = opaque {}; | ||
| 698 | |||
| 699 | pub const SourceManager = opaque { | 700 | pub const SourceManager = opaque { |
| 700 | pub const getSpellingLoc = ZigClangSourceManager_getSpellingLoc; | 701 | pub const getSpellingLoc = ZigClangSourceManager_getSpellingLoc; |
| 701 | extern fn ZigClangSourceManager_getSpellingLoc(*const SourceManager, Loc: SourceLocation) SourceLocation; | 702 | extern fn ZigClangSourceManager_getSpellingLoc(*const SourceManager, Loc: SourceLocation) SourceLocation; |
src/codegen.zig+10-5| ... | @@ -451,11 +451,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -451,11 +451,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 451 | 451 | ||
| 452 | const src_data: struct { lbrace_src: usize, rbrace_src: usize, source: []const u8 } = blk: { | 452 | const src_data: struct { lbrace_src: usize, rbrace_src: usize, source: []const u8 } = blk: { |
| 453 | const container_scope = module_fn.owner_decl.container; | 453 | const container_scope = module_fn.owner_decl.container; |
| 454 | const tree = container_scope.file_scope.contents.tree; | 454 | const tree = container_scope.file_scope.tree; |
| 455 | const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?; | 455 | const node_tags = tree.nodes.items(.tag); |
| 456 | const block = fn_proto.getBodyNode().?.castTag(.Block).?; | 456 | const node_datas = tree.nodes.items(.data); |
| 457 | const lbrace_src = tree.token_locs[block.lbrace].start; | 457 | const token_starts = tree.tokens.items(.start); |
| 458 | const rbrace_src = tree.token_locs[block.rbrace].start; | 458 | |
| 459 | const fn_decl = tree.rootDecls()[module_fn.owner_decl.src_index]; | ||
| 460 | assert(node_tags[fn_decl] == .fn_decl); | ||
| 461 | const block = node_datas[fn_decl].rhs; | ||
| 462 | const lbrace_src = token_starts[tree.firstToken(block)]; | ||
| 463 | const rbrace_src = token_starts[tree.lastToken(block)]; | ||
| 459 | break :blk .{ | 464 | break :blk .{ |
| 460 | .lbrace_src = lbrace_src, | 465 | .lbrace_src = lbrace_src, |
| 461 | .rbrace_src = rbrace_src, | 466 | .rbrace_src = rbrace_src, |
src/codegen/c.zig+56-2| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const assert = std.debug.assert; | ||
| 2 | const mem = std.mem; | 3 | const mem = std.mem; |
| 3 | const log = std.log.scoped(.c); | 4 | const log = std.log.scoped(.c); |
| 4 | 5 | ||
| ... | @@ -42,7 +43,7 @@ pub const Object = struct { | ... | @@ -42,7 +43,7 @@ pub const Object = struct { |
| 42 | next_arg_index: usize = 0, | 43 | next_arg_index: usize = 0, |
| 43 | next_local_index: usize = 0, | 44 | next_local_index: usize = 0, |
| 44 | next_block_index: usize = 0, | 45 | next_block_index: usize = 0, |
| 45 | indent_writer: std.io.AutoIndentingStream(std.ArrayList(u8).Writer), | 46 | indent_writer: IndentWriter(std.ArrayList(u8).Writer), |
| 46 | 47 | ||
| 47 | fn resolveInst(o: *Object, inst: *Inst) !CValue { | 48 | fn resolveInst(o: *Object, inst: *Inst) !CValue { |
| 48 | if (inst.value()) |_| { | 49 | if (inst.value()) |_| { |
| ... | @@ -63,7 +64,7 @@ pub const Object = struct { | ... | @@ -63,7 +64,7 @@ pub const Object = struct { |
| 63 | return local_value; | 64 | return local_value; |
| 64 | } | 65 | } |
| 65 | 66 | ||
| 66 | fn writer(o: *Object) std.io.AutoIndentingStream(std.ArrayList(u8).Writer).Writer { | 67 | fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer { |
| 67 | return o.indent_writer.writer(); | 68 | return o.indent_writer.writer(); |
| 68 | } | 69 | } |
| 69 | 70 | ||
| ... | @@ -796,3 +797,56 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { | ... | @@ -796,3 +797,56 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { |
| 796 | 797 | ||
| 797 | return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{}); | 798 | return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{}); |
| 798 | } | 799 | } |
| 800 | |||
| 801 | fn IndentWriter(comptime UnderlyingWriter: type) type { | ||
| 802 | return struct { | ||
| 803 | const Self = @This(); | ||
| 804 | pub const Error = UnderlyingWriter.Error; | ||
| 805 | pub const Writer = std.io.Writer(*Self, Error, write); | ||
| 806 | |||
| 807 | pub const indent_delta = 4; | ||
| 808 | |||
| 809 | underlying_writer: UnderlyingWriter, | ||
| 810 | indent_count: usize = 0, | ||
| 811 | current_line_empty: bool = true, | ||
| 812 | |||
| 813 | pub fn writer(self: *Self) Writer { | ||
| 814 | return .{ .context = self }; | ||
| 815 | } | ||
| 816 | |||
| 817 | pub fn write(self: *Self, bytes: []const u8) Error!usize { | ||
| 818 | if (bytes.len == 0) return @as(usize, 0); | ||
| 819 | |||
| 820 | const current_indent = self.indent_count * Self.indent_delta; | ||
| 821 | if (self.current_line_empty and current_indent > 0) { | ||
| 822 | try self.underlying_writer.writeByteNTimes(' ', current_indent); | ||
| 823 | } | ||
| 824 | self.current_line_empty = false; | ||
| 825 | |||
| 826 | return self.writeNoIndent(bytes); | ||
| 827 | } | ||
| 828 | |||
| 829 | pub fn insertNewline(self: *Self) Error!void { | ||
| 830 | _ = try self.writeNoIndent("\n"); | ||
| 831 | } | ||
| 832 | |||
| 833 | pub fn pushIndent(self: *Self) void { | ||
| 834 | self.indent_count += 1; | ||
| 835 | } | ||
| 836 | |||
| 837 | pub fn popIndent(self: *Self) void { | ||
| 838 | assert(self.indent_count != 0); | ||
| 839 | self.indent_count -= 1; | ||
| 840 | } | ||
| 841 | |||
| 842 | fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize { | ||
| 843 | if (bytes.len == 0) return @as(usize, 0); | ||
| 844 | |||
| 845 | try self.underlying_writer.writeAll(bytes); | ||
| 846 | if (bytes[bytes.len - 1] == '\n') { | ||
| 847 | self.current_line_empty = true; | ||
| 848 | } | ||
| 849 | return bytes.len; | ||
| 850 | } | ||
| 851 | }; | ||
| 852 | } |
src/ir.zig+1| ... | @@ -317,6 +317,7 @@ pub const Inst = struct { | ... | @@ -317,6 +317,7 @@ pub const Inst = struct { |
| 317 | pub const base_tag = Tag.arg; | 317 | pub const base_tag = Tag.arg; |
| 318 | 318 | ||
| 319 | base: Inst, | 319 | base: Inst, |
| 320 | /// This exists to be emitted into debug info. | ||
| 320 | name: [*:0]const u8, | 321 | name: [*:0]const u8, |
| 321 | 322 | ||
| 322 | pub fn operandCount(self: *const Arg) usize { | 323 | pub fn operandCount(self: *const Arg) usize { |
src/link.zig+2-2| ... | @@ -550,11 +550,11 @@ pub const File = struct { | ... | @@ -550,11 +550,11 @@ pub const File = struct { |
| 550 | id_symlink_basename, | 550 | id_symlink_basename, |
| 551 | &prev_digest_buf, | 551 | &prev_digest_buf, |
| 552 | ) catch |err| b: { | 552 | ) catch |err| b: { |
| 553 | log.debug("archive new_digest={} readFile error: {s}", .{ digest, @errorName(err) }); | 553 | log.debug("archive new_digest={x} readFile error: {s}", .{ digest, @errorName(err) }); |
| 554 | break :b prev_digest_buf[0..0]; | 554 | break :b prev_digest_buf[0..0]; |
| 555 | }; | 555 | }; |
| 556 | if (mem.eql(u8, prev_digest, &digest)) { | 556 | if (mem.eql(u8, prev_digest, &digest)) { |
| 557 | log.debug("archive digest={} match - skipping invocation", .{digest}); | 557 | log.debug("archive digest={x} match - skipping invocation", .{digest}); |
| 558 | base.lock = man.toOwnedLock(); | 558 | base.lock = man.toOwnedLock(); |
| 559 | return; | 559 | return; |
| 560 | } | 560 | } |
src/link/C.zig+1-1| ... | @@ -97,7 +97,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { | ... | @@ -97,7 +97,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { |
| 97 | .value_map = codegen.CValueMap.init(module.gpa), | 97 | .value_map = codegen.CValueMap.init(module.gpa), |
| 98 | .indent_writer = undefined, // set later so we can get a pointer to object.code | 98 | .indent_writer = undefined, // set later so we can get a pointer to object.code |
| 99 | }; | 99 | }; |
| 100 | object.indent_writer = std.io.autoIndentingStream(4, object.code.writer()); | 100 | object.indent_writer = .{ .underlying_writer = object.code.writer() }; |
| 101 | defer object.value_map.deinit(); | 101 | defer object.value_map.deinit(); |
| 102 | defer object.code.deinit(); | 102 | defer object.code.deinit(); |
| 103 | defer object.dg.fwd_decl.deinit(); | 103 | defer object.dg.fwd_decl.deinit(); |
src/link/Coff.zig+3-3| ... | @@ -892,17 +892,17 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { | ... | @@ -892,17 +892,17 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 892 | id_symlink_basename, | 892 | id_symlink_basename, |
| 893 | &prev_digest_buf, | 893 | &prev_digest_buf, |
| 894 | ) catch |err| blk: { | 894 | ) catch |err| blk: { |
| 895 | log.debug("COFF LLD new_digest={} error: {s}", .{ digest, @errorName(err) }); | 895 | log.debug("COFF LLD new_digest={x} error: {s}", .{ digest, @errorName(err) }); |
| 896 | // Handle this as a cache miss. | 896 | // Handle this as a cache miss. |
| 897 | break :blk prev_digest_buf[0..0]; | 897 | break :blk prev_digest_buf[0..0]; |
| 898 | }; | 898 | }; |
| 899 | if (mem.eql(u8, prev_digest, &digest)) { | 899 | if (mem.eql(u8, prev_digest, &digest)) { |
| 900 | log.debug("COFF LLD digest={} match - skipping invocation", .{digest}); | 900 | log.debug("COFF LLD digest={x} match - skipping invocation", .{digest}); |
| 901 | // Hot diggity dog! The output binary is already there. | 901 | // Hot diggity dog! The output binary is already there. |
| 902 | self.base.lock = man.toOwnedLock(); | 902 | self.base.lock = man.toOwnedLock(); |
| 903 | return; | 903 | return; |
| 904 | } | 904 | } |
| 905 | log.debug("COFF LLD prev_digest={} new_digest={}", .{ prev_digest, digest }); | 905 | log.debug("COFF LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest }); |
| 906 | 906 | ||
| 907 | // We are about to change the output file to be different, so we invalidate the build hash now. | 907 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 908 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { | 908 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { |
src/link/Elf.zig+27-15| ... | @@ -1365,17 +1365,17 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1365,17 +1365,17 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1365 | id_symlink_basename, | 1365 | id_symlink_basename, |
| 1366 | &prev_digest_buf, | 1366 | &prev_digest_buf, |
| 1367 | ) catch |err| blk: { | 1367 | ) catch |err| blk: { |
| 1368 | log.debug("ELF LLD new_digest={} error: {s}", .{ digest, @errorName(err) }); | 1368 | log.debug("ELF LLD new_digest={x} error: {s}", .{ digest, @errorName(err) }); |
| 1369 | // Handle this as a cache miss. | 1369 | // Handle this as a cache miss. |
| 1370 | break :blk prev_digest_buf[0..0]; | 1370 | break :blk prev_digest_buf[0..0]; |
| 1371 | }; | 1371 | }; |
| 1372 | if (mem.eql(u8, prev_digest, &digest)) { | 1372 | if (mem.eql(u8, prev_digest, &digest)) { |
| 1373 | log.debug("ELF LLD digest={} match - skipping invocation", .{digest}); | 1373 | log.debug("ELF LLD digest={x} match - skipping invocation", .{digest}); |
| 1374 | // Hot diggity dog! The output binary is already there. | 1374 | // Hot diggity dog! The output binary is already there. |
| 1375 | self.base.lock = man.toOwnedLock(); | 1375 | self.base.lock = man.toOwnedLock(); |
| 1376 | return; | 1376 | return; |
| 1377 | } | 1377 | } |
| 1378 | log.debug("ELF LLD prev_digest={} new_digest={}", .{ prev_digest, digest }); | 1378 | log.debug("ELF LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest }); |
| 1379 | 1379 | ||
| 1380 | // We are about to change the output file to be different, so we invalidate the build hash now. | 1380 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 1381 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { | 1381 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { |
| ... | @@ -2223,13 +2223,19 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { | ... | @@ -2223,13 +2223,19 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { |
| 2223 | try dbg_line_buffer.ensureCapacity(26); | 2223 | try dbg_line_buffer.ensureCapacity(26); |
| 2224 | 2224 | ||
| 2225 | const line_off: u28 = blk: { | 2225 | const line_off: u28 = blk: { |
| 2226 | const tree = decl.container.file_scope.contents.tree; | 2226 | const tree = decl.container.file_scope.tree; |
| 2227 | const file_ast_decls = tree.root_node.decls(); | 2227 | const node_tags = tree.nodes.items(.tag); |
| 2228 | const node_datas = tree.nodes.items(.data); | ||
| 2229 | const token_starts = tree.tokens.items(.start); | ||
| 2230 | |||
| 2231 | const file_ast_decls = tree.rootDecls(); | ||
| 2228 | // TODO Look into improving the performance here by adding a token-index-to-line | 2232 | // TODO Look into improving the performance here by adding a token-index-to-line |
| 2229 | // lookup table. Currently this involves scanning over the source code for newlines. | 2233 | // lookup table. Currently this involves scanning over the source code for newlines. |
| 2230 | const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?; | 2234 | const fn_decl = file_ast_decls[decl.src_index]; |
| 2231 | const block = fn_proto.getBodyNode().?.castTag(.Block).?; | 2235 | assert(node_tags[fn_decl] == .fn_decl); |
| 2232 | const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start); | 2236 | const block = node_datas[fn_decl].rhs; |
| 2237 | const lbrace = tree.firstToken(block); | ||
| 2238 | const line_delta = std.zig.lineDelta(tree.source, 0, token_starts[lbrace]); | ||
| 2233 | break :blk @intCast(u28, line_delta); | 2239 | break :blk @intCast(u28, line_delta); |
| 2234 | }; | 2240 | }; |
| 2235 | 2241 | ||
| ... | @@ -2744,13 +2750,19 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec | ... | @@ -2744,13 +2750,19 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec |
| 2744 | 2750 | ||
| 2745 | if (self.llvm_ir_module) |_| return; | 2751 | if (self.llvm_ir_module) |_| return; |
| 2746 | 2752 | ||
| 2747 | const tree = decl.container.file_scope.contents.tree; | 2753 | const tree = decl.container.file_scope.tree; |
| 2748 | const file_ast_decls = tree.root_node.decls(); | 2754 | const node_tags = tree.nodes.items(.tag); |
| 2755 | const node_datas = tree.nodes.items(.data); | ||
| 2756 | const token_starts = tree.tokens.items(.start); | ||
| 2757 | |||
| 2758 | const file_ast_decls = tree.rootDecls(); | ||
| 2749 | // TODO Look into improving the performance here by adding a token-index-to-line | 2759 | // TODO Look into improving the performance here by adding a token-index-to-line |
| 2750 | // lookup table. Currently this involves scanning over the source code for newlines. | 2760 | // lookup table. Currently this involves scanning over the source code for newlines. |
| 2751 | const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?; | 2761 | const fn_decl = file_ast_decls[decl.src_index]; |
| 2752 | const block = fn_proto.getBodyNode().?.castTag(.Block).?; | 2762 | assert(node_tags[fn_decl] == .fn_decl); |
| 2753 | const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start); | 2763 | const block = node_datas[fn_decl].rhs; |
| 2764 | const lbrace = tree.firstToken(block); | ||
| 2765 | const line_delta = std.zig.lineDelta(tree.source, 0, token_starts[lbrace]); | ||
| 2754 | const casted_line_off = @intCast(u28, line_delta); | 2766 | const casted_line_off = @intCast(u28, line_delta); |
| 2755 | 2767 | ||
| 2756 | const shdr = &self.sections.items[self.debug_line_section_index.?]; | 2768 | const shdr = &self.sections.items[self.debug_line_section_index.?]; |
| ... | @@ -3025,7 +3037,7 @@ const min_nop_size = 2; | ... | @@ -3025,7 +3037,7 @@ const min_nop_size = 2; |
| 3025 | 3037 | ||
| 3026 | /// Writes to the file a buffer, prefixed and suffixed by the specified number of | 3038 | /// Writes to the file a buffer, prefixed and suffixed by the specified number of |
| 3027 | /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes | 3039 | /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes |
| 3028 | /// are less than 126,976 bytes (if this limit is ever reached, this function can be | 3040 | /// are less than 1044480 bytes (if this limit is ever reached, this function can be |
| 3029 | /// improved to make more than one pwritev call, or the limit can be raised by a fixed | 3041 | /// improved to make more than one pwritev call, or the limit can be raised by a fixed |
| 3030 | /// amount by increasing the length of `vecs`). | 3042 | /// amount by increasing the length of `vecs`). |
| 3031 | fn pwriteDbgLineNops( | 3043 | fn pwriteDbgLineNops( |
| ... | @@ -3040,7 +3052,7 @@ fn pwriteDbgLineNops( | ... | @@ -3040,7 +3052,7 @@ fn pwriteDbgLineNops( |
| 3040 | 3052 | ||
| 3041 | const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096; | 3053 | const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096; |
| 3042 | const three_byte_nop = [3]u8{ DW.LNS_advance_pc, 0b1000_0000, 0 }; | 3054 | const three_byte_nop = [3]u8{ DW.LNS_advance_pc, 0b1000_0000, 0 }; |
| 3043 | var vecs: [32]std.os.iovec_const = undefined; | 3055 | var vecs: [256]std.os.iovec_const = undefined; |
| 3044 | var vec_index: usize = 0; | 3056 | var vec_index: usize = 0; |
| 3045 | { | 3057 | { |
| 3046 | var padding_left = prev_padding_size; | 3058 | var padding_left = prev_padding_size; |
src/link/MachO.zig+3-3| ... | @@ -556,17 +556,17 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -556,17 +556,17 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 556 | id_symlink_basename, | 556 | id_symlink_basename, |
| 557 | &prev_digest_buf, | 557 | &prev_digest_buf, |
| 558 | ) catch |err| blk: { | 558 | ) catch |err| blk: { |
| 559 | log.debug("MachO LLD new_digest={} error: {s}", .{ digest, @errorName(err) }); | 559 | log.debug("MachO LLD new_digest={x} error: {s}", .{ digest, @errorName(err) }); |
| 560 | // Handle this as a cache miss. | 560 | // Handle this as a cache miss. |
| 561 | break :blk prev_digest_buf[0..0]; | 561 | break :blk prev_digest_buf[0..0]; |
| 562 | }; | 562 | }; |
| 563 | if (mem.eql(u8, prev_digest, &digest)) { | 563 | if (mem.eql(u8, prev_digest, &digest)) { |
| 564 | log.debug("MachO LLD digest={} match - skipping invocation", .{digest}); | 564 | log.debug("MachO LLD digest={x} match - skipping invocation", .{digest}); |
| 565 | // Hot diggity dog! The output binary is already there. | 565 | // Hot diggity dog! The output binary is already there. |
| 566 | self.base.lock = man.toOwnedLock(); | 566 | self.base.lock = man.toOwnedLock(); |
| 567 | return; | 567 | return; |
| 568 | } | 568 | } |
| 569 | log.debug("MachO LLD prev_digest={} new_digest={}", .{ prev_digest, digest }); | 569 | log.debug("MachO LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest }); |
| 570 | 570 | ||
| 571 | // We are about to change the output file to be different, so we invalidate the build hash now. | 571 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 572 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { | 572 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { |
src/link/MachO/DebugSymbols.zig+22-10| ... | @@ -904,13 +904,19 @@ pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const M | ... | @@ -904,13 +904,19 @@ pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const M |
| 904 | const tracy = trace(@src()); | 904 | const tracy = trace(@src()); |
| 905 | defer tracy.end(); | 905 | defer tracy.end(); |
| 906 | 906 | ||
| 907 | const tree = decl.container.file_scope.contents.tree; | 907 | const tree = decl.container.file_scope.tree; |
| 908 | const file_ast_decls = tree.root_node.decls(); | 908 | const node_tags = tree.nodes.items(.tag); |
| 909 | const node_datas = tree.nodes.items(.data); | ||
| 910 | const token_starts = tree.tokens.items(.start); | ||
| 911 | |||
| 912 | const file_ast_decls = tree.rootDecls(); | ||
| 909 | // TODO Look into improving the performance here by adding a token-index-to-line | 913 | // TODO Look into improving the performance here by adding a token-index-to-line |
| 910 | // lookup table. Currently this involves scanning over the source code for newlines. | 914 | // lookup table. Currently this involves scanning over the source code for newlines. |
| 911 | const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?; | 915 | const fn_decl = file_ast_decls[decl.src_index]; |
| 912 | const block = fn_proto.getBodyNode().?.castTag(.Block).?; | 916 | assert(node_tags[fn_decl] == .fn_decl); |
| 913 | const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start); | 917 | const block = node_datas[fn_decl].rhs; |
| 918 | const lbrace = tree.firstToken(block); | ||
| 919 | const line_delta = std.zig.lineDelta(tree.source, 0, token_starts[lbrace]); | ||
| 914 | const casted_line_off = @intCast(u28, line_delta); | 920 | const casted_line_off = @intCast(u28, line_delta); |
| 915 | 921 | ||
| 916 | const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment; | 922 | const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment; |
| ... | @@ -948,13 +954,19 @@ pub fn initDeclDebugBuffers( | ... | @@ -948,13 +954,19 @@ pub fn initDeclDebugBuffers( |
| 948 | try dbg_line_buffer.ensureCapacity(26); | 954 | try dbg_line_buffer.ensureCapacity(26); |
| 949 | 955 | ||
| 950 | const line_off: u28 = blk: { | 956 | const line_off: u28 = blk: { |
| 951 | const tree = decl.container.file_scope.contents.tree; | 957 | const tree = decl.container.file_scope.tree; |
| 952 | const file_ast_decls = tree.root_node.decls(); | 958 | const node_tags = tree.nodes.items(.tag); |
| 959 | const node_datas = tree.nodes.items(.data); | ||
| 960 | const token_starts = tree.tokens.items(.start); | ||
| 961 | |||
| 962 | const file_ast_decls = tree.rootDecls(); | ||
| 953 | // TODO Look into improving the performance here by adding a token-index-to-line | 963 | // TODO Look into improving the performance here by adding a token-index-to-line |
| 954 | // lookup table. Currently this involves scanning over the source code for newlines. | 964 | // lookup table. Currently this involves scanning over the source code for newlines. |
| 955 | const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?; | 965 | const fn_decl = file_ast_decls[decl.src_index]; |
| 956 | const block = fn_proto.getBodyNode().?.castTag(.Block).?; | 966 | assert(node_tags[fn_decl] == .fn_decl); |
| 957 | const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start); | 967 | const block = node_datas[fn_decl].rhs; |
| 968 | const lbrace = tree.firstToken(block); | ||
| 969 | const line_delta = std.zig.lineDelta(tree.source, 0, token_starts[lbrace]); | ||
| 958 | break :blk @intCast(u28, line_delta); | 970 | break :blk @intCast(u28, line_delta); |
| 959 | }; | 971 | }; |
| 960 | 972 |
src/link/Wasm.zig+3-3| ... | @@ -391,17 +391,17 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { | ... | @@ -391,17 +391,17 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 391 | id_symlink_basename, | 391 | id_symlink_basename, |
| 392 | &prev_digest_buf, | 392 | &prev_digest_buf, |
| 393 | ) catch |err| blk: { | 393 | ) catch |err| blk: { |
| 394 | log.debug("WASM LLD new_digest={} error: {s}", .{ digest, @errorName(err) }); | 394 | log.debug("WASM LLD new_digest={x} error: {s}", .{ digest, @errorName(err) }); |
| 395 | // Handle this as a cache miss. | 395 | // Handle this as a cache miss. |
| 396 | break :blk prev_digest_buf[0..0]; | 396 | break :blk prev_digest_buf[0..0]; |
| 397 | }; | 397 | }; |
| 398 | if (mem.eql(u8, prev_digest, &digest)) { | 398 | if (mem.eql(u8, prev_digest, &digest)) { |
| 399 | log.debug("WASM LLD digest={} match - skipping invocation", .{digest}); | 399 | log.debug("WASM LLD digest={x} match - skipping invocation", .{digest}); |
| 400 | // Hot diggity dog! The output binary is already there. | 400 | // Hot diggity dog! The output binary is already there. |
| 401 | self.base.lock = man.toOwnedLock(); | 401 | self.base.lock = man.toOwnedLock(); |
| 402 | return; | 402 | return; |
| 403 | } | 403 | } |
| 404 | log.debug("WASM LLD prev_digest={} new_digest={}", .{ prev_digest, digest }); | 404 | log.debug("WASM LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest }); |
| 405 | 405 | ||
| 406 | // We are about to change the output file to be different, so we invalidate the build hash now. | 406 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 407 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { | 407 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { |
src/main.zig+44-41| ... | @@ -2158,7 +2158,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi | ... | @@ -2158,7 +2158,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi |
| 2158 | const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"}); | 2158 | const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"}); |
| 2159 | const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path); | 2159 | const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path); |
| 2160 | var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{}; | 2160 | var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{}; |
| 2161 | const tree = translate_c.translate( | 2161 | var tree = translate_c.translate( |
| 2162 | comp.gpa, | 2162 | comp.gpa, |
| 2163 | new_argv.ptr, | 2163 | new_argv.ptr, |
| 2164 | new_argv.ptr + new_argv.len, | 2164 | new_argv.ptr + new_argv.len, |
| ... | @@ -2179,7 +2179,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi | ... | @@ -2179,7 +2179,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi |
| 2179 | process.exit(1); | 2179 | process.exit(1); |
| 2180 | }, | 2180 | }, |
| 2181 | }; | 2181 | }; |
| 2182 | defer tree.deinit(); | 2182 | defer tree.deinit(comp.gpa); |
| 2183 | 2183 | ||
| 2184 | if (out_dep_path) |dep_file_path| { | 2184 | if (out_dep_path) |dep_file_path| { |
| 2185 | const dep_basename = std.fs.path.basename(dep_file_path); | 2185 | const dep_basename = std.fs.path.basename(dep_file_path); |
| ... | @@ -2193,16 +2193,21 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi | ... | @@ -2193,16 +2193,21 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi |
| 2193 | 2193 | ||
| 2194 | const digest = man.final(); | 2194 | const digest = man.final(); |
| 2195 | const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest }); | 2195 | const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest }); |
| 2196 | |||
| 2196 | var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{}); | 2197 | var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{}); |
| 2197 | defer o_dir.close(); | 2198 | defer o_dir.close(); |
| 2199 | |||
| 2198 | var zig_file = try o_dir.createFile(translated_zig_basename, .{}); | 2200 | var zig_file = try o_dir.createFile(translated_zig_basename, .{}); |
| 2199 | defer zig_file.close(); | 2201 | defer zig_file.close(); |
| 2200 | 2202 | ||
| 2201 | var bw = io.bufferedWriter(zig_file.writer()); | 2203 | const formatted = try tree.render(comp.gpa); |
| 2202 | _ = try std.zig.render(comp.gpa, bw.writer(), tree); | 2204 | defer comp.gpa.free(formatted); |
| 2203 | try bw.flush(); | ||
| 2204 | 2205 | ||
| 2205 | man.writeManifest() catch |err| warn("failed to write cache manifest: {s}", .{@errorName(err)}); | 2206 | try zig_file.writeAll(formatted); |
| 2207 | |||
| 2208 | man.writeManifest() catch |err| warn("failed to write cache manifest: {s}", .{ | ||
| 2209 | @errorName(err), | ||
| 2210 | }); | ||
| 2206 | 2211 | ||
| 2207 | break :digest digest; | 2212 | break :digest digest; |
| 2208 | }; | 2213 | }; |
| ... | @@ -2689,10 +2694,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { | ... | @@ -2689,10 +2694,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { |
| 2689 | const source_code = try stdin.readAllAlloc(gpa, max_src_size); | 2694 | const source_code = try stdin.readAllAlloc(gpa, max_src_size); |
| 2690 | defer gpa.free(source_code); | 2695 | defer gpa.free(source_code); |
| 2691 | 2696 | ||
| 2692 | const tree = std.zig.parse(gpa, source_code) catch |err| { | 2697 | var tree = std.zig.parse(gpa, source_code) catch |err| { |
| 2693 | fatal("error parsing stdin: {s}", .{err}); | 2698 | fatal("error parsing stdin: {s}", .{err}); |
| 2694 | }; | 2699 | }; |
| 2695 | defer tree.deinit(); | 2700 | defer tree.deinit(gpa); |
| 2696 | 2701 | ||
| 2697 | for (tree.errors) |parse_error| { | 2702 | for (tree.errors) |parse_error| { |
| 2698 | try printErrMsgToFile(gpa, parse_error, tree, "<stdin>", stderr_file, color); | 2703 | try printErrMsgToFile(gpa, parse_error, tree, "<stdin>", stderr_file, color); |
| ... | @@ -2700,16 +2705,15 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { | ... | @@ -2700,16 +2705,15 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { |
| 2700 | if (tree.errors.len != 0) { | 2705 | if (tree.errors.len != 0) { |
| 2701 | process.exit(1); | 2706 | process.exit(1); |
| 2702 | } | 2707 | } |
| 2708 | const formatted = try tree.render(gpa); | ||
| 2709 | defer gpa.free(formatted); | ||
| 2710 | |||
| 2703 | if (check_flag) { | 2711 | if (check_flag) { |
| 2704 | const anything_changed = try std.zig.render(gpa, io.null_writer, tree); | 2712 | const code: u8 = @boolToInt(mem.eql(u8, formatted, source_code)); |
| 2705 | const code = if (anything_changed) @as(u8, 1) else @as(u8, 0); | ||
| 2706 | process.exit(code); | 2713 | process.exit(code); |
| 2707 | } | 2714 | } |
| 2708 | 2715 | ||
| 2709 | var bw = io.bufferedWriter(io.getStdOut().writer()); | 2716 | return io.getStdOut().writeAll(formatted); |
| 2710 | _ = try std.zig.render(gpa, bw.writer(), tree); | ||
| 2711 | try bw.flush(); | ||
| 2712 | return; | ||
| 2713 | } | 2717 | } |
| 2714 | 2718 | ||
| 2715 | if (input_files.items.len == 0) { | 2719 | if (input_files.items.len == 0) { |
| ... | @@ -2846,8 +2850,8 @@ fn fmtPathFile( | ... | @@ -2846,8 +2850,8 @@ fn fmtPathFile( |
| 2846 | // Add to set after no longer possible to get error.IsDir. | 2850 | // Add to set after no longer possible to get error.IsDir. |
| 2847 | if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; | 2851 | if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; |
| 2848 | 2852 | ||
| 2849 | const tree = try std.zig.parse(fmt.gpa, source_code); | 2853 | var tree = try std.zig.parse(fmt.gpa, source_code); |
| 2850 | defer tree.deinit(); | 2854 | defer tree.deinit(fmt.gpa); |
| 2851 | 2855 | ||
| 2852 | for (tree.errors) |parse_error| { | 2856 | for (tree.errors) |parse_error| { |
| 2853 | try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color); | 2857 | try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color); |
| ... | @@ -2857,22 +2861,19 @@ fn fmtPathFile( | ... | @@ -2857,22 +2861,19 @@ fn fmtPathFile( |
| 2857 | return; | 2861 | return; |
| 2858 | } | 2862 | } |
| 2859 | 2863 | ||
| 2864 | // As a heuristic, we make enough capacity for the same as the input source. | ||
| 2865 | fmt.out_buffer.shrinkRetainingCapacity(0); | ||
| 2866 | try fmt.out_buffer.ensureCapacity(source_code.len); | ||
| 2867 | |||
| 2868 | try tree.renderToArrayList(&fmt.out_buffer); | ||
| 2869 | if (mem.eql(u8, fmt.out_buffer.items, source_code)) | ||
| 2870 | return; | ||
| 2871 | |||
| 2860 | if (check_mode) { | 2872 | if (check_mode) { |
| 2861 | const anything_changed = try std.zig.render(fmt.gpa, io.null_writer, tree); | 2873 | const stdout = io.getStdOut().writer(); |
| 2862 | if (anything_changed) { | 2874 | try stdout.print("{s}\n", .{file_path}); |
| 2863 | const stdout = io.getStdOut().writer(); | 2875 | fmt.any_error = true; |
| 2864 | try stdout.print("{s}\n", .{file_path}); | ||
| 2865 | fmt.any_error = true; | ||
| 2866 | } | ||
| 2867 | } else { | 2876 | } else { |
| 2868 | // As a heuristic, we make enough capacity for the same as the input source. | ||
| 2869 | try fmt.out_buffer.ensureCapacity(source_code.len); | ||
| 2870 | fmt.out_buffer.items.len = 0; | ||
| 2871 | const writer = fmt.out_buffer.writer(); | ||
| 2872 | const anything_changed = try std.zig.render(fmt.gpa, writer, tree); | ||
| 2873 | if (!anything_changed) | ||
| 2874 | return; // Good thing we didn't waste any file system access on this. | ||
| 2875 | |||
| 2876 | var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode }); | 2877 | var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode }); |
| 2877 | defer af.deinit(); | 2878 | defer af.deinit(); |
| 2878 | 2879 | ||
| ... | @@ -2886,7 +2887,7 @@ fn fmtPathFile( | ... | @@ -2886,7 +2887,7 @@ fn fmtPathFile( |
| 2886 | fn printErrMsgToFile( | 2887 | fn printErrMsgToFile( |
| 2887 | gpa: *mem.Allocator, | 2888 | gpa: *mem.Allocator, |
| 2888 | parse_error: ast.Error, | 2889 | parse_error: ast.Error, |
| 2889 | tree: *ast.Tree, | 2890 | tree: ast.Tree, |
| 2890 | path: []const u8, | 2891 | path: []const u8, |
| 2891 | file: fs.File, | 2892 | file: fs.File, |
| 2892 | color: Color, | 2893 | color: Color, |
| ... | @@ -2896,19 +2897,17 @@ fn printErrMsgToFile( | ... | @@ -2896,19 +2897,17 @@ fn printErrMsgToFile( |
| 2896 | .on => true, | 2897 | .on => true, |
| 2897 | .off => false, | 2898 | .off => false, |
| 2898 | }; | 2899 | }; |
| 2899 | const lok_token = parse_error.loc(); | 2900 | const lok_token = parse_error.token; |
| 2900 | const span_first = lok_token; | ||
| 2901 | const span_last = lok_token; | ||
| 2902 | 2901 | ||
| 2903 | const first_token = tree.token_locs[span_first]; | 2902 | const token_starts = tree.tokens.items(.start); |
| 2904 | const last_token = tree.token_locs[span_last]; | 2903 | const token_tags = tree.tokens.items(.tag); |
| 2905 | const start_loc = tree.tokenLocationLoc(0, first_token); | 2904 | const first_token_start = token_starts[lok_token]; |
| 2906 | const end_loc = tree.tokenLocationLoc(first_token.end, last_token); | 2905 | const start_loc = tree.tokenLocation(0, lok_token); |
| 2907 | 2906 | ||
| 2908 | var text_buf = std.ArrayList(u8).init(gpa); | 2907 | var text_buf = std.ArrayList(u8).init(gpa); |
| 2909 | defer text_buf.deinit(); | 2908 | defer text_buf.deinit(); |
| 2910 | const writer = text_buf.writer(); | 2909 | const writer = text_buf.writer(); |
| 2911 | try parse_error.render(tree.token_ids, writer); | 2910 | try tree.renderError(parse_error, writer); |
| 2912 | const text = text_buf.items; | 2911 | const text = text_buf.items; |
| 2913 | 2912 | ||
| 2914 | const stream = file.writer(); | 2913 | const stream = file.writer(); |
| ... | @@ -2925,8 +2924,12 @@ fn printErrMsgToFile( | ... | @@ -2925,8 +2924,12 @@ fn printErrMsgToFile( |
| 2925 | } | 2924 | } |
| 2926 | try stream.writeByte('\n'); | 2925 | try stream.writeByte('\n'); |
| 2927 | try stream.writeByteNTimes(' ', start_loc.column); | 2926 | try stream.writeByteNTimes(' ', start_loc.column); |
| 2928 | try stream.writeByteNTimes('~', last_token.end - first_token.start); | 2927 | if (token_tags[lok_token].lexeme()) |lexeme| { |
| 2929 | try stream.writeByte('\n'); | 2928 | try stream.writeByteNTimes('~', lexeme.len); |
| 2929 | try stream.writeByte('\n'); | ||
| 2930 | } else { | ||
| 2931 | try stream.writeAll("^\n"); | ||
| 2932 | } | ||
| 2930 | } | 2933 | } |
| 2931 | 2934 | ||
| 2932 | pub const info_zen = | 2935 | pub const info_zen = |
src/test.zig+13-11| ... | @@ -155,7 +155,7 @@ pub const TestContext = struct { | ... | @@ -155,7 +155,7 @@ pub const TestContext = struct { |
| 155 | self.updates.append(.{ | 155 | self.updates.append(.{ |
| 156 | .src = src, | 156 | .src = src, |
| 157 | .case = .{ .Header = result }, | 157 | .case = .{ .Header = result }, |
| 158 | }) catch unreachable; | 158 | }) catch @panic("out of memory"); |
| 159 | } | 159 | } |
| 160 | 160 | ||
| 161 | /// Adds a subcase in which the module is updated with `src`, compiled, | 161 | /// Adds a subcase in which the module is updated with `src`, compiled, |
| ... | @@ -164,7 +164,7 @@ pub const TestContext = struct { | ... | @@ -164,7 +164,7 @@ pub const TestContext = struct { |
| 164 | self.updates.append(.{ | 164 | self.updates.append(.{ |
| 165 | .src = src, | 165 | .src = src, |
| 166 | .case = .{ .Execution = result }, | 166 | .case = .{ .Execution = result }, |
| 167 | }) catch unreachable; | 167 | }) catch @panic("out of memory"); |
| 168 | } | 168 | } |
| 169 | 169 | ||
| 170 | /// Adds a subcase in which the module is updated with `src`, compiled, | 170 | /// Adds a subcase in which the module is updated with `src`, compiled, |
| ... | @@ -173,7 +173,7 @@ pub const TestContext = struct { | ... | @@ -173,7 +173,7 @@ pub const TestContext = struct { |
| 173 | self.updates.append(.{ | 173 | self.updates.append(.{ |
| 174 | .src = src, | 174 | .src = src, |
| 175 | .case = .{ .CompareObjectFile = result }, | 175 | .case = .{ .CompareObjectFile = result }, |
| 176 | }) catch unreachable; | 176 | }) catch @panic("out of memory"); |
| 177 | } | 177 | } |
| 178 | 178 | ||
| 179 | /// Adds a subcase in which the module is updated with `src`, which | 179 | /// Adds a subcase in which the module is updated with `src`, which |
| ... | @@ -181,7 +181,7 @@ pub const TestContext = struct { | ... | @@ -181,7 +181,7 @@ pub const TestContext = struct { |
| 181 | /// for the expected reasons, given in sequential order in `errors` in | 181 | /// for the expected reasons, given in sequential order in `errors` in |
| 182 | /// the form `:line:column: error: message`. | 182 | /// the form `:line:column: error: message`. |
| 183 | pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void { | 183 | pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void { |
| 184 | var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable; | 184 | var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch @panic("out of memory"); |
| 185 | for (errors) |err_msg_line, i| { | 185 | for (errors) |err_msg_line, i| { |
| 186 | if (std.mem.startsWith(u8, err_msg_line, "error: ")) { | 186 | if (std.mem.startsWith(u8, err_msg_line, "error: ")) { |
| 187 | array[i] = .{ | 187 | array[i] = .{ |
| ... | @@ -224,7 +224,7 @@ pub const TestContext = struct { | ... | @@ -224,7 +224,7 @@ pub const TestContext = struct { |
| 224 | }, | 224 | }, |
| 225 | }; | 225 | }; |
| 226 | } | 226 | } |
| 227 | self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch unreachable; | 227 | self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch @panic("out of memory"); |
| 228 | } | 228 | } |
| 229 | 229 | ||
| 230 | /// Adds a subcase in which the module is updated with `src`, and | 230 | /// Adds a subcase in which the module is updated with `src`, and |
| ... | @@ -247,7 +247,7 @@ pub const TestContext = struct { | ... | @@ -247,7 +247,7 @@ pub const TestContext = struct { |
| 247 | .output_mode = .Exe, | 247 | .output_mode = .Exe, |
| 248 | .extension = extension, | 248 | .extension = extension, |
| 249 | .files = std.ArrayList(File).init(ctx.cases.allocator), | 249 | .files = std.ArrayList(File).init(ctx.cases.allocator), |
| 250 | }) catch unreachable; | 250 | }) catch @panic("out of memory"); |
| 251 | return &ctx.cases.items[ctx.cases.items.len - 1]; | 251 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 252 | } | 252 | } |
| 253 | 253 | ||
| ... | @@ -262,15 +262,17 @@ pub const TestContext = struct { | ... | @@ -262,15 +262,17 @@ pub const TestContext = struct { |
| 262 | } | 262 | } |
| 263 | 263 | ||
| 264 | pub fn exeFromCompiledC(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { | 264 | pub fn exeFromCompiledC(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { |
| 265 | const prefixed_name = std.fmt.allocPrint(ctx.cases.allocator, "CBE: {s}", .{name}) catch | ||
| 266 | @panic("out of memory"); | ||
| 265 | ctx.cases.append(Case{ | 267 | ctx.cases.append(Case{ |
| 266 | .name = name, | 268 | .name = prefixed_name, |
| 267 | .target = target, | 269 | .target = target, |
| 268 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | 270 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 269 | .output_mode = .Exe, | 271 | .output_mode = .Exe, |
| 270 | .extension = .Zig, | 272 | .extension = .Zig, |
| 271 | .object_format = .c, | 273 | .object_format = .c, |
| 272 | .files = std.ArrayList(File).init(ctx.cases.allocator), | 274 | .files = std.ArrayList(File).init(ctx.cases.allocator), |
| 273 | }) catch unreachable; | 275 | }) catch @panic("out of memory"); |
| 274 | return &ctx.cases.items[ctx.cases.items.len - 1]; | 276 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 275 | } | 277 | } |
| 276 | 278 | ||
| ... | @@ -285,7 +287,7 @@ pub const TestContext = struct { | ... | @@ -285,7 +287,7 @@ pub const TestContext = struct { |
| 285 | .extension = .Zig, | 287 | .extension = .Zig, |
| 286 | .files = std.ArrayList(File).init(ctx.cases.allocator), | 288 | .files = std.ArrayList(File).init(ctx.cases.allocator), |
| 287 | .llvm_backend = true, | 289 | .llvm_backend = true, |
| 288 | }) catch unreachable; | 290 | }) catch @panic("out of memory"); |
| 289 | return &ctx.cases.items[ctx.cases.items.len - 1]; | 291 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 290 | } | 292 | } |
| 291 | 293 | ||
| ... | @@ -302,7 +304,7 @@ pub const TestContext = struct { | ... | @@ -302,7 +304,7 @@ pub const TestContext = struct { |
| 302 | .output_mode = .Obj, | 304 | .output_mode = .Obj, |
| 303 | .extension = extension, | 305 | .extension = extension, |
| 304 | .files = std.ArrayList(File).init(ctx.cases.allocator), | 306 | .files = std.ArrayList(File).init(ctx.cases.allocator), |
| 305 | }) catch unreachable; | 307 | }) catch @panic("out of memory"); |
| 306 | return &ctx.cases.items[ctx.cases.items.len - 1]; | 308 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 307 | } | 309 | } |
| 308 | 310 | ||
| ... | @@ -326,7 +328,7 @@ pub const TestContext = struct { | ... | @@ -326,7 +328,7 @@ pub const TestContext = struct { |
| 326 | .extension = ext, | 328 | .extension = ext, |
| 327 | .object_format = .c, | 329 | .object_format = .c, |
| 328 | .files = std.ArrayList(File).init(ctx.cases.allocator), | 330 | .files = std.ArrayList(File).init(ctx.cases.allocator), |
| 329 | }) catch unreachable; | 331 | }) catch @panic("out of memory"); |
| 330 | return &ctx.cases.items[ctx.cases.items.len - 1]; | 332 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 331 | } | 333 | } |
| 332 | 334 |
src/translate_c.zig+1790-3687| ... | @@ -3,23 +3,24 @@ | ... | @@ -3,23 +3,24 @@ |
| 3 | 3 | ||
| 4 | const std = @import("std"); | 4 | const std = @import("std"); |
| 5 | const assert = std.debug.assert; | 5 | const assert = std.debug.assert; |
| 6 | const ast = std.zig.ast; | ||
| 7 | const Token = std.zig.Token; | ||
| 8 | const clang = @import("clang.zig"); | 6 | const clang = @import("clang.zig"); |
| 9 | const ctok = std.c.tokenizer; | 7 | const ctok = std.c.tokenizer; |
| 10 | const CToken = std.c.Token; | 8 | const CToken = std.c.Token; |
| 11 | const mem = std.mem; | 9 | const mem = std.mem; |
| 12 | const math = std.math; | 10 | const math = std.math; |
| 11 | const ast = @import("translate_c/ast.zig"); | ||
| 12 | const Node = ast.Node; | ||
| 13 | const Tag = Node.Tag; | ||
| 13 | 14 | ||
| 14 | const CallingConvention = std.builtin.CallingConvention; | 15 | const CallingConvention = std.builtin.CallingConvention; |
| 15 | 16 | ||
| 16 | pub const ClangErrMsg = clang.Stage2ErrorMsg; | 17 | pub const ClangErrMsg = clang.Stage2ErrorMsg; |
| 17 | 18 | ||
| 18 | pub const Error = error{OutOfMemory}; | 19 | pub const Error = std.mem.Allocator.Error; |
| 19 | const TypeError = Error || error{UnsupportedType}; | 20 | const TypeError = Error || error{UnsupportedType}; |
| 20 | const TransError = TypeError || error{UnsupportedTranslation}; | 21 | const TransError = TypeError || error{UnsupportedTranslation}; |
| 21 | 22 | ||
| 22 | const SymbolTable = std.StringArrayHashMap(*ast.Node); | 23 | const SymbolTable = std.StringArrayHashMap(Node); |
| 23 | const AliasList = std.ArrayList(struct { | 24 | const AliasList = std.ArrayList(struct { |
| 24 | alias: []const u8, | 25 | alias: []const u8, |
| 25 | name: []const u8, | 26 | name: []const u8, |
| ... | @@ -30,23 +31,11 @@ const Scope = struct { | ... | @@ -30,23 +31,11 @@ const Scope = struct { |
| 30 | parent: ?*Scope, | 31 | parent: ?*Scope, |
| 31 | 32 | ||
| 32 | const Id = enum { | 33 | const Id = enum { |
| 33 | Switch, | 34 | block, |
| 34 | Block, | 35 | root, |
| 35 | Root, | 36 | condition, |
| 36 | Condition, | 37 | loop, |
| 37 | Loop, | 38 | do_loop, |
| 38 | }; | ||
| 39 | |||
| 40 | /// Represents an in-progress ast.Node.Switch. This struct is stack-allocated. | ||
| 41 | /// When it is deinitialized, it produces an ast.Node.Switch which is allocated | ||
| 42 | /// into the main arena. | ||
| 43 | const Switch = struct { | ||
| 44 | base: Scope, | ||
| 45 | pending_block: Block, | ||
| 46 | cases: []*ast.Node, | ||
| 47 | case_index: usize, | ||
| 48 | switch_label: ?[]const u8, | ||
| 49 | default_label: ?[]const u8, | ||
| 50 | }; | 39 | }; |
| 51 | 40 | ||
| 52 | /// Used for the scope of condition expressions, for example `if (cond)`. | 41 | /// Used for the scope of condition expressions, for example `if (cond)`. |
| ... | @@ -67,16 +56,15 @@ const Scope = struct { | ... | @@ -67,16 +56,15 @@ const Scope = struct { |
| 67 | } | 56 | } |
| 68 | }; | 57 | }; |
| 69 | 58 | ||
| 70 | /// Represents an in-progress ast.Node.Block. This struct is stack-allocated. | 59 | /// Represents an in-progress Node.Block. This struct is stack-allocated. |
| 71 | /// When it is deinitialized, it produces an ast.Node.Block which is allocated | 60 | /// When it is deinitialized, it produces an Node.Block which is allocated |
| 72 | /// into the main arena. | 61 | /// into the main arena. |
| 73 | const Block = struct { | 62 | const Block = struct { |
| 74 | base: Scope, | 63 | base: Scope, |
| 75 | statements: std.ArrayList(*ast.Node), | 64 | statements: std.ArrayList(Node), |
| 76 | variables: AliasList, | 65 | variables: AliasList, |
| 77 | label: ?ast.TokenIndex, | ||
| 78 | mangle_count: u32 = 0, | 66 | mangle_count: u32 = 0, |
| 79 | lbrace: ast.TokenIndex, | 67 | label: ?[]const u8 = null, |
| 80 | 68 | ||
| 81 | /// When the block corresponds to a function, keep track of the return type | 69 | /// When the block corresponds to a function, keep track of the return type |
| 82 | /// so that the return expression can be cast, if necessary | 70 | /// so that the return expression can be cast, if necessary |
| ... | @@ -85,17 +73,14 @@ const Scope = struct { | ... | @@ -85,17 +73,14 @@ const Scope = struct { |
| 85 | fn init(c: *Context, parent: *Scope, labeled: bool) !Block { | 73 | fn init(c: *Context, parent: *Scope, labeled: bool) !Block { |
| 86 | var blk = Block{ | 74 | var blk = Block{ |
| 87 | .base = .{ | 75 | .base = .{ |
| 88 | .id = .Block, | 76 | .id = .block, |
| 89 | .parent = parent, | 77 | .parent = parent, |
| 90 | }, | 78 | }, |
| 91 | .statements = std.ArrayList(*ast.Node).init(c.gpa), | 79 | .statements = std.ArrayList(Node).init(c.gpa), |
| 92 | .variables = AliasList.init(c.gpa), | 80 | .variables = AliasList.init(c.gpa), |
| 93 | .label = null, | ||
| 94 | .lbrace = try appendToken(c, .LBrace, "{"), | ||
| 95 | }; | 81 | }; |
| 96 | if (labeled) { | 82 | if (labeled) { |
| 97 | blk.label = try appendIdentifier(c, try blk.makeMangledName(c, "blk")); | 83 | blk.label = try blk.makeMangledName(c, "blk"); |
| 98 | _ = try appendToken(c, .Colon, ":"); | ||
| 99 | } | 84 | } |
| 100 | return blk; | 85 | return blk; |
| 101 | } | 86 | } |
| ... | @@ -106,31 +91,24 @@ const Scope = struct { | ... | @@ -106,31 +91,24 @@ const Scope = struct { |
| 106 | self.* = undefined; | 91 | self.* = undefined; |
| 107 | } | 92 | } |
| 108 | 93 | ||
| 109 | fn complete(self: *Block, c: *Context) !*ast.Node { | 94 | fn complete(self: *Block, c: *Context) !Node { |
| 110 | // We reserve 1 extra statement if the parent is a Loop. This is in case of | 95 | if (self.base.parent.?.id == .do_loop) { |
| 111 | // do while, we want to put `if (cond) break;` at the end. | 96 | // We reserve 1 extra statement if the parent is a do_loop. This is in case of |
| 112 | const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .Loop); | 97 | // do while, we want to put `if (cond) break;` at the end. |
| 113 | const rbrace = try appendToken(c, .RBrace, "}"); | 98 | const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .do_loop); |
| 114 | if (self.label) |label| { | 99 | var stmts = try c.arena.alloc(Node, alloc_len); |
| 115 | const node = try ast.Node.LabeledBlock.alloc(c.arena, alloc_len); | 100 | stmts.len = self.statements.items.len; |
| 116 | node.* = .{ | 101 | mem.copy(Node, stmts, self.statements.items); |
| 117 | .statements_len = self.statements.items.len, | 102 | return Tag.block.create(c.arena, .{ |
| 118 | .lbrace = self.lbrace, | 103 | .label = self.label, |
| 119 | .rbrace = rbrace, | 104 | .stmts = stmts, |
| 120 | .label = label, | 105 | }); |
| 121 | }; | ||
| 122 | mem.copy(*ast.Node, node.statements(), self.statements.items); | ||
| 123 | return &node.base; | ||
| 124 | } else { | ||
| 125 | const node = try ast.Node.Block.alloc(c.arena, alloc_len); | ||
| 126 | node.* = .{ | ||
| 127 | .statements_len = self.statements.items.len, | ||
| 128 | .lbrace = self.lbrace, | ||
| 129 | .rbrace = rbrace, | ||
| 130 | }; | ||
| 131 | mem.copy(*ast.Node, node.statements(), self.statements.items); | ||
| 132 | return &node.base; | ||
| 133 | } | 106 | } |
| 107 | if (self.statements.items.len == 0) return Tag.empty_block.init(); | ||
| 108 | return Tag.block.create(c.arena, .{ | ||
| 109 | .label = self.label, | ||
| 110 | .stmts = try c.arena.dupe(Node, self.statements.items), | ||
| 111 | }); | ||
| 134 | } | 112 | } |
| 135 | 113 | ||
| 136 | /// Given the desired name, return a name that does not shadow anything from outer scopes. | 114 | /// Given the desired name, return a name that does not shadow anything from outer scopes. |
| ... | @@ -174,19 +152,27 @@ const Scope = struct { | ... | @@ -174,19 +152,27 @@ const Scope = struct { |
| 174 | sym_table: SymbolTable, | 152 | sym_table: SymbolTable, |
| 175 | macro_table: SymbolTable, | 153 | macro_table: SymbolTable, |
| 176 | context: *Context, | 154 | context: *Context, |
| 155 | nodes: std.ArrayList(Node), | ||
| 177 | 156 | ||
| 178 | fn init(c: *Context) Root { | 157 | fn init(c: *Context) Root { |
| 179 | return .{ | 158 | return .{ |
| 180 | .base = .{ | 159 | .base = .{ |
| 181 | .id = .Root, | 160 | .id = .root, |
| 182 | .parent = null, | 161 | .parent = null, |
| 183 | }, | 162 | }, |
| 184 | .sym_table = SymbolTable.init(c.arena), | 163 | .sym_table = SymbolTable.init(c.gpa), |
| 185 | .macro_table = SymbolTable.init(c.arena), | 164 | .macro_table = SymbolTable.init(c.gpa), |
| 186 | .context = c, | 165 | .context = c, |
| 166 | .nodes = std.ArrayList(Node).init(c.gpa), | ||
| 187 | }; | 167 | }; |
| 188 | } | 168 | } |
| 189 | 169 | ||
| 170 | fn deinit(scope: *Root) void { | ||
| 171 | scope.sym_table.deinit(); | ||
| 172 | scope.macro_table.deinit(); | ||
| 173 | scope.nodes.deinit(); | ||
| 174 | } | ||
| 175 | |||
| 190 | /// Check if the global scope contains this name, without looking into the "future", e.g. | 176 | /// Check if the global scope contains this name, without looking into the "future", e.g. |
| 191 | /// ignore the preprocessed decl and macro names. | 177 | /// ignore the preprocessed decl and macro names. |
| 192 | fn containsNow(scope: *Root, name: []const u8) bool { | 178 | fn containsNow(scope: *Root, name: []const u8) bool { |
| ... | @@ -205,20 +191,20 @@ const Scope = struct { | ... | @@ -205,20 +191,20 @@ const Scope = struct { |
| 205 | var scope = inner; | 191 | var scope = inner; |
| 206 | while (true) { | 192 | while (true) { |
| 207 | switch (scope.id) { | 193 | switch (scope.id) { |
| 208 | .Root => unreachable, | 194 | .root => unreachable, |
| 209 | .Block => return @fieldParentPtr(Block, "base", scope), | 195 | .block => return @fieldParentPtr(Block, "base", scope), |
| 210 | .Condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c), | 196 | .condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c), |
| 211 | else => scope = scope.parent.?, | 197 | else => scope = scope.parent.?, |
| 212 | } | 198 | } |
| 213 | } | 199 | } |
| 214 | } | 200 | } |
| 215 | 201 | ||
| 216 | fn findBlockReturnType(inner: *Scope, c: *Context) ?clang.QualType { | 202 | fn findBlockReturnType(inner: *Scope, c: *Context) clang.QualType { |
| 217 | var scope = inner; | 203 | var scope = inner; |
| 218 | while (true) { | 204 | while (true) { |
| 219 | switch (scope.id) { | 205 | switch (scope.id) { |
| 220 | .Root => return null, | 206 | .root => unreachable, |
| 221 | .Block => { | 207 | .block => { |
| 222 | const block = @fieldParentPtr(Block, "base", scope); | 208 | const block = @fieldParentPtr(Block, "base", scope); |
| 223 | if (block.return_type) |qt| return qt; | 209 | if (block.return_type) |qt| return qt; |
| 224 | scope = scope.parent.?; | 210 | scope = scope.parent.?; |
| ... | @@ -230,17 +216,17 @@ const Scope = struct { | ... | @@ -230,17 +216,17 @@ const Scope = struct { |
| 230 | 216 | ||
| 231 | fn getAlias(scope: *Scope, name: []const u8) []const u8 { | 217 | fn getAlias(scope: *Scope, name: []const u8) []const u8 { |
| 232 | return switch (scope.id) { | 218 | return switch (scope.id) { |
| 233 | .Root => return name, | 219 | .root => return name, |
| 234 | .Block => @fieldParentPtr(Block, "base", scope).getAlias(name), | 220 | .block => @fieldParentPtr(Block, "base", scope).getAlias(name), |
| 235 | .Switch, .Loop, .Condition => scope.parent.?.getAlias(name), | 221 | .loop, .do_loop, .condition => scope.parent.?.getAlias(name), |
| 236 | }; | 222 | }; |
| 237 | } | 223 | } |
| 238 | 224 | ||
| 239 | fn contains(scope: *Scope, name: []const u8) bool { | 225 | fn contains(scope: *Scope, name: []const u8) bool { |
| 240 | return switch (scope.id) { | 226 | return switch (scope.id) { |
| 241 | .Root => @fieldParentPtr(Root, "base", scope).contains(name), | 227 | .root => @fieldParentPtr(Root, "base", scope).contains(name), |
| 242 | .Block => @fieldParentPtr(Block, "base", scope).contains(name), | 228 | .block => @fieldParentPtr(Block, "base", scope).contains(name), |
| 243 | .Switch, .Loop, .Condition => scope.parent.?.contains(name), | 229 | .loop, .do_loop, .condition => scope.parent.?.contains(name), |
| 244 | }; | 230 | }; |
| 245 | } | 231 | } |
| 246 | 232 | ||
| ... | @@ -248,20 +234,26 @@ const Scope = struct { | ... | @@ -248,20 +234,26 @@ const Scope = struct { |
| 248 | var scope = inner; | 234 | var scope = inner; |
| 249 | while (true) { | 235 | while (true) { |
| 250 | switch (scope.id) { | 236 | switch (scope.id) { |
| 251 | .Root => unreachable, | 237 | .root => unreachable, |
| 252 | .Switch => return scope, | 238 | .loop, .do_loop => return scope, |
| 253 | .Loop => return scope, | ||
| 254 | else => scope = scope.parent.?, | 239 | else => scope = scope.parent.?, |
| 255 | } | 240 | } |
| 256 | } | 241 | } |
| 257 | } | 242 | } |
| 258 | 243 | ||
| 259 | fn getSwitch(inner: *Scope) *Scope.Switch { | 244 | /// Appends a node to the first block scope if inside a function, or to the root tree if not. |
| 245 | fn appendNode(inner: *Scope, node: Node) !void { | ||
| 260 | var scope = inner; | 246 | var scope = inner; |
| 261 | while (true) { | 247 | while (true) { |
| 262 | switch (scope.id) { | 248 | switch (scope.id) { |
| 263 | .Root => unreachable, | 249 | .root => { |
| 264 | .Switch => return @fieldParentPtr(Switch, "base", scope), | 250 | const root = @fieldParentPtr(Root, "base", scope); |
| 251 | return root.nodes.append(node); | ||
| 252 | }, | ||
| 253 | .block => { | ||
| 254 | const block = @fieldParentPtr(Block, "base", scope); | ||
| 255 | return block.statements.append(node); | ||
| 256 | }, | ||
| 265 | else => scope = scope.parent.?, | 257 | else => scope = scope.parent.?, |
| 266 | } | 258 | } |
| 267 | } | 259 | } |
| ... | @@ -271,18 +263,12 @@ const Scope = struct { | ... | @@ -271,18 +263,12 @@ const Scope = struct { |
| 271 | pub const Context = struct { | 263 | pub const Context = struct { |
| 272 | gpa: *mem.Allocator, | 264 | gpa: *mem.Allocator, |
| 273 | arena: *mem.Allocator, | 265 | arena: *mem.Allocator, |
| 274 | token_ids: std.ArrayListUnmanaged(Token.Id) = .{}, | ||
| 275 | token_locs: std.ArrayListUnmanaged(Token.Loc) = .{}, | ||
| 276 | errors: std.ArrayListUnmanaged(ast.Error) = .{}, | ||
| 277 | source_buffer: *std.ArrayList(u8), | ||
| 278 | err: Error, | ||
| 279 | source_manager: *clang.SourceManager, | 266 | source_manager: *clang.SourceManager, |
| 280 | decl_table: std.AutoArrayHashMapUnmanaged(usize, []const u8) = .{}, | 267 | decl_table: std.AutoArrayHashMapUnmanaged(usize, []const u8) = .{}, |
| 281 | alias_list: AliasList, | 268 | alias_list: AliasList, |
| 282 | global_scope: *Scope.Root, | 269 | global_scope: *Scope.Root, |
| 283 | clang_context: *clang.ASTContext, | 270 | clang_context: *clang.ASTContext, |
| 284 | mangle_count: u32 = 0, | 271 | mangle_count: u32 = 0, |
| 285 | root_decls: std.ArrayListUnmanaged(*ast.Node) = .{}, | ||
| 286 | opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{}, | 272 | opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{}, |
| 287 | 273 | ||
| 288 | /// This one is different than the root scope's name table. This contains | 274 | /// This one is different than the root scope's name table. This contains |
| ... | @@ -311,90 +297,15 @@ pub const Context = struct { | ... | @@ -311,90 +297,15 @@ pub const Context = struct { |
| 311 | const column = c.source_manager.getSpellingColumnNumber(spelling_loc); | 297 | const column = c.source_manager.getSpellingColumnNumber(spelling_loc); |
| 312 | return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, column }); | 298 | return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, column }); |
| 313 | } | 299 | } |
| 314 | |||
| 315 | fn createCall(c: *Context, fn_expr: *ast.Node, params_len: ast.NodeIndex) !*ast.Node.Call { | ||
| 316 | _ = try appendToken(c, .LParen, "("); | ||
| 317 | const node = try ast.Node.Call.alloc(c.arena, params_len); | ||
| 318 | node.* = .{ | ||
| 319 | .lhs = fn_expr, | ||
| 320 | .params_len = params_len, | ||
| 321 | .async_token = null, | ||
| 322 | .rtoken = undefined, // set after appending args | ||
| 323 | }; | ||
| 324 | return node; | ||
| 325 | } | ||
| 326 | |||
| 327 | fn createBuiltinCall(c: *Context, name: []const u8, params_len: ast.NodeIndex) !*ast.Node.BuiltinCall { | ||
| 328 | const builtin_token = try appendToken(c, .Builtin, name); | ||
| 329 | _ = try appendToken(c, .LParen, "("); | ||
| 330 | const node = try ast.Node.BuiltinCall.alloc(c.arena, params_len); | ||
| 331 | node.* = .{ | ||
| 332 | .builtin_token = builtin_token, | ||
| 333 | .params_len = params_len, | ||
| 334 | .rparen_token = undefined, // set after appending args | ||
| 335 | }; | ||
| 336 | return node; | ||
| 337 | } | ||
| 338 | |||
| 339 | fn createBlock(c: *Context, statements_len: ast.NodeIndex) !*ast.Node.Block { | ||
| 340 | const block_node = try ast.Node.Block.alloc(c.arena, statements_len); | ||
| 341 | block_node.* = .{ | ||
| 342 | .lbrace = try appendToken(c, .LBrace, "{"), | ||
| 343 | .statements_len = statements_len, | ||
| 344 | .rbrace = undefined, | ||
| 345 | }; | ||
| 346 | return block_node; | ||
| 347 | } | ||
| 348 | }; | 300 | }; |
| 349 | 301 | ||
| 350 | fn addCBuiltinsNamespace(c: *Context) Error!void { | ||
| 351 | // pub usingnamespace @import("std").c.builtins; | ||
| 352 | const pub_tok = try appendToken(c, .Keyword_pub, "pub"); | ||
| 353 | const use_tok = try appendToken(c, .Keyword_usingnamespace, "usingnamespace"); | ||
| 354 | const import_tok = try appendToken(c, .Builtin, "@import"); | ||
| 355 | const lparen_tok = try appendToken(c, .LParen, "("); | ||
| 356 | const std_tok = try appendToken(c, .StringLiteral, "\"std\""); | ||
| 357 | const rparen_tok = try appendToken(c, .RParen, ")"); | ||
| 358 | |||
| 359 | const std_node = try c.arena.create(ast.Node.OneToken); | ||
| 360 | std_node.* = .{ | ||
| 361 | .base = .{ .tag = .StringLiteral }, | ||
| 362 | .token = std_tok, | ||
| 363 | }; | ||
| 364 | |||
| 365 | const call_node = try ast.Node.BuiltinCall.alloc(c.arena, 1); | ||
| 366 | call_node.* = .{ | ||
| 367 | .builtin_token = import_tok, | ||
| 368 | .params_len = 1, | ||
| 369 | .rparen_token = rparen_tok, | ||
| 370 | }; | ||
| 371 | call_node.params()[0] = &std_node.base; | ||
| 372 | |||
| 373 | var access_chain = &call_node.base; | ||
| 374 | access_chain = try transCreateNodeFieldAccess(c, access_chain, "c"); | ||
| 375 | access_chain = try transCreateNodeFieldAccess(c, access_chain, "builtins"); | ||
| 376 | |||
| 377 | const semi_tok = try appendToken(c, .Semicolon, ";"); | ||
| 378 | |||
| 379 | const bytes = try c.gpa.alignedAlloc(u8, @alignOf(ast.Node.Use), @sizeOf(ast.Node.Use)); | ||
| 380 | const using_node = @ptrCast(*ast.Node.Use, bytes.ptr); | ||
| 381 | using_node.* = .{ | ||
| 382 | .doc_comments = null, | ||
| 383 | .visib_token = pub_tok, | ||
| 384 | .use_token = use_tok, | ||
| 385 | .expr = access_chain, | ||
| 386 | .semicolon_token = semi_tok, | ||
| 387 | }; | ||
| 388 | try c.root_decls.append(c.gpa, &using_node.base); | ||
| 389 | } | ||
| 390 | |||
| 391 | pub fn translate( | 302 | pub fn translate( |
| 392 | gpa: *mem.Allocator, | 303 | gpa: *mem.Allocator, |
| 393 | args_begin: [*]?[*]const u8, | 304 | args_begin: [*]?[*]const u8, |
| 394 | args_end: [*]?[*]const u8, | 305 | args_end: [*]?[*]const u8, |
| 395 | errors: *[]ClangErrMsg, | 306 | errors: *[]ClangErrMsg, |
| 396 | resources_path: [*:0]const u8, | 307 | resources_path: [*:0]const u8, |
| 397 | ) !*ast.Tree { | 308 | ) !std.zig.ast.Tree { |
| 398 | const ast_unit = clang.LoadFromCommandLine( | 309 | const ast_unit = clang.LoadFromCommandLine( |
| 399 | args_begin, | 310 | args_begin, |
| 400 | args_end, | 311 | args_end, |
| ... | @@ -407,9 +318,6 @@ pub fn translate( | ... | @@ -407,9 +318,6 @@ pub fn translate( |
| 407 | }; | 318 | }; |
| 408 | defer ast_unit.delete(); | 319 | defer ast_unit.delete(); |
| 409 | 320 | ||
| 410 | var source_buffer = std.ArrayList(u8).init(gpa); | ||
| 411 | defer source_buffer.deinit(); | ||
| 412 | |||
| 413 | // For memory that has the same lifetime as the Tree that we return | 321 | // For memory that has the same lifetime as the Tree that we return |
| 414 | // from this function. | 322 | // from this function. |
| 415 | var arena = std.heap.ArenaAllocator.init(gpa); | 323 | var arena = std.heap.ArenaAllocator.init(gpa); |
| ... | @@ -418,9 +326,7 @@ pub fn translate( | ... | @@ -418,9 +326,7 @@ pub fn translate( |
| 418 | var context = Context{ | 326 | var context = Context{ |
| 419 | .gpa = gpa, | 327 | .gpa = gpa, |
| 420 | .arena = &arena.allocator, | 328 | .arena = &arena.allocator, |
| 421 | .source_buffer = &source_buffer, | ||
| 422 | .source_manager = ast_unit.getSourceManager(), | 329 | .source_manager = ast_unit.getSourceManager(), |
| 423 | .err = undefined, | ||
| 424 | .alias_list = AliasList.init(gpa), | 330 | .alias_list = AliasList.init(gpa), |
| 425 | .global_scope = try arena.allocator.create(Scope.Root), | 331 | .global_scope = try arena.allocator.create(Scope.Root), |
| 426 | .clang_context = ast_unit.getASTContext(), | 332 | .clang_context = ast_unit.getASTContext(), |
| ... | @@ -429,20 +335,17 @@ pub fn translate( | ... | @@ -429,20 +335,17 @@ pub fn translate( |
| 429 | defer { | 335 | defer { |
| 430 | context.decl_table.deinit(gpa); | 336 | context.decl_table.deinit(gpa); |
| 431 | context.alias_list.deinit(); | 337 | context.alias_list.deinit(); |
| 432 | context.token_ids.deinit(gpa); | ||
| 433 | context.token_locs.deinit(gpa); | ||
| 434 | context.errors.deinit(gpa); | ||
| 435 | context.global_names.deinit(gpa); | 338 | context.global_names.deinit(gpa); |
| 436 | context.root_decls.deinit(gpa); | ||
| 437 | context.opaque_demotes.deinit(gpa); | 339 | context.opaque_demotes.deinit(gpa); |
| 340 | context.global_scope.deinit(); | ||
| 438 | } | 341 | } |
| 439 | 342 | ||
| 440 | try addCBuiltinsNamespace(&context); | 343 | try context.global_scope.nodes.append(Tag.usingnamespace_builtins.init()); |
| 441 | 344 | ||
| 442 | try prepopulateGlobalNameTable(ast_unit, &context); | 345 | try prepopulateGlobalNameTable(ast_unit, &context); |
| 443 | 346 | ||
| 444 | if (!ast_unit.visitLocalTopLevelDecls(&context, declVisitorC)) { | 347 | if (!ast_unit.visitLocalTopLevelDecls(&context, declVisitorC)) { |
| 445 | return context.err; | 348 | return error.OutOfMemory; |
| 446 | } | 349 | } |
| 447 | 350 | ||
| 448 | try transPreprocessorEntities(&context, ast_unit); | 351 | try transPreprocessorEntities(&context, ast_unit); |
| ... | @@ -450,38 +353,17 @@ pub fn translate( | ... | @@ -450,38 +353,17 @@ pub fn translate( |
| 450 | try addMacros(&context); | 353 | try addMacros(&context); |
| 451 | for (context.alias_list.items) |alias| { | 354 | for (context.alias_list.items) |alias| { |
| 452 | if (!context.global_scope.sym_table.contains(alias.alias)) { | 355 | if (!context.global_scope.sym_table.contains(alias.alias)) { |
| 453 | try createAlias(&context, alias); | 356 | const node = try Tag.alias.create(context.arena, .{ .actual = alias.alias, .mangled = alias.name }); |
| 454 | } | 357 | try addTopLevelDecl(&context, alias.alias, node); |
| 455 | } | ||
| 456 | |||
| 457 | const eof_token = try appendToken(&context, .Eof, ""); | ||
| 458 | const root_node = try ast.Node.Root.create(&arena.allocator, context.root_decls.items.len, eof_token); | ||
| 459 | mem.copy(*ast.Node, root_node.decls(), context.root_decls.items); | ||
| 460 | |||
| 461 | if (false) { | ||
| 462 | std.debug.warn("debug source:\n{s}\n==EOF==\ntokens:\n", .{source_buffer.items}); | ||
| 463 | for (context.token_ids.items) |token| { | ||
| 464 | std.debug.warn("{}\n", .{token}); | ||
| 465 | } | 358 | } |
| 466 | } | 359 | } |
| 467 | 360 | ||
| 468 | const tree = try arena.allocator.create(ast.Tree); | 361 | return ast.render(gpa, context.global_scope.nodes.items); |
| 469 | tree.* = .{ | ||
| 470 | .gpa = gpa, | ||
| 471 | .source = try arena.allocator.dupe(u8, source_buffer.items), | ||
| 472 | .token_ids = context.token_ids.toOwnedSlice(gpa), | ||
| 473 | .token_locs = context.token_locs.toOwnedSlice(gpa), | ||
| 474 | .errors = context.errors.toOwnedSlice(gpa), | ||
| 475 | .root_node = root_node, | ||
| 476 | .arena = arena.state, | ||
| 477 | .generated = true, | ||
| 478 | }; | ||
| 479 | return tree; | ||
| 480 | } | 362 | } |
| 481 | 363 | ||
| 482 | fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void { | 364 | fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void { |
| 483 | if (!ast_unit.visitLocalTopLevelDecls(c, declVisitorNamesOnlyC)) { | 365 | if (!ast_unit.visitLocalTopLevelDecls(c, declVisitorNamesOnlyC)) { |
| 484 | return c.err; | 366 | return error.OutOfMemory; |
| 485 | } | 367 | } |
| 486 | 368 | ||
| 487 | // TODO if we see #undef, delete it from the table | 369 | // TODO if we see #undef, delete it from the table |
| ... | @@ -504,19 +386,13 @@ fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void { | ... | @@ -504,19 +386,13 @@ fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void { |
| 504 | 386 | ||
| 505 | fn declVisitorNamesOnlyC(context: ?*c_void, decl: *const clang.Decl) callconv(.C) bool { | 387 | fn declVisitorNamesOnlyC(context: ?*c_void, decl: *const clang.Decl) callconv(.C) bool { |
| 506 | const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context)); | 388 | const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context)); |
| 507 | declVisitorNamesOnly(c, decl) catch |err| { | 389 | declVisitorNamesOnly(c, decl) catch return false; |
| 508 | c.err = err; | ||
| 509 | return false; | ||
| 510 | }; | ||
| 511 | return true; | 390 | return true; |
| 512 | } | 391 | } |
| 513 | 392 | ||
| 514 | fn declVisitorC(context: ?*c_void, decl: *const clang.Decl) callconv(.C) bool { | 393 | fn declVisitorC(context: ?*c_void, decl: *const clang.Decl) callconv(.C) bool { |
| 515 | const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context)); | 394 | const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context)); |
| 516 | declVisitor(c, decl) catch |err| { | 395 | declVisitor(c, decl) catch return false; |
| 517 | c.err = err; | ||
| 518 | return false; | ||
| 519 | }; | ||
| 520 | return true; | 396 | return true; |
| 521 | } | 397 | } |
| 522 | 398 | ||
| ... | @@ -533,13 +409,13 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void { | ... | @@ -533,13 +409,13 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void { |
| 533 | return visitFnDecl(c, @ptrCast(*const clang.FunctionDecl, decl)); | 409 | return visitFnDecl(c, @ptrCast(*const clang.FunctionDecl, decl)); |
| 534 | }, | 410 | }, |
| 535 | .Typedef => { | 411 | .Typedef => { |
| 536 | _ = try transTypeDef(c, @ptrCast(*const clang.TypedefNameDecl, decl), true); | 412 | try transTypeDef(c, &c.global_scope.base, @ptrCast(*const clang.TypedefNameDecl, decl)); |
| 537 | }, | 413 | }, |
| 538 | .Enum => { | 414 | .Enum => { |
| 539 | _ = try transEnumDecl(c, @ptrCast(*const clang.EnumDecl, decl)); | 415 | try transEnumDecl(c, &c.global_scope.base, @ptrCast(*const clang.EnumDecl, decl)); |
| 540 | }, | 416 | }, |
| 541 | .Record => { | 417 | .Record => { |
| 542 | _ = try transRecordDecl(c, @ptrCast(*const clang.RecordDecl, decl)); | 418 | try transRecordDecl(c, &c.global_scope.base, @ptrCast(*const clang.RecordDecl, decl)); |
| 543 | }, | 419 | }, |
| 544 | .Var => { | 420 | .Var => { |
| 545 | return visitVarDecl(c, @ptrCast(*const clang.VarDecl, decl), null); | 421 | return visitVarDecl(c, @ptrCast(*const clang.VarDecl, decl), null); |
| ... | @@ -549,7 +425,7 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void { | ... | @@ -549,7 +425,7 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void { |
| 549 | }, | 425 | }, |
| 550 | else => { | 426 | else => { |
| 551 | const decl_name = try c.str(decl.getDeclKindName()); | 427 | const decl_name = try c.str(decl.getDeclKindName()); |
| 552 | try emitWarning(c, decl.getLocation(), "ignoring {s} declaration", .{decl_name}); | 428 | try warn(c, &c.global_scope.base, decl.getLocation(), "ignoring {s} declaration", .{decl_name}); |
| 553 | }, | 429 | }, |
| 554 | } | 430 | } |
| 555 | } | 431 | } |
| ... | @@ -565,7 +441,6 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { | ... | @@ -565,7 +441,6 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 565 | return visitFnDecl(c, def); | 441 | return visitFnDecl(c, def); |
| 566 | } | 442 | } |
| 567 | 443 | ||
| 568 | const rp = makeRestorePoint(c); | ||
| 569 | const fn_decl_loc = fn_decl.getLocation(); | 444 | const fn_decl_loc = fn_decl.getLocation(); |
| 570 | const has_body = fn_decl.hasBody(); | 445 | const has_body = fn_decl.hasBody(); |
| 571 | const storage_class = fn_decl.getStorageClass(); | 446 | const storage_class = fn_decl.getStorageClass(); |
| ... | @@ -609,9 +484,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { | ... | @@ -609,9 +484,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 609 | decl_ctx.has_body = false; | 484 | decl_ctx.has_body = false; |
| 610 | decl_ctx.storage_class = .Extern; | 485 | decl_ctx.storage_class = .Extern; |
| 611 | decl_ctx.is_export = false; | 486 | decl_ctx.is_export = false; |
| 612 | try emitWarning(c, fn_decl_loc, "TODO unable to translate variadic function, demoted to declaration", .{}); | 487 | try warn(c, &c.global_scope.base, fn_decl_loc, "TODO unable to translate variadic function, demoted to extern", .{}); |
| 613 | } | 488 | } |
| 614 | break :blk transFnProto(rp, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) { | 489 | break :blk transFnProto(c, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) { |
| 615 | error.UnsupportedType => { | 490 | error.UnsupportedType => { |
| 616 | return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{}); | 491 | return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{}); |
| 617 | }, | 492 | }, |
| ... | @@ -620,7 +495,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { | ... | @@ -620,7 +495,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 620 | }, | 495 | }, |
| 621 | .FunctionNoProto => blk: { | 496 | .FunctionNoProto => blk: { |
| 622 | const fn_no_proto_type = @ptrCast(*const clang.FunctionType, fn_type); | 497 | const fn_no_proto_type = @ptrCast(*const clang.FunctionType, fn_type); |
| 623 | break :blk transFnNoProto(rp, fn_no_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) { | 498 | break :blk transFnNoProto(c, fn_no_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) { |
| 624 | error.UnsupportedType => { | 499 | error.UnsupportedType => { |
| 625 | return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{}); | 500 | return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{}); |
| 626 | }, | 501 | }, |
| ... | @@ -631,124 +506,99 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { | ... | @@ -631,124 +506,99 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 631 | }; | 506 | }; |
| 632 | 507 | ||
| 633 | if (!decl_ctx.has_body) { | 508 | if (!decl_ctx.has_body) { |
| 634 | const semi_tok = try appendToken(c, .Semicolon, ";"); | 509 | return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); |
| 635 | return addTopLevelDecl(c, fn_name, &proto_node.base); | ||
| 636 | } | 510 | } |
| 637 | 511 | ||
| 638 | // actual function definition with body | 512 | // actual function definition with body |
| 639 | const body_stmt = fn_decl.getBody(); | 513 | const body_stmt = fn_decl.getBody(); |
| 640 | var block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, false); | 514 | var block_scope = try Scope.Block.init(c, &c.global_scope.base, false); |
| 641 | block_scope.return_type = return_qt; | 515 | block_scope.return_type = return_qt; |
| 642 | defer block_scope.deinit(); | 516 | defer block_scope.deinit(); |
| 643 | 517 | ||
| 644 | var scope = &block_scope.base; | 518 | var scope = &block_scope.base; |
| 645 | 519 | ||
| 646 | var param_id: c_uint = 0; | 520 | var param_id: c_uint = 0; |
| 647 | for (proto_node.params()) |*param, i| { | 521 | for (proto_node.data.params) |*param, i| { |
| 648 | const param_name = if (param.name_token) |name_tok| | 522 | const param_name = param.name orelse { |
| 649 | tokenSlice(c, name_tok) | 523 | proto_node.data.is_extern = true; |
| 650 | else | 524 | proto_node.data.is_export = false; |
| 651 | return failDecl(c, fn_decl_loc, fn_name, "function {s} parameter has no name", .{fn_name}); | 525 | try warn(c, &c.global_scope.base, fn_decl_loc, "function {s} parameter has no name, demoted to extern", .{fn_name}); |
| 526 | return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); | ||
| 527 | }; | ||
| 652 | 528 | ||
| 653 | const c_param = fn_decl.getParamDecl(param_id); | 529 | const c_param = fn_decl.getParamDecl(param_id); |
| 654 | const qual_type = c_param.getOriginalType(); | 530 | const qual_type = c_param.getOriginalType(); |
| 655 | const is_const = qual_type.isConstQualified(); | 531 | const is_const = qual_type.isConstQualified(); |
| 656 | 532 | ||
| 657 | const mangled_param_name = try block_scope.makeMangledName(c, param_name); | 533 | const mangled_param_name = try block_scope.makeMangledName(c, param_name); |
| 534 | param.name = mangled_param_name; | ||
| 658 | 535 | ||
| 659 | if (!is_const) { | 536 | if (!is_const) { |
| 660 | const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{s}", .{mangled_param_name}); | 537 | const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{s}", .{mangled_param_name}); |
| 661 | const arg_name = try block_scope.makeMangledName(c, bare_arg_name); | 538 | const arg_name = try block_scope.makeMangledName(c, bare_arg_name); |
| 539 | param.name = arg_name; | ||
| 662 | 540 | ||
| 663 | const mut_tok = try appendToken(c, .Keyword_var, "var"); | 541 | const redecl_node = try Tag.arg_redecl.create(c.arena, .{ .actual = mangled_param_name, .mangled = arg_name }); |
| 664 | const name_tok = try appendIdentifier(c, mangled_param_name); | 542 | try block_scope.statements.append(redecl_node); |
| 665 | const eq_token = try appendToken(c, .Equal, "="); | ||
| 666 | const init_node = try transCreateNodeIdentifier(c, arg_name); | ||
| 667 | const semicolon_token = try appendToken(c, .Semicolon, ";"); | ||
| 668 | const node = try ast.Node.VarDecl.create(c.arena, .{ | ||
| 669 | .mut_token = mut_tok, | ||
| 670 | .name_token = name_tok, | ||
| 671 | .semicolon_token = semicolon_token, | ||
| 672 | }, .{ | ||
| 673 | .eq_token = eq_token, | ||
| 674 | .init_node = init_node, | ||
| 675 | }); | ||
| 676 | try block_scope.statements.append(&node.base); | ||
| 677 | param.name_token = try appendIdentifier(c, arg_name); | ||
| 678 | _ = try appendToken(c, .Colon, ":"); | ||
| 679 | } | 543 | } |
| 680 | 544 | ||
| 681 | param_id += 1; | 545 | param_id += 1; |
| 682 | } | 546 | } |
| 683 | 547 | ||
| 684 | const casted_body = @ptrCast(*const clang.CompoundStmt, body_stmt); | 548 | const casted_body = @ptrCast(*const clang.CompoundStmt, body_stmt); |
| 685 | transCompoundStmtInline(rp, &block_scope.base, casted_body, &block_scope) catch |err| switch (err) { | 549 | transCompoundStmtInline(c, casted_body, &block_scope) catch |err| switch (err) { |
| 686 | error.OutOfMemory => |e| return e, | 550 | error.OutOfMemory => |e| return e, |
| 687 | error.UnsupportedTranslation, | 551 | error.UnsupportedTranslation, |
| 688 | error.UnsupportedType, | 552 | error.UnsupportedType, |
| 689 | => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}), | 553 | => { |
| 554 | proto_node.data.is_extern = true; | ||
| 555 | proto_node.data.is_export = false; | ||
| 556 | try warn(c, &c.global_scope.base, fn_decl_loc, "unable to translate function, demoted to extern", .{}); | ||
| 557 | return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); | ||
| 558 | }, | ||
| 690 | }; | 559 | }; |
| 691 | // add return statement if the function didn't have one | 560 | // add return statement if the function didn't have one |
| 692 | blk: { | 561 | blk: { |
| 693 | if (fn_ty.getNoReturnAttr()) break :blk; | 562 | const maybe_body = try block_scope.complete(c); |
| 694 | if (isCVoid(return_qt)) break :blk; | 563 | if (fn_ty.getNoReturnAttr() or isCVoid(return_qt) or maybe_body.isNoreturn(false)) { |
| 695 | 564 | proto_node.data.body = maybe_body; | |
| 696 | if (block_scope.statements.items.len > 0) { | 565 | break :blk; |
| 697 | var last = block_scope.statements.items[block_scope.statements.items.len - 1]; | ||
| 698 | while (true) { | ||
| 699 | switch (last.tag) { | ||
| 700 | .Block, .LabeledBlock => { | ||
| 701 | const stmts = last.blockStatements(); | ||
| 702 | if (stmts.len == 0) break; | ||
| 703 | |||
| 704 | last = stmts[stmts.len - 1]; | ||
| 705 | }, | ||
| 706 | // no extra return needed | ||
| 707 | .Return => break :blk, | ||
| 708 | else => break, | ||
| 709 | } | ||
| 710 | } | ||
| 711 | } | 566 | } |
| 712 | 567 | ||
| 713 | const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{ | 568 | const rhs = transZeroInitExpr(c, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) { |
| 714 | .ltoken = try appendToken(rp.c, .Keyword_return, "return"), | 569 | error.OutOfMemory => |e| return e, |
| 715 | .tag = .Return, | 570 | error.UnsupportedTranslation, |
| 716 | }, .{ | 571 | error.UnsupportedType, |
| 717 | .rhs = transZeroInitExpr(rp, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) { | 572 | => { |
| 718 | error.OutOfMemory => |e| return e, | 573 | proto_node.data.is_extern = true; |
| 719 | error.UnsupportedTranslation, | 574 | proto_node.data.is_export = false; |
| 720 | error.UnsupportedType, | 575 | try warn(c, &c.global_scope.base, fn_decl_loc, "unable to create a return value for function, demoted to extern", .{}); |
| 721 | => return failDecl(c, fn_decl_loc, fn_name, "unable to create a return value for function", .{}), | 576 | return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); |
| 722 | }, | 577 | }, |
| 723 | }); | 578 | }; |
| 724 | _ = try appendToken(rp.c, .Semicolon, ";"); | 579 | const ret = try Tag.@"return".create(c.arena, rhs); |
| 725 | try block_scope.statements.append(&return_expr.base); | 580 | try block_scope.statements.append(ret); |
| 581 | proto_node.data.body = try block_scope.complete(c); | ||
| 726 | } | 582 | } |
| 727 | 583 | ||
| 728 | const body_node = try block_scope.complete(rp.c); | 584 | return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); |
| 729 | proto_node.setBodyNode(body_node); | ||
| 730 | return addTopLevelDecl(c, fn_name, &proto_node.base); | ||
| 731 | } | 585 | } |
| 732 | 586 | ||
| 733 | fn transQualTypeMaybeInitialized(rp: RestorePoint, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!*ast.Node { | 587 | fn transQualTypeMaybeInitialized(c: *Context, scope: *Scope, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!Node { |
| 734 | return if (decl_init) |init_expr| | 588 | return if (decl_init) |init_expr| |
| 735 | transQualTypeInitialized(rp, qt, init_expr, loc) | 589 | transQualTypeInitialized(c, scope, qt, init_expr, loc) |
| 736 | else | 590 | else |
| 737 | transQualType(rp, qt, loc); | 591 | transQualType(c, scope, qt, loc); |
| 738 | } | 592 | } |
| 593 | |||
| 739 | /// if mangled_name is not null, this var decl was declared in a block scope. | 594 | /// if mangled_name is not null, this var decl was declared in a block scope. |
| 740 | fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]const u8) Error!void { | 595 | fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]const u8) Error!void { |
| 741 | const var_name = mangled_name orelse try c.str(@ptrCast(*const clang.NamedDecl, var_decl).getName_bytes_begin()); | 596 | const var_name = mangled_name orelse try c.str(@ptrCast(*const clang.NamedDecl, var_decl).getName_bytes_begin()); |
| 742 | if (c.global_scope.sym_table.contains(var_name)) | 597 | if (c.global_scope.sym_table.contains(var_name)) |
| 743 | return; // Avoid processing this decl twice | 598 | return; // Avoid processing this decl twice |
| 744 | const rp = makeRestorePoint(c); | ||
| 745 | const visib_tok = if (mangled_name) |_| null else try appendToken(c, .Keyword_pub, "pub"); | ||
| 746 | |||
| 747 | const thread_local_token = if (var_decl.getTLSKind() == .None) | ||
| 748 | null | ||
| 749 | else | ||
| 750 | try appendToken(c, .Keyword_threadlocal, "threadlocal"); | ||
| 751 | 599 | ||
| 600 | const is_pub = mangled_name == null; | ||
| 601 | const is_threadlocal = var_decl.getTLSKind() != .None; | ||
| 752 | const scope = &c.global_scope.base; | 602 | const scope = &c.global_scope.base; |
| 753 | 603 | ||
| 754 | // TODO https://github.com/ziglang/zig/issues/3756 | 604 | // TODO https://github.com/ziglang/zig/issues/3756 |
| ... | @@ -767,211 +617,148 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co | ... | @@ -767,211 +617,148 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co |
| 767 | // does the same as: | 617 | // does the same as: |
| 768 | // extern int foo; | 618 | // extern int foo; |
| 769 | // int foo = 2; | 619 | // int foo = 2; |
| 770 | const extern_tok = if (storage_class == .Extern and !has_init) | 620 | var is_extern = storage_class == .Extern and !has_init; |
| 771 | try appendToken(c, .Keyword_extern, "extern") | 621 | var is_export = !is_extern and storage_class != .Static; |
| 772 | else if (storage_class != .Static) | ||
| 773 | try appendToken(c, .Keyword_export, "export") | ||
| 774 | else | ||
| 775 | null; | ||
| 776 | |||
| 777 | const mut_tok = if (is_const) | ||
| 778 | try appendToken(c, .Keyword_const, "const") | ||
| 779 | else | ||
| 780 | try appendToken(c, .Keyword_var, "var"); | ||
| 781 | 622 | ||
| 782 | const name_tok = try appendIdentifier(c, checked_name); | 623 | const type_node = transQualTypeMaybeInitialized(c, scope, qual_type, decl_init, var_decl_loc) catch |err| switch (err) { |
| 783 | |||
| 784 | _ = try appendToken(c, .Colon, ":"); | ||
| 785 | |||
| 786 | const type_node = transQualTypeMaybeInitialized(rp, qual_type, decl_init, var_decl_loc) catch |err| switch (err) { | ||
| 787 | error.UnsupportedTranslation, error.UnsupportedType => { | 624 | error.UnsupportedTranslation, error.UnsupportedType => { |
| 788 | return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{}); | 625 | return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{}); |
| 789 | }, | 626 | }, |
| 790 | error.OutOfMemory => |e| return e, | 627 | error.OutOfMemory => |e| return e, |
| 791 | }; | 628 | }; |
| 792 | 629 | ||
| 793 | var eq_tok: ast.TokenIndex = undefined; | 630 | var init_node: ?Node = null; |
| 794 | var init_node: ?*ast.Node = null; | ||
| 795 | 631 | ||
| 796 | // If the initialization expression is not present, initialize with undefined. | 632 | // If the initialization expression is not present, initialize with undefined. |
| 797 | // If it is an integer literal, we can skip the @as since it will be redundant | 633 | // If it is an integer literal, we can skip the @as since it will be redundant |
| 798 | // with the variable type. | 634 | // with the variable type. |
| 799 | if (has_init) { | 635 | if (has_init) trans_init: { |
| 800 | eq_tok = try appendToken(c, .Equal, "="); | ||
| 801 | if (decl_init) |expr| { | 636 | if (decl_init) |expr| { |
| 802 | const node_or_error = if (expr.getStmtClass() == .StringLiteralClass) | 637 | const node_or_error = if (expr.getStmtClass() == .StringLiteralClass) |
| 803 | transStringLiteralAsArray(rp, &c.global_scope.base, @ptrCast(*const clang.StringLiteral, expr), zigArraySize(rp.c, type_node) catch 0) | 638 | transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), zigArraySize(c, type_node) catch 0) |
| 804 | else | 639 | else |
| 805 | transExprCoercing(rp, scope, expr, .used, .r_value); | 640 | transExprCoercing(c, scope, expr, .used); |
| 806 | init_node = node_or_error catch |err| switch (err) { | 641 | init_node = node_or_error catch |err| switch (err) { |
| 807 | error.UnsupportedTranslation, | 642 | error.UnsupportedTranslation, |
| 808 | error.UnsupportedType, | 643 | error.UnsupportedType, |
| 809 | => { | 644 | => { |
| 810 | return failDecl(c, var_decl_loc, checked_name, "unable to translate initializer", .{}); | 645 | is_extern = true; |
| 646 | is_export = false; | ||
| 647 | try warn(c, scope, var_decl_loc, "unable to translate variable initializer, demoted to extern", .{}); | ||
| 648 | break :trans_init; | ||
| 811 | }, | 649 | }, |
| 812 | error.OutOfMemory => |e| return e, | 650 | error.OutOfMemory => |e| return e, |
| 813 | }; | 651 | }; |
| 652 | if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node.?)) { | ||
| 653 | init_node = try Tag.bool_to_int.create(c.arena, init_node.?); | ||
| 654 | } | ||
| 814 | } else { | 655 | } else { |
| 815 | init_node = try transCreateNodeUndefinedLiteral(c); | 656 | init_node = Tag.undefined_literal.init(); |
| 816 | } | 657 | } |
| 817 | } else if (storage_class != .Extern) { | 658 | } else if (storage_class != .Extern) { |
| 818 | eq_tok = try appendToken(c, .Equal, "="); | ||
| 819 | // The C language specification states that variables with static or threadlocal | 659 | // The C language specification states that variables with static or threadlocal |
| 820 | // storage without an initializer are initialized to a zero value. | 660 | // storage without an initializer are initialized to a zero value. |
| 821 | 661 | ||
| 822 | // @import("std").mem.zeroes(T) | 662 | // @import("std").mem.zeroes(T) |
| 823 | const import_fn_call = try c.createBuiltinCall("@import", 1); | 663 | init_node = try Tag.std_mem_zeroes.create(c.arena, type_node); |
| 824 | const std_node = try transCreateNodeStringLiteral(c, "\"std\""); | ||
| 825 | import_fn_call.params()[0] = std_node; | ||
| 826 | import_fn_call.rparen_token = try appendToken(c, .RParen, ")"); | ||
| 827 | const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem"); | ||
| 828 | const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroes"); | ||
| 829 | |||
| 830 | const zero_init_call = try c.createCall(outer_field_access, 1); | ||
| 831 | zero_init_call.params()[0] = type_node; | ||
| 832 | zero_init_call.rtoken = try appendToken(c, .RParen, ")"); | ||
| 833 | |||
| 834 | init_node = &zero_init_call.base; | ||
| 835 | } | 664 | } |
| 836 | 665 | ||
| 837 | const linksection_expr = blk: { | 666 | const linksection_string = blk: { |
| 838 | var str_len: usize = undefined; | 667 | var str_len: usize = undefined; |
| 839 | if (var_decl.getSectionAttribute(&str_len)) |str_ptr| { | 668 | if (var_decl.getSectionAttribute(&str_len)) |str_ptr| { |
| 840 | _ = try appendToken(rp.c, .Keyword_linksection, "linksection"); | 669 | break :blk str_ptr[0..str_len]; |
| 841 | _ = try appendToken(rp.c, .LParen, "("); | ||
| 842 | const expr = try transCreateNodeStringLiteral( | ||
| 843 | rp.c, | ||
| 844 | try std.fmt.allocPrint(rp.c.arena, "\"{s}\"", .{str_ptr[0..str_len]}), | ||
| 845 | ); | ||
| 846 | _ = try appendToken(rp.c, .RParen, ")"); | ||
| 847 | |||
| 848 | break :blk expr; | ||
| 849 | } | 670 | } |
| 850 | break :blk null; | 671 | break :blk null; |
| 851 | }; | 672 | }; |
| 852 | 673 | ||
| 853 | const align_expr = blk: { | 674 | const alignment = blk: { |
| 854 | const alignment = var_decl.getAlignedAttribute(rp.c.clang_context); | 675 | const alignment = var_decl.getAlignedAttribute(c.clang_context); |
| 855 | if (alignment != 0) { | 676 | if (alignment != 0) { |
| 856 | _ = try appendToken(rp.c, .Keyword_align, "align"); | ||
| 857 | _ = try appendToken(rp.c, .LParen, "("); | ||
| 858 | // Clang reports the alignment in bits | 677 | // Clang reports the alignment in bits |
| 859 | const expr = try transCreateNodeInt(rp.c, alignment / 8); | 678 | break :blk alignment / 8; |
| 860 | _ = try appendToken(rp.c, .RParen, ")"); | ||
| 861 | |||
| 862 | break :blk expr; | ||
| 863 | } | 679 | } |
| 864 | break :blk null; | 680 | break :blk null; |
| 865 | }; | 681 | }; |
| 866 | 682 | ||
| 867 | const node = try ast.Node.VarDecl.create(c.arena, .{ | 683 | const node = try Tag.var_decl.create(c.arena, .{ |
| 868 | .name_token = name_tok, | 684 | .is_pub = is_pub, |
| 869 | .mut_token = mut_tok, | 685 | .is_const = is_const, |
| 870 | .semicolon_token = try appendToken(c, .Semicolon, ";"), | 686 | .is_extern = is_extern, |
| 871 | }, .{ | 687 | .is_export = is_export, |
| 872 | .visib_token = visib_tok, | 688 | .is_threadlocal = is_threadlocal, |
| 873 | .thread_local_token = thread_local_token, | 689 | .linksection_string = linksection_string, |
| 874 | .eq_token = eq_tok, | 690 | .alignment = alignment, |
| 875 | .extern_export_token = extern_tok, | 691 | .name = checked_name, |
| 876 | .type_node = type_node, | 692 | .type = type_node, |
| 877 | .align_node = align_expr, | 693 | .init = init_node, |
| 878 | .section_node = linksection_expr, | ||
| 879 | .init_node = init_node, | ||
| 880 | }); | 694 | }); |
| 881 | return addTopLevelDecl(c, checked_name, &node.base); | 695 | return addTopLevelDecl(c, checked_name, node); |
| 882 | } | 696 | } |
| 883 | 697 | ||
| 884 | fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const clang.TypedefNameDecl, builtin_name: []const u8) !*ast.Node { | 698 | const builtin_typedef_map = std.ComptimeStringMap([]const u8, .{ |
| 885 | _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin_name); | 699 | .{ "uint8_t", "u8" }, |
| 886 | return transCreateNodeIdentifier(c, builtin_name); | 700 | .{ "int8_t", "i8" }, |
| 887 | } | 701 | .{ "uint16_t", "u16" }, |
| 888 | 702 | .{ "int16_t", "i16" }, | |
| 889 | fn checkForBuiltinTypedef(checked_name: []const u8) ?[]const u8 { | 703 | .{ "uint32_t", "u32" }, |
| 890 | const table = [_][2][]const u8{ | 704 | .{ "int32_t", "i32" }, |
| 891 | .{ "uint8_t", "u8" }, | 705 | .{ "uint64_t", "u64" }, |
| 892 | .{ "int8_t", "i8" }, | 706 | .{ "int64_t", "i64" }, |
| 893 | .{ "uint16_t", "u16" }, | 707 | .{ "intptr_t", "isize" }, |
| 894 | .{ "int16_t", "i16" }, | 708 | .{ "uintptr_t", "usize" }, |
| 895 | .{ "uint32_t", "u32" }, | 709 | .{ "ssize_t", "isize" }, |
| 896 | .{ "int32_t", "i32" }, | 710 | .{ "size_t", "usize" }, |
| 897 | .{ "uint64_t", "u64" }, | 711 | }); |
| 898 | .{ "int64_t", "i64" }, | ||
| 899 | .{ "intptr_t", "isize" }, | ||
| 900 | .{ "uintptr_t", "usize" }, | ||
| 901 | .{ "ssize_t", "isize" }, | ||
| 902 | .{ "size_t", "usize" }, | ||
| 903 | }; | ||
| 904 | |||
| 905 | for (table) |entry| { | ||
| 906 | if (mem.eql(u8, checked_name, entry[0])) { | ||
| 907 | return entry[1]; | ||
| 908 | } | ||
| 909 | } | ||
| 910 | |||
| 911 | return null; | ||
| 912 | } | ||
| 913 | 712 | ||
| 914 | fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_level_visit: bool) Error!?*ast.Node { | 713 | fn transTypeDef(c: *Context, scope: *Scope, typedef_decl: *const clang.TypedefNameDecl) Error!void { |
| 915 | if (c.decl_table.get(@ptrToInt(typedef_decl.getCanonicalDecl()))) |name| | 714 | if (c.decl_table.get(@ptrToInt(typedef_decl.getCanonicalDecl()))) |name| |
| 916 | return transCreateNodeIdentifier(c, name); // Avoid processing this decl twice | 715 | return; // Avoid processing this decl twice |
| 917 | const rp = makeRestorePoint(c); | 716 | const toplevel = scope.id == .root; |
| 717 | const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; | ||
| 918 | 718 | ||
| 919 | const typedef_name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin()); | 719 | const bare_name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin()); |
| 920 | 720 | ||
| 921 | // TODO https://github.com/ziglang/zig/issues/3756 | 721 | // TODO https://github.com/ziglang/zig/issues/3756 |
| 922 | // TODO https://github.com/ziglang/zig/issues/1802 | 722 | // TODO https://github.com/ziglang/zig/issues/1802 |
| 923 | const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ typedef_name, c.getMangle() }) else typedef_name; | 723 | var name: []const u8 = if (isZigPrimitiveType(bare_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ bare_name, c.getMangle() }) else bare_name; |
| 924 | if (checkForBuiltinTypedef(checked_name)) |builtin| { | 724 | if (builtin_typedef_map.get(name)) |builtin| { |
| 925 | return transTypeDefAsBuiltin(c, typedef_decl, builtin); | 725 | return c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin); |
| 926 | } | ||
| 927 | |||
| 928 | if (!top_level_visit) { | ||
| 929 | return transCreateNodeIdentifier(c, checked_name); | ||
| 930 | } | 726 | } |
| 727 | if (!toplevel) name = try bs.makeMangledName(c, name); | ||
| 728 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), name); | ||
| 931 | 729 | ||
| 932 | _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), checked_name); | ||
| 933 | const node = (try transCreateNodeTypedef(rp, typedef_decl, true, checked_name)) orelse return null; | ||
| 934 | try addTopLevelDecl(c, checked_name, node); | ||
| 935 | return transCreateNodeIdentifier(c, checked_name); | ||
| 936 | } | ||
| 937 | |||
| 938 | fn transCreateNodeTypedef( | ||
| 939 | rp: RestorePoint, | ||
| 940 | typedef_decl: *const clang.TypedefNameDecl, | ||
| 941 | toplevel: bool, | ||
| 942 | checked_name: []const u8, | ||
| 943 | ) Error!?*ast.Node { | ||
| 944 | const visib_tok = if (toplevel) try appendToken(rp.c, .Keyword_pub, "pub") else null; | ||
| 945 | const mut_tok = try appendToken(rp.c, .Keyword_const, "const"); | ||
| 946 | const name_tok = try appendIdentifier(rp.c, checked_name); | ||
| 947 | const eq_token = try appendToken(rp.c, .Equal, "="); | ||
| 948 | const child_qt = typedef_decl.getUnderlyingType(); | 730 | const child_qt = typedef_decl.getUnderlyingType(); |
| 949 | const typedef_loc = typedef_decl.getLocation(); | 731 | const typedef_loc = typedef_decl.getLocation(); |
| 950 | const init_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) { | 732 | const init_node = transQualType(c, scope, child_qt, typedef_loc) catch |err| switch (err) { |
| 951 | error.UnsupportedType => { | 733 | error.UnsupportedType => { |
| 952 | try failDecl(rp.c, typedef_loc, checked_name, "unable to resolve typedef child type", .{}); | 734 | return failDecl(c, typedef_loc, name, "unable to resolve typedef child type", .{}); |
| 953 | return null; | ||
| 954 | }, | 735 | }, |
| 955 | error.OutOfMemory => |e| return e, | 736 | error.OutOfMemory => |e| return e, |
| 956 | }; | 737 | }; |
| 957 | const semicolon_token = try appendToken(rp.c, .Semicolon, ";"); | 738 | |
| 958 | 739 | const payload = try c.arena.create(ast.Payload.SimpleVarDecl); | |
| 959 | const node = try ast.Node.VarDecl.create(rp.c.arena, .{ | 740 | payload.* = .{ |
| 960 | .name_token = name_tok, | 741 | .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@boolToInt(toplevel)] }, |
| 961 | .mut_token = mut_tok, | 742 | .data = .{ |
| 962 | .semicolon_token = semicolon_token, | 743 | .name = name, |
| 963 | }, .{ | 744 | .init = init_node, |
| 964 | .visib_token = visib_tok, | 745 | }, |
| 965 | .eq_token = eq_token, | 746 | }; |
| 966 | .init_node = init_node, | 747 | const node = Node.initPayload(&payload.base); |
| 967 | }); | 748 | |
| 968 | return &node.base; | 749 | if (toplevel) { |
| 750 | try addTopLevelDecl(c, name, node); | ||
| 751 | } else { | ||
| 752 | try scope.appendNode(node); | ||
| 753 | } | ||
| 969 | } | 754 | } |
| 970 | 755 | ||
| 971 | fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*ast.Node { | 756 | fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordDecl) Error!void { |
| 972 | if (c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl()))) |name| | 757 | if (c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl()))) |name| |
| 973 | return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice | 758 | return; // Avoid processing this decl twice |
| 974 | const record_loc = record_decl.getLocation(); | 759 | const record_loc = record_decl.getLocation(); |
| 760 | const toplevel = scope.id == .root; | ||
| 761 | const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; | ||
| 975 | 762 | ||
| 976 | var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin()); | 763 | var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin()); |
| 977 | var is_unnamed = false; | 764 | var is_unnamed = false; |
| ... | @@ -983,46 +770,31 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as | ... | @@ -983,46 +770,31 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as |
| 983 | } | 770 | } |
| 984 | 771 | ||
| 985 | var container_kind_name: []const u8 = undefined; | 772 | var container_kind_name: []const u8 = undefined; |
| 986 | var container_kind: std.zig.Token.Id = undefined; | 773 | var is_union = false; |
| 987 | if (record_decl.isUnion()) { | 774 | if (record_decl.isUnion()) { |
| 988 | container_kind_name = "union"; | 775 | container_kind_name = "union"; |
| 989 | container_kind = .Keyword_union; | 776 | is_union = true; |
| 990 | } else if (record_decl.isStruct()) { | 777 | } else if (record_decl.isStruct()) { |
| 991 | container_kind_name = "struct"; | 778 | container_kind_name = "struct"; |
| 992 | container_kind = .Keyword_struct; | ||
| 993 | } else { | 779 | } else { |
| 994 | try emitWarning(c, record_loc, "record {s} is not a struct or union", .{bare_name}); | 780 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), bare_name); |
| 995 | return null; | 781 | return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name}); |
| 996 | } | 782 | } |
| 997 | 783 | ||
| 998 | const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); | 784 | var name: []const u8 = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); |
| 999 | _ = try c.decl_table.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name); | 785 | if (!toplevel) name = try bs.makeMangledName(c, name); |
| 1000 | 786 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name); | |
| 1001 | const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null; | ||
| 1002 | const mut_tok = try appendToken(c, .Keyword_const, "const"); | ||
| 1003 | const name_tok = try appendIdentifier(c, name); | ||
| 1004 | |||
| 1005 | const eq_token = try appendToken(c, .Equal, "="); | ||
| 1006 | 787 | ||
| 1007 | var semicolon: ast.TokenIndex = undefined; | 788 | const is_pub = toplevel and !is_unnamed; |
| 1008 | const init_node = blk: { | 789 | const init_node = blk: { |
| 1009 | const rp = makeRestorePoint(c); | ||
| 1010 | const record_def = record_decl.getDefinition() orelse { | 790 | const record_def = record_decl.getDefinition() orelse { |
| 1011 | _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); | 791 | _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); |
| 1012 | const opaque_type = try transCreateNodeOpaqueType(c); | 792 | break :blk Tag.opaque_literal.init(); |
| 1013 | semicolon = try appendToken(c, .Semicolon, ";"); | ||
| 1014 | break :blk opaque_type; | ||
| 1015 | }; | 793 | }; |
| 1016 | 794 | ||
| 1017 | const layout_tok = try if (record_decl.getPackedAttribute()) | 795 | const is_packed = record_decl.getPackedAttribute(); |
| 1018 | appendToken(c, .Keyword_packed, "packed") | 796 | var fields = std.ArrayList(ast.Payload.Record.Field).init(c.gpa); |
| 1019 | else | 797 | defer fields.deinit(); |
| 1020 | appendToken(c, .Keyword_extern, "extern"); | ||
| 1021 | const container_tok = try appendToken(c, container_kind, container_kind_name); | ||
| 1022 | const lbrace_token = try appendToken(c, .LBrace, "{"); | ||
| 1023 | |||
| 1024 | var fields_and_decls = std.ArrayList(*ast.Node).init(c.gpa); | ||
| 1025 | defer fields_and_decls.deinit(); | ||
| 1026 | 798 | ||
| 1027 | var unnamed_field_count: u32 = 0; | 799 | var unnamed_field_count: u32 = 0; |
| 1028 | var it = record_def.field_begin(); | 800 | var it = record_def.field_begin(); |
| ... | @@ -1034,111 +806,88 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as | ... | @@ -1034,111 +806,88 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as |
| 1034 | 806 | ||
| 1035 | if (field_decl.isBitField()) { | 807 | if (field_decl.isBitField()) { |
| 1036 | _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); | 808 | _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); |
| 1037 | const opaque_type = try transCreateNodeOpaqueType(c); | 809 | try warn(c, scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); |
| 1038 | semicolon = try appendToken(c, .Semicolon, ";"); | 810 | break :blk Tag.opaque_literal.init(); |
| 1039 | try emitWarning(c, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); | ||
| 1040 | break :blk opaque_type; | ||
| 1041 | } | 811 | } |
| 1042 | 812 | ||
| 1043 | if (qualTypeCanon(field_qt).isIncompleteOrZeroLengthArrayType(c.clang_context)) { | 813 | if (qualTypeCanon(field_qt).isIncompleteOrZeroLengthArrayType(c.clang_context)) { |
| 1044 | _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); | 814 | _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); |
| 1045 | const opaque_type = try transCreateNodeOpaqueType(c); | 815 | try warn(c, scope, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name}); |
| 1046 | semicolon = try appendToken(c, .Semicolon, ";"); | 816 | break :blk Tag.opaque_literal.init(); |
| 1047 | try emitWarning(c, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name}); | ||
| 1048 | break :blk opaque_type; | ||
| 1049 | } | 817 | } |
| 1050 | 818 | ||
| 1051 | var is_anon = false; | 819 | var is_anon = false; |
| 1052 | var raw_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin()); | 820 | var field_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin()); |
| 1053 | if (field_decl.isAnonymousStructOrUnion() or raw_name.len == 0) { | 821 | if (field_decl.isAnonymousStructOrUnion() or field_name.len == 0) { |
| 1054 | // Context.getMangle() is not used here because doing so causes unpredictable field names for anonymous fields. | 822 | // Context.getMangle() is not used here because doing so causes unpredictable field names for anonymous fields. |
| 1055 | raw_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count}); | 823 | field_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count}); |
| 1056 | unnamed_field_count += 1; | 824 | unnamed_field_count += 1; |
| 1057 | is_anon = true; | 825 | is_anon = true; |
| 1058 | } | 826 | } |
| 1059 | const field_name = try appendIdentifier(c, raw_name); | 827 | const field_type = transQualType(c, scope, field_qt, field_loc) catch |err| switch (err) { |
| 1060 | _ = try appendToken(c, .Colon, ":"); | ||
| 1061 | const field_type = transQualType(rp, field_qt, field_loc) catch |err| switch (err) { | ||
| 1062 | error.UnsupportedType => { | 828 | error.UnsupportedType => { |
| 1063 | _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); | 829 | _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); |
| 1064 | const opaque_type = try transCreateNodeOpaqueType(c); | 830 | try warn(c, scope, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, field_name }); |
| 1065 | semicolon = try appendToken(c, .Semicolon, ";"); | 831 | break :blk Tag.opaque_literal.init(); |
| 1066 | try emitWarning(c, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, raw_name }); | ||
| 1067 | break :blk opaque_type; | ||
| 1068 | }, | 832 | }, |
| 1069 | else => |e| return e, | 833 | else => |e| return e, |
| 1070 | }; | 834 | }; |
| 1071 | 835 | ||
| 1072 | const align_expr = blk_2: { | 836 | const alignment = blk_2: { |
| 1073 | const alignment = field_decl.getAlignedAttribute(c.clang_context); | 837 | const alignment = field_decl.getAlignedAttribute(c.clang_context); |
| 1074 | if (alignment != 0) { | 838 | if (alignment != 0) { |
| 1075 | _ = try appendToken(c, .Keyword_align, "align"); | ||
| 1076 | _ = try appendToken(c, .LParen, "("); | ||
| 1077 | // Clang reports the alignment in bits | 839 | // Clang reports the alignment in bits |
| 1078 | const expr = try transCreateNodeInt(c, alignment / 8); | 840 | break :blk_2 alignment / 8; |
| 1079 | _ = try appendToken(c, .RParen, ")"); | ||
| 1080 | |||
| 1081 | break :blk_2 expr; | ||
| 1082 | } | 841 | } |
| 1083 | break :blk_2 null; | 842 | break :blk_2 null; |
| 1084 | }; | 843 | }; |
| 1085 | 844 | ||
| 1086 | const field_node = try c.arena.create(ast.Node.ContainerField); | ||
| 1087 | field_node.* = .{ | ||
| 1088 | .doc_comments = null, | ||
| 1089 | .comptime_token = null, | ||
| 1090 | .name_token = field_name, | ||
| 1091 | .type_expr = field_type, | ||
| 1092 | .value_expr = null, | ||
| 1093 | .align_expr = align_expr, | ||
| 1094 | }; | ||
| 1095 | |||
| 1096 | if (is_anon) { | 845 | if (is_anon) { |
| 1097 | _ = try c.decl_table.put( | 846 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(field_decl.getCanonicalDecl()), field_name); |
| 1098 | c.gpa, | ||
| 1099 | @ptrToInt(field_decl.getCanonicalDecl()), | ||
| 1100 | raw_name, | ||
| 1101 | ); | ||
| 1102 | } | 847 | } |
| 1103 | 848 | ||
| 1104 | try fields_and_decls.append(&field_node.base); | 849 | try fields.append(.{ |
| 1105 | _ = try appendToken(c, .Comma, ","); | 850 | .name = field_name, |
| 851 | .type = field_type, | ||
| 852 | .alignment = alignment, | ||
| 853 | }); | ||
| 1106 | } | 854 | } |
| 1107 | const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len); | 855 | |
| 1108 | container_node.* = .{ | 856 | const record_payload = try c.arena.create(ast.Payload.Record); |
| 1109 | .layout_token = layout_tok, | 857 | record_payload.* = .{ |
| 1110 | .kind_token = container_tok, | 858 | .base = .{ .tag = ([2]Tag{ .@"struct", .@"union" })[@boolToInt(is_union)] }, |
| 1111 | .init_arg_expr = .None, | 859 | .data = .{ |
| 1112 | .fields_and_decls_len = fields_and_decls.items.len, | 860 | .is_packed = is_packed, |
| 1113 | .lbrace_token = lbrace_token, | 861 | .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items), |
| 1114 | .rbrace_token = try appendToken(c, .RBrace, "}"), | 862 | }, |
| 1115 | }; | 863 | }; |
| 1116 | mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items); | 864 | break :blk Node.initPayload(&record_payload.base); |
| 1117 | semicolon = try appendToken(c, .Semicolon, ";"); | ||
| 1118 | break :blk &container_node.base; | ||
| 1119 | }; | 865 | }; |
| 1120 | 866 | ||
| 1121 | const node = try ast.Node.VarDecl.create(c.arena, .{ | 867 | const payload = try c.arena.create(ast.Payload.SimpleVarDecl); |
| 1122 | .name_token = name_tok, | 868 | payload.* = .{ |
| 1123 | .mut_token = mut_tok, | 869 | .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] }, |
| 1124 | .semicolon_token = semicolon, | 870 | .data = .{ |
| 1125 | }, .{ | 871 | .name = name, |
| 1126 | .visib_token = visib_tok, | 872 | .init = init_node, |
| 1127 | .eq_token = eq_token, | 873 | }, |
| 1128 | .init_node = init_node, | 874 | }; |
| 1129 | }); | ||
| 1130 | 875 | ||
| 1131 | try addTopLevelDecl(c, name, &node.base); | 876 | if (toplevel) { |
| 1132 | if (!is_unnamed) | 877 | try addTopLevelDecl(c, name, Node.initPayload(&payload.base)); |
| 1133 | try c.alias_list.append(.{ .alias = bare_name, .name = name }); | 878 | if (!is_unnamed) |
| 1134 | return transCreateNodeIdentifier(c, name); | 879 | try c.alias_list.append(.{ .alias = bare_name, .name = name }); |
| 880 | } else { | ||
| 881 | try scope.appendNode(Node.initPayload(&payload.base)); | ||
| 882 | } | ||
| 1135 | } | 883 | } |
| 1136 | 884 | ||
| 1137 | fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node { | 885 | fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) Error!void { |
| 1138 | if (c.decl_table.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |name| | 886 | if (c.decl_table.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |name| |
| 1139 | return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice | 887 | return; // Avoid processing this decl twice |
| 1140 | const rp = makeRestorePoint(c); | ||
| 1141 | const enum_loc = enum_decl.getLocation(); | 888 | const enum_loc = enum_decl.getLocation(); |
| 889 | const toplevel = scope.id == .root; | ||
| 890 | const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; | ||
| 1142 | 891 | ||
| 1143 | var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()); | 892 | var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()); |
| 1144 | var is_unnamed = false; | 893 | var is_unnamed = false; |
| ... | @@ -1147,13 +896,13 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node | ... | @@ -1147,13 +896,13 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node |
| 1147 | is_unnamed = true; | 896 | is_unnamed = true; |
| 1148 | } | 897 | } |
| 1149 | 898 | ||
| 1150 | const name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name}); | 899 | var name: []const u8 = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name}); |
| 1151 | _ = try c.decl_table.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name); | 900 | if (!toplevel) _ = try bs.makeMangledName(c, name); |
| 901 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name); | ||
| 1152 | 902 | ||
| 1153 | const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null; | 903 | const is_pub = toplevel and !is_unnamed; |
| 1154 | const mut_tok = try appendToken(c, .Keyword_const, "const"); | 904 | var redecls = std.ArrayList(Tag.enum_redecl.Data()).init(c.gpa); |
| 1155 | const name_tok = try appendIdentifier(c, name); | 905 | defer redecls.deinit(); |
| 1156 | const eq_token = try appendToken(c, .Equal, "="); | ||
| 1157 | 906 | ||
| 1158 | const init_node = if (enum_decl.getDefinition()) |enum_def| blk: { | 907 | const init_node = if (enum_decl.getDefinition()) |enum_def| blk: { |
| 1159 | var pure_enum = true; | 908 | var pure_enum = true; |
| ... | @@ -1167,11 +916,8 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node | ... | @@ -1167,11 +916,8 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node |
| 1167 | } | 916 | } |
| 1168 | } | 917 | } |
| 1169 | 918 | ||
| 1170 | const extern_tok = try appendToken(c, .Keyword_extern, "extern"); | 919 | var fields = std.ArrayList(ast.Payload.Enum.Field).init(c.gpa); |
| 1171 | const container_tok = try appendToken(c, .Keyword_enum, "enum"); | 920 | defer fields.deinit(); |
| 1172 | |||
| 1173 | var fields_and_decls = std.ArrayList(*ast.Node).init(c.gpa); | ||
| 1174 | defer fields_and_decls.deinit(); | ||
| 1175 | 921 | ||
| 1176 | const int_type = enum_decl.getIntegerType(); | 922 | const int_type = enum_decl.getIntegerType(); |
| 1177 | // The underlying type may be null in case of forward-declared enum | 923 | // The underlying type may be null in case of forward-declared enum |
| ... | @@ -1179,30 +925,22 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node | ... | @@ -1179,30 +925,22 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node |
| 1179 | // default to the usual integer type used for all the enums. | 925 | // default to the usual integer type used for all the enums. |
| 1180 | 926 | ||
| 1181 | // default to c_int since msvc and gcc default to different types | 927 | // default to c_int since msvc and gcc default to different types |
| 1182 | _ = try appendToken(c, .LParen, "("); | 928 | const init_arg_expr = if (int_type.ptr != null and |
| 1183 | const init_arg_expr = ast.Node.ContainerDecl.InitArg{ | 929 | !isCBuiltinType(int_type, .UInt) and |
| 1184 | .Type = if (int_type.ptr != null and | 930 | !isCBuiltinType(int_type, .Int)) |
| 1185 | !isCBuiltinType(int_type, .UInt) and | 931 | transQualType(c, scope, int_type, enum_loc) catch |err| switch (err) { |
| 1186 | !isCBuiltinType(int_type, .Int)) | 932 | error.UnsupportedType => { |
| 1187 | transQualType(rp, int_type, enum_loc) catch |err| switch (err) { | 933 | return failDecl(c, enum_loc, name, "unable to translate enum tag type", .{}); |
| 1188 | error.UnsupportedType => { | 934 | }, |
| 1189 | try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{}); | 935 | else => |e| return e, |
| 1190 | return null; | 936 | } |
| 1191 | }, | 937 | else |
| 1192 | else => |e| return e, | 938 | try Tag.type.create(c.arena, "c_int"); |
| 1193 | } | ||
| 1194 | else | ||
| 1195 | try transCreateNodeIdentifier(c, "c_int"), | ||
| 1196 | }; | ||
| 1197 | _ = try appendToken(c, .RParen, ")"); | ||
| 1198 | |||
| 1199 | const lbrace_token = try appendToken(c, .LBrace, "{"); | ||
| 1200 | 939 | ||
| 1201 | it = enum_def.enumerator_begin(); | 940 | it = enum_def.enumerator_begin(); |
| 1202 | end_it = enum_def.enumerator_end(); | 941 | end_it = enum_def.enumerator_end(); |
| 1203 | while (it.neq(end_it)) : (it = it.next()) { | 942 | while (it.neq(end_it)) : (it = it.next()) { |
| 1204 | const enum_const = it.deref(); | 943 | const enum_const = it.deref(); |
| 1205 | |||
| 1206 | const enum_val_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_const).getName_bytes_begin()); | 944 | const enum_val_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_const).getName_bytes_begin()); |
| 1207 | 945 | ||
| 1208 | const field_name = if (!is_unnamed and mem.startsWith(u8, enum_val_name, bare_name)) | 946 | const field_name = if (!is_unnamed and mem.startsWith(u8, enum_val_name, bare_name)) |
| ... | @@ -1210,123 +948,62 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node | ... | @@ -1210,123 +948,62 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node |
| 1210 | else | 948 | else |
| 1211 | enum_val_name; | 949 | enum_val_name; |
| 1212 | 950 | ||
| 1213 | const field_name_tok = try appendIdentifier(c, field_name); | 951 | const int_node = if (!pure_enum) |
| 1214 | 952 | try transCreateNodeAPInt(c, enum_const.getInitVal()) | |
| 1215 | const int_node = if (!pure_enum) blk_2: { | 953 | else |
| 1216 | _ = try appendToken(c, .Colon, "="); | ||
| 1217 | break :blk_2 try transCreateNodeAPInt(c, enum_const.getInitVal()); | ||
| 1218 | } else | ||
| 1219 | null; | 954 | null; |
| 1220 | 955 | ||
| 1221 | const field_node = try c.arena.create(ast.Node.ContainerField); | 956 | try fields.append(.{ |
| 1222 | field_node.* = .{ | 957 | .name = field_name, |
| 1223 | .doc_comments = null, | 958 | .value = int_node, |
| 1224 | .comptime_token = null, | 959 | }); |
| 1225 | .name_token = field_name_tok, | ||
| 1226 | .type_expr = null, | ||
| 1227 | .value_expr = int_node, | ||
| 1228 | .align_expr = null, | ||
| 1229 | }; | ||
| 1230 | |||
| 1231 | try fields_and_decls.append(&field_node.base); | ||
| 1232 | _ = try appendToken(c, .Comma, ","); | ||
| 1233 | 960 | ||
| 1234 | // In C each enum value is in the global namespace. So we put them there too. | 961 | // In C each enum value is in the global namespace. So we put them there too. |
| 1235 | // At this point we can rely on the enum emitting successfully. | 962 | // At this point we can rely on the enum emitting successfully. |
| 1236 | const tld_visib_tok = try appendToken(c, .Keyword_pub, "pub"); | 963 | try redecls.append(.{ |
| 1237 | const tld_mut_tok = try appendToken(c, .Keyword_const, "const"); | 964 | .enum_val_name = enum_val_name, |
| 1238 | const tld_name_tok = try appendIdentifier(c, enum_val_name); | 965 | .field_name = field_name, |
| 1239 | const tld_eq_token = try appendToken(c, .Equal, "="); | 966 | .enum_name = name, |
| 1240 | const cast_node = try rp.c.createBuiltinCall("@enumToInt", 1); | ||
| 1241 | const enum_ident = try transCreateNodeIdentifier(c, name); | ||
| 1242 | const period_tok = try appendToken(c, .Period, "."); | ||
| 1243 | const field_ident = try transCreateNodeIdentifier(c, field_name); | ||
| 1244 | const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 1245 | field_access_node.* = .{ | ||
| 1246 | .base = .{ .tag = .Period }, | ||
| 1247 | .op_token = period_tok, | ||
| 1248 | .lhs = enum_ident, | ||
| 1249 | .rhs = field_ident, | ||
| 1250 | }; | ||
| 1251 | cast_node.params()[0] = &field_access_node.base; | ||
| 1252 | cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 1253 | const tld_init_node = &cast_node.base; | ||
| 1254 | const tld_semicolon_token = try appendToken(c, .Semicolon, ";"); | ||
| 1255 | const tld_node = try ast.Node.VarDecl.create(c.arena, .{ | ||
| 1256 | .name_token = tld_name_tok, | ||
| 1257 | .mut_token = tld_mut_tok, | ||
| 1258 | .semicolon_token = tld_semicolon_token, | ||
| 1259 | }, .{ | ||
| 1260 | .visib_token = tld_visib_tok, | ||
| 1261 | .eq_token = tld_eq_token, | ||
| 1262 | .init_node = tld_init_node, | ||
| 1263 | }); | 967 | }); |
| 1264 | try addTopLevelDecl(c, field_name, &tld_node.base); | ||
| 1265 | } | 968 | } |
| 1266 | // make non exhaustive | ||
| 1267 | const field_node = try c.arena.create(ast.Node.ContainerField); | ||
| 1268 | field_node.* = .{ | ||
| 1269 | .doc_comments = null, | ||
| 1270 | .comptime_token = null, | ||
| 1271 | .name_token = try appendIdentifier(c, "_"), | ||
| 1272 | .type_expr = null, | ||
| 1273 | .value_expr = null, | ||
| 1274 | .align_expr = null, | ||
| 1275 | }; | ||
| 1276 | 969 | ||
| 1277 | try fields_and_decls.append(&field_node.base); | 970 | break :blk try Tag.@"enum".create(c.arena, .{ |
| 1278 | _ = try appendToken(c, .Comma, ","); | 971 | .int_type = init_arg_expr, |
| 1279 | const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len); | 972 | .fields = try c.arena.dupe(ast.Payload.Enum.Field, fields.items), |
| 1280 | container_node.* = .{ | 973 | }); |
| 1281 | .layout_token = extern_tok, | ||
| 1282 | .kind_token = container_tok, | ||
| 1283 | .init_arg_expr = init_arg_expr, | ||
| 1284 | .fields_and_decls_len = fields_and_decls.items.len, | ||
| 1285 | .lbrace_token = lbrace_token, | ||
| 1286 | .rbrace_token = try appendToken(c, .RBrace, "}"), | ||
| 1287 | }; | ||
| 1288 | mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items); | ||
| 1289 | break :blk &container_node.base; | ||
| 1290 | } else blk: { | 974 | } else blk: { |
| 1291 | _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), {}); | 975 | _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), {}); |
| 1292 | break :blk try transCreateNodeOpaqueType(c); | 976 | break :blk Tag.opaque_literal.init(); |
| 1293 | }; | 977 | }; |
| 1294 | 978 | ||
| 1295 | const semicolon_token = try appendToken(c, .Semicolon, ";"); | 979 | const payload = try c.arena.create(ast.Payload.SimpleVarDecl); |
| 1296 | const node = try ast.Node.VarDecl.create(c.arena, .{ | 980 | payload.* = .{ |
| 1297 | .name_token = name_tok, | 981 | .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] }, |
| 1298 | .mut_token = mut_tok, | 982 | .data = .{ |
| 1299 | .semicolon_token = semicolon_token, | 983 | .name = name, |
| 1300 | }, .{ | 984 | .init = init_node, |
| 1301 | .visib_token = visib_tok, | 985 | }, |
| 1302 | .eq_token = eq_token, | 986 | }; |
| 1303 | .init_node = init_node, | ||
| 1304 | }); | ||
| 1305 | 987 | ||
| 1306 | try addTopLevelDecl(c, name, &node.base); | 988 | if (toplevel) { |
| 1307 | if (!is_unnamed) | 989 | try addTopLevelDecl(c, name, Node.initPayload(&payload.base)); |
| 1308 | try c.alias_list.append(.{ .alias = bare_name, .name = name }); | 990 | if (!is_unnamed) |
| 1309 | return transCreateNodeIdentifier(c, name); | 991 | try c.alias_list.append(.{ .alias = bare_name, .name = name }); |
| 1310 | } | 992 | } else { |
| 1311 | 993 | try scope.appendNode(Node.initPayload(&payload.base)); | |
| 1312 | fn createAlias(c: *Context, alias: anytype) !void { | 994 | } |
| 1313 | const visib_tok = try appendToken(c, .Keyword_pub, "pub"); | 995 | |
| 1314 | const mut_tok = try appendToken(c, .Keyword_const, "const"); | 996 | for (redecls.items) |redecl| { |
| 1315 | const name_tok = try appendIdentifier(c, alias.alias); | 997 | if (toplevel) { |
| 1316 | const eq_token = try appendToken(c, .Equal, "="); | 998 | try addTopLevelDecl(c, redecl.field_name, try Tag.pub_enum_redecl.create(c.arena, redecl)); |
| 1317 | const init_node = try transCreateNodeIdentifier(c, alias.name); | 999 | } else { |
| 1318 | const semicolon_token = try appendToken(c, .Semicolon, ";"); | 1000 | try scope.appendNode(try Tag.enum_redecl.create(c.arena, .{ |
| 1319 | 1001 | .enum_val_name = try bs.makeMangledName(c, redecl.enum_val_name), | |
| 1320 | const node = try ast.Node.VarDecl.create(c.arena, .{ | 1002 | .field_name = redecl.field_name, |
| 1321 | .name_token = name_tok, | 1003 | .enum_name = redecl.enum_name, |
| 1322 | .mut_token = mut_tok, | 1004 | })); |
| 1323 | .semicolon_token = semicolon_token, | 1005 | } |
| 1324 | }, .{ | 1006 | } |
| 1325 | .visib_token = visib_tok, | ||
| 1326 | .eq_token = eq_token, | ||
| 1327 | .init_node = init_node, | ||
| 1328 | }); | ||
| 1329 | return addTopLevelDecl(c, alias.alias, &node.base); | ||
| 1330 | } | 1007 | } |
| 1331 | 1008 | ||
| 1332 | const ResultUsed = enum { | 1009 | const ResultUsed = enum { |
| ... | @@ -1334,317 +1011,252 @@ const ResultUsed = enum { | ... | @@ -1334,317 +1011,252 @@ const ResultUsed = enum { |
| 1334 | unused, | 1011 | unused, |
| 1335 | }; | 1012 | }; |
| 1336 | 1013 | ||
| 1337 | const LRValue = enum { | ||
| 1338 | l_value, | ||
| 1339 | r_value, | ||
| 1340 | }; | ||
| 1341 | |||
| 1342 | fn transStmt( | 1014 | fn transStmt( |
| 1343 | rp: RestorePoint, | 1015 | c: *Context, |
| 1344 | scope: *Scope, | 1016 | scope: *Scope, |
| 1345 | stmt: *const clang.Stmt, | 1017 | stmt: *const clang.Stmt, |
| 1346 | result_used: ResultUsed, | 1018 | result_used: ResultUsed, |
| 1347 | lrvalue: LRValue, | 1019 | ) TransError!Node { |
| 1348 | ) TransError!*ast.Node { | ||
| 1349 | const sc = stmt.getStmtClass(); | 1020 | const sc = stmt.getStmtClass(); |
| 1350 | switch (sc) { | 1021 | switch (sc) { |
| 1351 | .BinaryOperatorClass => return transBinaryOperator(rp, scope, @ptrCast(*const clang.BinaryOperator, stmt), result_used), | 1022 | .BinaryOperatorClass => return transBinaryOperator(c, scope, @ptrCast(*const clang.BinaryOperator, stmt), result_used), |
| 1352 | .CompoundStmtClass => return transCompoundStmt(rp, scope, @ptrCast(*const clang.CompoundStmt, stmt)), | 1023 | .CompoundStmtClass => return transCompoundStmt(c, scope, @ptrCast(*const clang.CompoundStmt, stmt)), |
| 1353 | .CStyleCastExprClass => return transCStyleCastExprClass(rp, scope, @ptrCast(*const clang.CStyleCastExpr, stmt), result_used, lrvalue), | 1024 | .CStyleCastExprClass => return transCStyleCastExprClass(c, scope, @ptrCast(*const clang.CStyleCastExpr, stmt), result_used), |
| 1354 | .DeclStmtClass => return transDeclStmt(rp, scope, @ptrCast(*const clang.DeclStmt, stmt)), | 1025 | .DeclStmtClass => return transDeclStmt(c, scope, @ptrCast(*const clang.DeclStmt, stmt)), |
| 1355 | .DeclRefExprClass => return transDeclRefExpr(rp, scope, @ptrCast(*const clang.DeclRefExpr, stmt), lrvalue), | 1026 | .DeclRefExprClass => return transDeclRefExpr(c, scope, @ptrCast(*const clang.DeclRefExpr, stmt)), |
| 1356 | .ImplicitCastExprClass => return transImplicitCastExpr(rp, scope, @ptrCast(*const clang.ImplicitCastExpr, stmt), result_used), | 1027 | .ImplicitCastExprClass => return transImplicitCastExpr(c, scope, @ptrCast(*const clang.ImplicitCastExpr, stmt), result_used), |
| 1357 | .IntegerLiteralClass => return transIntegerLiteral(rp, scope, @ptrCast(*const clang.IntegerLiteral, stmt), result_used, .with_as), | 1028 | .IntegerLiteralClass => return transIntegerLiteral(c, scope, @ptrCast(*const clang.IntegerLiteral, stmt), result_used, .with_as), |
| 1358 | .ReturnStmtClass => return transReturnStmt(rp, scope, @ptrCast(*const clang.ReturnStmt, stmt)), | 1029 | .ReturnStmtClass => return transReturnStmt(c, scope, @ptrCast(*const clang.ReturnStmt, stmt)), |
| 1359 | .StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const clang.StringLiteral, stmt), result_used), | 1030 | .StringLiteralClass => return transStringLiteral(c, scope, @ptrCast(*const clang.StringLiteral, stmt), result_used), |
| 1360 | .ParenExprClass => { | 1031 | .ParenExprClass => { |
| 1361 | const expr = try transExpr(rp, scope, @ptrCast(*const clang.ParenExpr, stmt).getSubExpr(), .used, lrvalue); | 1032 | const expr = try transExpr(c, scope, @ptrCast(*const clang.ParenExpr, stmt).getSubExpr(), .used); |
| 1362 | if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr); | 1033 | return maybeSuppressResult(c, scope, result_used, expr); |
| 1363 | const node = try rp.c.arena.create(ast.Node.GroupedExpression); | 1034 | }, |
| 1364 | node.* = .{ | 1035 | .InitListExprClass => return transInitListExpr(c, scope, @ptrCast(*const clang.InitListExpr, stmt), result_used), |
| 1365 | .lparen = try appendToken(rp.c, .LParen, "("), | 1036 | .ImplicitValueInitExprClass => return transImplicitValueInitExpr(c, scope, @ptrCast(*const clang.Expr, stmt), result_used), |
| 1366 | .expr = expr, | 1037 | .IfStmtClass => return transIfStmt(c, scope, @ptrCast(*const clang.IfStmt, stmt)), |
| 1367 | .rparen = try appendToken(rp.c, .RParen, ")"), | 1038 | .WhileStmtClass => return transWhileLoop(c, scope, @ptrCast(*const clang.WhileStmt, stmt)), |
| 1368 | }; | 1039 | .DoStmtClass => return transDoWhileLoop(c, scope, @ptrCast(*const clang.DoStmt, stmt)), |
| 1369 | return maybeSuppressResult(rp, scope, result_used, &node.base); | ||
| 1370 | }, | ||
| 1371 | .InitListExprClass => return transInitListExpr(rp, scope, @ptrCast(*const clang.InitListExpr, stmt), result_used), | ||
| 1372 | .ImplicitValueInitExprClass => return transImplicitValueInitExpr(rp, scope, @ptrCast(*const clang.Expr, stmt), result_used), | ||
| 1373 | .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const clang.IfStmt, stmt)), | ||
| 1374 | .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const clang.WhileStmt, stmt)), | ||
| 1375 | .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const clang.DoStmt, stmt)), | ||
| 1376 | .NullStmtClass => { | 1040 | .NullStmtClass => { |
| 1377 | const block = try rp.c.createBlock(0); | 1041 | return Tag.empty_block.init(); |
| 1378 | block.rbrace = try appendToken(rp.c, .RBrace, "}"); | 1042 | }, |
| 1379 | return &block.base; | 1043 | .ContinueStmtClass => return Tag.@"continue".init(), |
| 1380 | }, | 1044 | .BreakStmtClass => return Tag.@"break".init(), |
| 1381 | .ContinueStmtClass => return try transCreateNodeContinue(rp.c), | 1045 | .ForStmtClass => return transForLoop(c, scope, @ptrCast(*const clang.ForStmt, stmt)), |
| 1382 | .BreakStmtClass => return transBreak(rp, scope), | 1046 | .FloatingLiteralClass => return transFloatingLiteral(c, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used), |
| 1383 | .ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const clang.ForStmt, stmt)), | ||
| 1384 | .FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used), | ||
| 1385 | .ConditionalOperatorClass => { | 1047 | .ConditionalOperatorClass => { |
| 1386 | return transConditionalOperator(rp, scope, @ptrCast(*const clang.ConditionalOperator, stmt), result_used); | 1048 | return transConditionalOperator(c, scope, @ptrCast(*const clang.ConditionalOperator, stmt), result_used); |
| 1387 | }, | 1049 | }, |
| 1388 | .BinaryConditionalOperatorClass => { | 1050 | .BinaryConditionalOperatorClass => { |
| 1389 | return transBinaryConditionalOperator(rp, scope, @ptrCast(*const clang.BinaryConditionalOperator, stmt), result_used); | 1051 | return transBinaryConditionalOperator(c, scope, @ptrCast(*const clang.BinaryConditionalOperator, stmt), result_used); |
| 1390 | }, | 1052 | }, |
| 1391 | .SwitchStmtClass => return transSwitch(rp, scope, @ptrCast(*const clang.SwitchStmt, stmt)), | 1053 | .SwitchStmtClass => return transSwitch(c, scope, @ptrCast(*const clang.SwitchStmt, stmt)), |
| 1392 | .CaseStmtClass => return transCase(rp, scope, @ptrCast(*const clang.CaseStmt, stmt)), | 1054 | .CaseStmtClass, .DefaultStmtClass => { |
| 1393 | .DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const clang.DefaultStmt, stmt)), | 1055 | return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO complex switch", .{}); |
| 1394 | .ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const clang.Expr, stmt), result_used), | 1056 | }, |
| 1395 | .PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const clang.PredefinedExpr, stmt), result_used), | 1057 | .ConstantExprClass => return transConstantExpr(c, scope, @ptrCast(*const clang.Expr, stmt), result_used), |
| 1396 | .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const clang.CharacterLiteral, stmt), result_used, .with_as), | 1058 | .PredefinedExprClass => return transPredefinedExpr(c, scope, @ptrCast(*const clang.PredefinedExpr, stmt), result_used), |
| 1397 | .StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const clang.StmtExpr, stmt), result_used), | 1059 | .CharacterLiteralClass => return transCharLiteral(c, scope, @ptrCast(*const clang.CharacterLiteral, stmt), result_used, .with_as), |
| 1398 | .MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const clang.MemberExpr, stmt), result_used), | 1060 | .StmtExprClass => return transStmtExpr(c, scope, @ptrCast(*const clang.StmtExpr, stmt), result_used), |
| 1399 | .ArraySubscriptExprClass => return transArrayAccess(rp, scope, @ptrCast(*const clang.ArraySubscriptExpr, stmt), result_used), | 1061 | .MemberExprClass => return transMemberExpr(c, scope, @ptrCast(*const clang.MemberExpr, stmt), result_used), |
| 1400 | .CallExprClass => return transCallExpr(rp, scope, @ptrCast(*const clang.CallExpr, stmt), result_used), | 1062 | .ArraySubscriptExprClass => return transArrayAccess(c, scope, @ptrCast(*const clang.ArraySubscriptExpr, stmt), result_used), |
| 1401 | .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(rp, scope, @ptrCast(*const clang.UnaryExprOrTypeTraitExpr, stmt), result_used), | 1063 | .CallExprClass => return transCallExpr(c, scope, @ptrCast(*const clang.CallExpr, stmt), result_used), |
| 1402 | .UnaryOperatorClass => return transUnaryOperator(rp, scope, @ptrCast(*const clang.UnaryOperator, stmt), result_used), | 1064 | .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(c, scope, @ptrCast(*const clang.UnaryExprOrTypeTraitExpr, stmt), result_used), |
| 1403 | .CompoundAssignOperatorClass => return transCompoundAssignOperator(rp, scope, @ptrCast(*const clang.CompoundAssignOperator, stmt), result_used), | 1065 | .UnaryOperatorClass => return transUnaryOperator(c, scope, @ptrCast(*const clang.UnaryOperator, stmt), result_used), |
| 1066 | .CompoundAssignOperatorClass => return transCompoundAssignOperator(c, scope, @ptrCast(*const clang.CompoundAssignOperator, stmt), result_used), | ||
| 1404 | .OpaqueValueExprClass => { | 1067 | .OpaqueValueExprClass => { |
| 1405 | const source_expr = @ptrCast(*const clang.OpaqueValueExpr, stmt).getSourceExpr().?; | 1068 | const source_expr = @ptrCast(*const clang.OpaqueValueExpr, stmt).getSourceExpr().?; |
| 1406 | const expr = try transExpr(rp, scope, source_expr, .used, lrvalue); | 1069 | const expr = try transExpr(c, scope, source_expr, .used); |
| 1407 | if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr); | 1070 | return maybeSuppressResult(c, scope, result_used, expr); |
| 1408 | const node = try rp.c.arena.create(ast.Node.GroupedExpression); | ||
| 1409 | node.* = .{ | ||
| 1410 | .lparen = try appendToken(rp.c, .LParen, "("), | ||
| 1411 | .expr = expr, | ||
| 1412 | .rparen = try appendToken(rp.c, .RParen, ")"), | ||
| 1413 | }; | ||
| 1414 | return maybeSuppressResult(rp, scope, result_used, &node.base); | ||
| 1415 | }, | 1071 | }, |
| 1416 | else => { | 1072 | else => { |
| 1417 | return revertAndWarn( | 1073 | return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)}); |
| 1418 | rp, | ||
| 1419 | error.UnsupportedTranslation, | ||
| 1420 | stmt.getBeginLoc(), | ||
| 1421 | "TODO implement translation of stmt class {s}", | ||
| 1422 | .{@tagName(sc)}, | ||
| 1423 | ); | ||
| 1424 | }, | 1074 | }, |
| 1425 | } | 1075 | } |
| 1426 | } | 1076 | } |
| 1427 | 1077 | ||
| 1428 | fn transBinaryOperator( | 1078 | fn transBinaryOperator( |
| 1429 | rp: RestorePoint, | 1079 | c: *Context, |
| 1430 | scope: *Scope, | 1080 | scope: *Scope, |
| 1431 | stmt: *const clang.BinaryOperator, | 1081 | stmt: *const clang.BinaryOperator, |
| 1432 | result_used: ResultUsed, | 1082 | result_used: ResultUsed, |
| 1433 | ) TransError!*ast.Node { | 1083 | ) TransError!Node { |
| 1434 | const op = stmt.getOpcode(); | 1084 | const op = stmt.getOpcode(); |
| 1435 | const qt = stmt.getType(); | 1085 | const qt = stmt.getType(); |
| 1436 | var op_token: ast.TokenIndex = undefined; | ||
| 1437 | var op_id: ast.Node.Tag = undefined; | ||
| 1438 | switch (op) { | 1086 | switch (op) { |
| 1439 | .Assign => return try transCreateNodeAssign(rp, scope, result_used, stmt.getLHS(), stmt.getRHS()), | 1087 | .Assign => return try transCreateNodeAssign(c, scope, result_used, stmt.getLHS(), stmt.getRHS()), |
| 1440 | .Comma => { | 1088 | .Comma => { |
| 1441 | var block_scope = try Scope.Block.init(rp.c, scope, true); | 1089 | var block_scope = try Scope.Block.init(c, scope, true); |
| 1442 | const lparen = try appendToken(rp.c, .LParen, "("); | 1090 | defer block_scope.deinit(); |
| 1443 | 1091 | ||
| 1444 | const lhs = try transExpr(rp, &block_scope.base, stmt.getLHS(), .unused, .r_value); | 1092 | const lhs = try transExpr(c, &block_scope.base, stmt.getLHS(), .unused); |
| 1445 | try block_scope.statements.append(lhs); | 1093 | try block_scope.statements.append(lhs); |
| 1446 | 1094 | ||
| 1447 | const rhs = try transExpr(rp, &block_scope.base, stmt.getRHS(), .used, .r_value); | 1095 | const rhs = try transExpr(c, &block_scope.base, stmt.getRHS(), .used); |
| 1448 | _ = try appendToken(rp.c, .Semicolon, ";"); | 1096 | const break_node = try Tag.break_val.create(c.arena, .{ |
| 1449 | const break_node = try transCreateNodeBreak(rp.c, block_scope.label, rhs); | 1097 | .label = block_scope.label, |
| 1450 | try block_scope.statements.append(&break_node.base); | 1098 | .val = rhs, |
| 1451 | const block_node = try block_scope.complete(rp.c); | 1099 | }); |
| 1452 | const rparen = try appendToken(rp.c, .RParen, ")"); | 1100 | try block_scope.statements.append(break_node); |
| 1453 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); | 1101 | const block_node = try block_scope.complete(c); |
| 1454 | grouped_expr.* = .{ | 1102 | return maybeSuppressResult(c, scope, result_used, block_node); |
| 1455 | .lparen = lparen, | ||
| 1456 | .expr = block_node, | ||
| 1457 | .rparen = rparen, | ||
| 1458 | }; | ||
| 1459 | return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base); | ||
| 1460 | }, | 1103 | }, |
| 1461 | .Div => { | 1104 | .Div => { |
| 1462 | if (cIsSignedInteger(qt)) { | 1105 | if (cIsSignedInteger(qt)) { |
| 1463 | // signed integer division uses @divTrunc | 1106 | // signed integer division uses @divTrunc |
| 1464 | const div_trunc_node = try rp.c.createBuiltinCall("@divTrunc", 2); | 1107 | const lhs = try transExpr(c, scope, stmt.getLHS(), .used); |
| 1465 | div_trunc_node.params()[0] = try transExpr(rp, scope, stmt.getLHS(), .used, .l_value); | 1108 | const rhs = try transExpr(c, scope, stmt.getRHS(), .used); |
| 1466 | _ = try appendToken(rp.c, .Comma, ","); | 1109 | const div_trunc = try Tag.div_trunc.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); |
| 1467 | const rhs = try transExpr(rp, scope, stmt.getRHS(), .used, .r_value); | 1110 | return maybeSuppressResult(c, scope, result_used, div_trunc); |
| 1468 | div_trunc_node.params()[1] = rhs; | ||
| 1469 | div_trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 1470 | return maybeSuppressResult(rp, scope, result_used, &div_trunc_node.base); | ||
| 1471 | } | 1111 | } |
| 1472 | }, | 1112 | }, |
| 1473 | .Rem => { | 1113 | .Rem => { |
| 1474 | if (cIsSignedInteger(qt)) { | 1114 | if (cIsSignedInteger(qt)) { |
| 1475 | // signed integer division uses @rem | 1115 | // signed integer division uses @rem |
| 1476 | const rem_node = try rp.c.createBuiltinCall("@rem", 2); | 1116 | const lhs = try transExpr(c, scope, stmt.getLHS(), .used); |
| 1477 | rem_node.params()[0] = try transExpr(rp, scope, stmt.getLHS(), .used, .l_value); | 1117 | const rhs = try transExpr(c, scope, stmt.getRHS(), .used); |
| 1478 | _ = try appendToken(rp.c, .Comma, ","); | 1118 | const rem = try Tag.rem.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); |
| 1479 | const rhs = try transExpr(rp, scope, stmt.getRHS(), .used, .r_value); | 1119 | return maybeSuppressResult(c, scope, result_used, rem); |
| 1480 | rem_node.params()[1] = rhs; | ||
| 1481 | rem_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 1482 | return maybeSuppressResult(rp, scope, result_used, &rem_node.base); | ||
| 1483 | } | 1120 | } |
| 1484 | }, | 1121 | }, |
| 1485 | .Shl => { | 1122 | .Shl => { |
| 1486 | const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<"); | 1123 | return transCreateNodeShiftOp(c, scope, stmt, .shl, result_used); |
| 1487 | return maybeSuppressResult(rp, scope, result_used, node); | ||
| 1488 | }, | 1124 | }, |
| 1489 | .Shr => { | 1125 | .Shr => { |
| 1490 | const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftRight, .AngleBracketAngleBracketRight, ">>"); | 1126 | return transCreateNodeShiftOp(c, scope, stmt, .shr, result_used); |
| 1491 | return maybeSuppressResult(rp, scope, result_used, node); | ||
| 1492 | }, | 1127 | }, |
| 1493 | .LAnd => { | 1128 | .LAnd => { |
| 1494 | const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolAnd, result_used, true); | 1129 | return transCreateNodeBoolInfixOp(c, scope, stmt, .@"and", result_used); |
| 1495 | return maybeSuppressResult(rp, scope, result_used, node); | ||
| 1496 | }, | 1130 | }, |
| 1497 | .LOr => { | 1131 | .LOr => { |
| 1498 | const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolOr, result_used, true); | 1132 | return transCreateNodeBoolInfixOp(c, scope, stmt, .@"or", result_used); |
| 1499 | return maybeSuppressResult(rp, scope, result_used, node); | ||
| 1500 | }, | 1133 | }, |
| 1501 | else => {}, | 1134 | else => {}, |
| 1502 | } | 1135 | } |
| 1503 | const lhs_node = try transExpr(rp, scope, stmt.getLHS(), .used, .l_value); | 1136 | var op_id: Tag = undefined; |
| 1504 | switch (op) { | 1137 | switch (op) { |
| 1505 | .Add => { | 1138 | .Add => { |
| 1506 | if (cIsUnsignedInteger(qt)) { | 1139 | if (cIsUnsignedInteger(qt)) { |
| 1507 | op_token = try appendToken(rp.c, .PlusPercent, "+%"); | 1140 | op_id = .add_wrap; |
| 1508 | op_id = .AddWrap; | ||
| 1509 | } else { | 1141 | } else { |
| 1510 | op_token = try appendToken(rp.c, .Plus, "+"); | 1142 | op_id = .add; |
| 1511 | op_id = .Add; | ||
| 1512 | } | 1143 | } |
| 1513 | }, | 1144 | }, |
| 1514 | .Sub => { | 1145 | .Sub => { |
| 1515 | if (cIsUnsignedInteger(qt)) { | 1146 | if (cIsUnsignedInteger(qt)) { |
| 1516 | op_token = try appendToken(rp.c, .MinusPercent, "-%"); | 1147 | op_id = .sub_wrap; |
| 1517 | op_id = .SubWrap; | ||
| 1518 | } else { | 1148 | } else { |
| 1519 | op_token = try appendToken(rp.c, .Minus, "-"); | 1149 | op_id = .sub; |
| 1520 | op_id = .Sub; | ||
| 1521 | } | 1150 | } |
| 1522 | }, | 1151 | }, |
| 1523 | .Mul => { | 1152 | .Mul => { |
| 1524 | if (cIsUnsignedInteger(qt)) { | 1153 | if (cIsUnsignedInteger(qt)) { |
| 1525 | op_token = try appendToken(rp.c, .AsteriskPercent, "*%"); | 1154 | op_id = .mul_wrap; |
| 1526 | op_id = .MulWrap; | ||
| 1527 | } else { | 1155 | } else { |
| 1528 | op_token = try appendToken(rp.c, .Asterisk, "*"); | 1156 | op_id = .mul; |
| 1529 | op_id = .Mul; | ||
| 1530 | } | 1157 | } |
| 1531 | }, | 1158 | }, |
| 1532 | .Div => { | 1159 | .Div => { |
| 1533 | // unsigned/float division uses the operator | 1160 | // unsigned/float division uses the operator |
| 1534 | op_id = .Div; | 1161 | op_id = .div; |
| 1535 | op_token = try appendToken(rp.c, .Slash, "/"); | ||
| 1536 | }, | 1162 | }, |
| 1537 | .Rem => { | 1163 | .Rem => { |
| 1538 | // unsigned/float division uses the operator | 1164 | // unsigned/float division uses the operator |
| 1539 | op_id = .Mod; | 1165 | op_id = .mod; |
| 1540 | op_token = try appendToken(rp.c, .Percent, "%"); | ||
| 1541 | }, | 1166 | }, |
| 1542 | .LT => { | 1167 | .LT => { |
| 1543 | op_id = .LessThan; | 1168 | op_id = .less_than; |
| 1544 | op_token = try appendToken(rp.c, .AngleBracketLeft, "<"); | ||
| 1545 | }, | 1169 | }, |
| 1546 | .GT => { | 1170 | .GT => { |
| 1547 | op_id = .GreaterThan; | 1171 | op_id = .greater_than; |
| 1548 | op_token = try appendToken(rp.c, .AngleBracketRight, ">"); | ||
| 1549 | }, | 1172 | }, |
| 1550 | .LE => { | 1173 | .LE => { |
| 1551 | op_id = .LessOrEqual; | 1174 | op_id = .less_than_equal; |
| 1552 | op_token = try appendToken(rp.c, .AngleBracketLeftEqual, "<="); | ||
| 1553 | }, | 1175 | }, |
| 1554 | .GE => { | 1176 | .GE => { |
| 1555 | op_id = .GreaterOrEqual; | 1177 | op_id = .greater_than_equal; |
| 1556 | op_token = try appendToken(rp.c, .AngleBracketRightEqual, ">="); | ||
| 1557 | }, | 1178 | }, |
| 1558 | .EQ => { | 1179 | .EQ => { |
| 1559 | op_id = .EqualEqual; | 1180 | op_id = .equal; |
| 1560 | op_token = try appendToken(rp.c, .EqualEqual, "=="); | ||
| 1561 | }, | 1181 | }, |
| 1562 | .NE => { | 1182 | .NE => { |
| 1563 | op_id = .BangEqual; | 1183 | op_id = .not_equal; |
| 1564 | op_token = try appendToken(rp.c, .BangEqual, "!="); | ||
| 1565 | }, | 1184 | }, |
| 1566 | .And => { | 1185 | .And => { |
| 1567 | op_id = .BitAnd; | 1186 | op_id = .bit_and; |
| 1568 | op_token = try appendToken(rp.c, .Ampersand, "&"); | ||
| 1569 | }, | 1187 | }, |
| 1570 | .Xor => { | 1188 | .Xor => { |
| 1571 | op_id = .BitXor; | 1189 | op_id = .bit_xor; |
| 1572 | op_token = try appendToken(rp.c, .Caret, "^"); | ||
| 1573 | }, | 1190 | }, |
| 1574 | .Or => { | 1191 | .Or => { |
| 1575 | op_id = .BitOr; | 1192 | op_id = .bit_or; |
| 1576 | op_token = try appendToken(rp.c, .Pipe, "|"); | ||
| 1577 | }, | 1193 | }, |
| 1578 | else => unreachable, | 1194 | else => unreachable, |
| 1579 | } | 1195 | } |
| 1580 | 1196 | ||
| 1581 | const rhs_node = try transExpr(rp, scope, stmt.getRHS(), .used, .r_value); | 1197 | const lhs_uncasted = try transExpr(c, scope, stmt.getLHS(), .used); |
| 1198 | const rhs_uncasted = try transExpr(c, scope, stmt.getRHS(), .used); | ||
| 1582 | 1199 | ||
| 1583 | const lhs = if (isBoolRes(lhs_node)) init: { | 1200 | const lhs = if (isBoolRes(lhs_uncasted)) |
| 1584 | const cast_node = try rp.c.createBuiltinCall("@boolToInt", 1); | 1201 | try Tag.bool_to_int.create(c.arena, lhs_uncasted) |
| 1585 | cast_node.params()[0] = lhs_node; | 1202 | else |
| 1586 | cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | 1203 | lhs_uncasted; |
| 1587 | break :init &cast_node.base; | ||
| 1588 | } else lhs_node; | ||
| 1589 | 1204 | ||
| 1590 | const rhs = if (isBoolRes(rhs_node)) init: { | 1205 | const rhs = if (isBoolRes(rhs_uncasted)) |
| 1591 | const cast_node = try rp.c.createBuiltinCall("@boolToInt", 1); | 1206 | try Tag.bool_to_int.create(c.arena, rhs_uncasted) |
| 1592 | cast_node.params()[0] = rhs_node; | 1207 | else |
| 1593 | cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | 1208 | rhs_uncasted; |
| 1594 | break :init &cast_node.base; | ||
| 1595 | } else rhs_node; | ||
| 1596 | 1209 | ||
| 1597 | return transCreateNodeInfixOp(rp, scope, lhs, op_id, op_token, rhs, result_used, true); | 1210 | return transCreateNodeInfixOp(c, scope, op_id, lhs, rhs, result_used); |
| 1598 | } | 1211 | } |
| 1599 | 1212 | ||
| 1600 | fn transCompoundStmtInline( | 1213 | fn transCompoundStmtInline( |
| 1601 | rp: RestorePoint, | 1214 | c: *Context, |
| 1602 | parent_scope: *Scope, | ||
| 1603 | stmt: *const clang.CompoundStmt, | 1215 | stmt: *const clang.CompoundStmt, |
| 1604 | block: *Scope.Block, | 1216 | block: *Scope.Block, |
| 1605 | ) TransError!void { | 1217 | ) TransError!void { |
| 1606 | var it = stmt.body_begin(); | 1218 | var it = stmt.body_begin(); |
| 1607 | const end_it = stmt.body_end(); | 1219 | const end_it = stmt.body_end(); |
| 1608 | while (it != end_it) : (it += 1) { | 1220 | while (it != end_it) : (it += 1) { |
| 1609 | const result = try transStmt(rp, parent_scope, it[0], .unused, .r_value); | 1221 | const result = try transStmt(c, &block.base, it[0], .unused); |
| 1610 | try block.statements.append(result); | 1222 | switch (result.tag()) { |
| 1223 | .declaration, .empty_block => {}, | ||
| 1224 | else => try block.statements.append(result), | ||
| 1225 | } | ||
| 1611 | } | 1226 | } |
| 1612 | } | 1227 | } |
| 1613 | 1228 | ||
| 1614 | fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const clang.CompoundStmt) TransError!*ast.Node { | 1229 | fn transCompoundStmt(c: *Context, scope: *Scope, stmt: *const clang.CompoundStmt) TransError!Node { |
| 1615 | var block_scope = try Scope.Block.init(rp.c, scope, false); | 1230 | var block_scope = try Scope.Block.init(c, scope, false); |
| 1616 | defer block_scope.deinit(); | 1231 | defer block_scope.deinit(); |
| 1617 | try transCompoundStmtInline(rp, &block_scope.base, stmt, &block_scope); | 1232 | try transCompoundStmtInline(c, stmt, &block_scope); |
| 1618 | return try block_scope.complete(rp.c); | 1233 | return try block_scope.complete(c); |
| 1619 | } | 1234 | } |
| 1620 | 1235 | ||
| 1621 | fn transCStyleCastExprClass( | 1236 | fn transCStyleCastExprClass( |
| 1622 | rp: RestorePoint, | 1237 | c: *Context, |
| 1623 | scope: *Scope, | 1238 | scope: *Scope, |
| 1624 | stmt: *const clang.CStyleCastExpr, | 1239 | stmt: *const clang.CStyleCastExpr, |
| 1625 | result_used: ResultUsed, | 1240 | result_used: ResultUsed, |
| 1626 | lrvalue: LRValue, | 1241 | ) TransError!Node { |
| 1627 | ) TransError!*ast.Node { | ||
| 1628 | const sub_expr = stmt.getSubExpr(); | 1242 | const sub_expr = stmt.getSubExpr(); |
| 1629 | const cast_node = (try transCCast( | 1243 | const cast_node = (try transCCast( |
| 1630 | rp, | 1244 | c, |
| 1631 | scope, | 1245 | scope, |
| 1632 | stmt.getBeginLoc(), | 1246 | stmt.getBeginLoc(), |
| 1633 | stmt.getType(), | 1247 | stmt.getType(), |
| 1634 | sub_expr.getType(), | 1248 | sub_expr.getType(), |
| 1635 | try transExpr(rp, scope, sub_expr, .used, lrvalue), | 1249 | try transExpr(c, scope, sub_expr, .used), |
| 1636 | )); | 1250 | )); |
| 1637 | return maybeSuppressResult(rp, scope, result_used, cast_node); | 1251 | return maybeSuppressResult(c, scope, result_used, cast_node); |
| 1638 | } | 1252 | } |
| 1639 | 1253 | ||
| 1640 | fn transDeclStmtOne( | 1254 | fn transDeclStmtOne( |
| 1641 | rp: RestorePoint, | 1255 | c: *Context, |
| 1642 | scope: *Scope, | 1256 | scope: *Scope, |
| 1643 | decl: *const clang.Decl, | 1257 | decl: *const clang.Decl, |
| 1644 | block_scope: *Scope.Block, | 1258 | block_scope: *Scope.Block, |
| 1645 | ) TransError!*ast.Node { | 1259 | ) TransError!void { |
| 1646 | const c = rp.c; | ||
| 1647 | |||
| 1648 | switch (decl.getKind()) { | 1260 | switch (decl.getKind()) { |
| 1649 | .Var => { | 1261 | .Var => { |
| 1650 | const var_decl = @ptrCast(*const clang.VarDecl, decl); | 1262 | const var_decl = @ptrCast(*const clang.VarDecl, decl); |
| ... | @@ -1658,62 +1270,51 @@ fn transDeclStmtOne( | ... | @@ -1658,62 +1270,51 @@ fn transDeclStmtOne( |
| 1658 | .Extern, .Static => { | 1270 | .Extern, .Static => { |
| 1659 | // This is actually a global variable, put it in the global scope and reference it. | 1271 | // This is actually a global variable, put it in the global scope and reference it. |
| 1660 | // `_ = mangled_name;` | 1272 | // `_ = mangled_name;` |
| 1661 | try visitVarDecl(rp.c, var_decl, mangled_name); | 1273 | return visitVarDecl(c, var_decl, mangled_name); |
| 1662 | return try maybeSuppressResult(rp, scope, .unused, try transCreateNodeIdentifier(rp.c, mangled_name)); | ||
| 1663 | }, | 1274 | }, |
| 1664 | else => {}, | 1275 | else => {}, |
| 1665 | } | 1276 | } |
| 1666 | 1277 | ||
| 1667 | const mut_tok = if (qual_type.isConstQualified()) | 1278 | const is_const = qual_type.isConstQualified(); |
| 1668 | try appendToken(c, .Keyword_const, "const") | ||
| 1669 | else | ||
| 1670 | try appendToken(c, .Keyword_var, "var"); | ||
| 1671 | const name_tok = try appendIdentifier(c, mangled_name); | ||
| 1672 | 1279 | ||
| 1673 | _ = try appendToken(c, .Colon, ":"); | ||
| 1674 | const loc = decl.getLocation(); | 1280 | const loc = decl.getLocation(); |
| 1675 | const type_node = try transQualTypeMaybeInitialized(rp, qual_type, decl_init, loc); | 1281 | const type_node = try transQualTypeMaybeInitialized(c, scope, qual_type, decl_init, loc); |
| 1676 | 1282 | ||
| 1677 | const eq_token = try appendToken(c, .Equal, "="); | ||
| 1678 | var init_node = if (decl_init) |expr| | 1283 | var init_node = if (decl_init) |expr| |
| 1679 | if (expr.getStmtClass() == .StringLiteralClass) | 1284 | if (expr.getStmtClass() == .StringLiteralClass) |
| 1680 | try transStringLiteralAsArray(rp, scope, @ptrCast(*const clang.StringLiteral, expr), try zigArraySize(rp.c, type_node)) | 1285 | try transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), try zigArraySize(c, type_node)) |
| 1681 | else | 1286 | else |
| 1682 | try transExprCoercing(rp, scope, expr, .used, .r_value) | 1287 | try transExprCoercing(c, scope, expr, .used) |
| 1683 | else | 1288 | else |
| 1684 | try transCreateNodeUndefinedLiteral(c); | 1289 | Tag.undefined_literal.init(); |
| 1685 | if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) { | 1290 | if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) { |
| 1686 | const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1); | 1291 | init_node = try Tag.bool_to_int.create(c.arena, init_node); |
| 1687 | builtin_node.params()[0] = init_node; | ||
| 1688 | builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 1689 | init_node = &builtin_node.base; | ||
| 1690 | } | 1292 | } |
| 1691 | const semicolon_token = try appendToken(c, .Semicolon, ";"); | 1293 | const node = try Tag.var_decl.create(c.arena, .{ |
| 1692 | const node = try ast.Node.VarDecl.create(c.arena, .{ | 1294 | .is_pub = false, |
| 1693 | .name_token = name_tok, | 1295 | .is_const = is_const, |
| 1694 | .mut_token = mut_tok, | 1296 | .is_extern = false, |
| 1695 | .semicolon_token = semicolon_token, | 1297 | .is_export = false, |
| 1696 | }, .{ | 1298 | .is_threadlocal = false, |
| 1697 | .eq_token = eq_token, | 1299 | .linksection_string = null, |
| 1698 | .type_node = type_node, | 1300 | .alignment = null, |
| 1699 | .init_node = init_node, | 1301 | .name = mangled_name, |
| 1302 | .type = type_node, | ||
| 1303 | .init = init_node, | ||
| 1700 | }); | 1304 | }); |
| 1701 | return &node.base; | 1305 | try block_scope.statements.append(node); |
| 1702 | }, | 1306 | }, |
| 1703 | .Typedef => { | 1307 | .Typedef => { |
| 1704 | const typedef_decl = @ptrCast(*const clang.TypedefNameDecl, decl); | 1308 | try transTypeDef(c, scope, @ptrCast(*const clang.TypedefNameDecl, decl)); |
| 1705 | const name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin()); | 1309 | }, |
| 1706 | 1310 | .Record => { | |
| 1707 | const underlying_qual = typedef_decl.getUnderlyingType(); | 1311 | try transRecordDecl(c, scope, @ptrCast(*const clang.RecordDecl, decl)); |
| 1708 | const underlying_type = underlying_qual.getTypePtr(); | ||
| 1709 | |||
| 1710 | const mangled_name = try block_scope.makeMangledName(c, name); | ||
| 1711 | const node = (try transCreateNodeTypedef(rp, typedef_decl, false, mangled_name)) orelse | ||
| 1712 | return error.UnsupportedTranslation; | ||
| 1713 | return node; | ||
| 1714 | }, | 1312 | }, |
| 1715 | else => |kind| return revertAndWarn( | 1313 | .Enum => { |
| 1716 | rp, | 1314 | try transEnumDecl(c, scope, @ptrCast(*const clang.EnumDecl, decl)); |
| 1315 | }, | ||
| 1316 | else => |kind| return fail( | ||
| 1317 | c, | ||
| 1717 | error.UnsupportedTranslation, | 1318 | error.UnsupportedTranslation, |
| 1718 | decl.getLocation(), | 1319 | decl.getLocation(), |
| 1719 | "TODO implement translation of DeclStmt kind {s}", | 1320 | "TODO implement translation of DeclStmt kind {s}", |
| ... | @@ -1722,96 +1323,86 @@ fn transDeclStmtOne( | ... | @@ -1722,96 +1323,86 @@ fn transDeclStmtOne( |
| 1722 | } | 1323 | } |
| 1723 | } | 1324 | } |
| 1724 | 1325 | ||
| 1725 | fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const clang.DeclStmt) TransError!*ast.Node { | 1326 | fn transDeclStmt(c: *Context, scope: *Scope, stmt: *const clang.DeclStmt) TransError!Node { |
| 1726 | const block_scope = scope.findBlockScope(rp.c) catch unreachable; | 1327 | const block_scope = try scope.findBlockScope(c); |
| 1727 | 1328 | ||
| 1728 | var it = stmt.decl_begin(); | 1329 | var it = stmt.decl_begin(); |
| 1729 | const end_it = stmt.decl_end(); | 1330 | const end_it = stmt.decl_end(); |
| 1730 | assert(it != end_it); | 1331 | while (it != end_it) : (it += 1) { |
| 1731 | while (true) : (it += 1) { | 1332 | try transDeclStmtOne(c, scope, it[0], block_scope); |
| 1732 | const node = try transDeclStmtOne(rp, scope, it[0], block_scope); | ||
| 1733 | |||
| 1734 | if (it + 1 == end_it) { | ||
| 1735 | return node; | ||
| 1736 | } else { | ||
| 1737 | try block_scope.statements.append(node); | ||
| 1738 | } | ||
| 1739 | } | 1333 | } |
| 1740 | unreachable; | 1334 | return Tag.declaration.init(); |
| 1741 | } | 1335 | } |
| 1742 | 1336 | ||
| 1743 | fn transDeclRefExpr( | 1337 | fn transDeclRefExpr( |
| 1744 | rp: RestorePoint, | 1338 | c: *Context, |
| 1745 | scope: *Scope, | 1339 | scope: *Scope, |
| 1746 | expr: *const clang.DeclRefExpr, | 1340 | expr: *const clang.DeclRefExpr, |
| 1747 | lrvalue: LRValue, | 1341 | ) TransError!Node { |
| 1748 | ) TransError!*ast.Node { | ||
| 1749 | const value_decl = expr.getDecl(); | 1342 | const value_decl = expr.getDecl(); |
| 1750 | const name = try rp.c.str(@ptrCast(*const clang.NamedDecl, value_decl).getName_bytes_begin()); | 1343 | const name = try c.str(@ptrCast(*const clang.NamedDecl, value_decl).getName_bytes_begin()); |
| 1751 | const mangled_name = scope.getAlias(name); | 1344 | const mangled_name = scope.getAlias(name); |
| 1752 | return transCreateNodeIdentifier(rp.c, mangled_name); | 1345 | return Tag.identifier.create(c.arena, mangled_name); |
| 1753 | } | 1346 | } |
| 1754 | 1347 | ||
| 1755 | fn transImplicitCastExpr( | 1348 | fn transImplicitCastExpr( |
| 1756 | rp: RestorePoint, | 1349 | c: *Context, |
| 1757 | scope: *Scope, | 1350 | scope: *Scope, |
| 1758 | expr: *const clang.ImplicitCastExpr, | 1351 | expr: *const clang.ImplicitCastExpr, |
| 1759 | result_used: ResultUsed, | 1352 | result_used: ResultUsed, |
| 1760 | ) TransError!*ast.Node { | 1353 | ) TransError!Node { |
| 1761 | const c = rp.c; | ||
| 1762 | const sub_expr = expr.getSubExpr(); | 1354 | const sub_expr = expr.getSubExpr(); |
| 1763 | const dest_type = getExprQualType(c, @ptrCast(*const clang.Expr, expr)); | 1355 | const dest_type = getExprQualType(c, @ptrCast(*const clang.Expr, expr)); |
| 1764 | const src_type = getExprQualType(c, sub_expr); | 1356 | const src_type = getExprQualType(c, sub_expr); |
| 1765 | switch (expr.getCastKind()) { | 1357 | switch (expr.getCastKind()) { |
| 1766 | .BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast, .PointerToIntegral, .IntegralToPointer => { | 1358 | .BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast, .PointerToIntegral, .IntegralToPointer => { |
| 1767 | const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value); | 1359 | const sub_expr_node = try transExpr(c, scope, sub_expr, .used); |
| 1768 | return try transCCast(rp, scope, expr.getBeginLoc(), dest_type, src_type, sub_expr_node); | 1360 | const casted = try transCCast(c, scope, expr.getBeginLoc(), dest_type, src_type, sub_expr_node); |
| 1361 | return maybeSuppressResult(c, scope, result_used, casted); | ||
| 1769 | }, | 1362 | }, |
| 1770 | .LValueToRValue, .NoOp, .FunctionToPointerDecay => { | 1363 | .LValueToRValue, .NoOp, .FunctionToPointerDecay => { |
| 1771 | const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value); | 1364 | const sub_expr_node = try transExpr(c, scope, sub_expr, .used); |
| 1772 | return maybeSuppressResult(rp, scope, result_used, sub_expr_node); | 1365 | return maybeSuppressResult(c, scope, result_used, sub_expr_node); |
| 1773 | }, | 1366 | }, |
| 1774 | .ArrayToPointerDecay => { | 1367 | .ArrayToPointerDecay => { |
| 1775 | if (exprIsNarrowStringLiteral(sub_expr)) { | 1368 | if (exprIsNarrowStringLiteral(sub_expr)) { |
| 1776 | const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value); | 1369 | const sub_expr_node = try transExpr(c, scope, sub_expr, .used); |
| 1777 | return maybeSuppressResult(rp, scope, result_used, sub_expr_node); | 1370 | return maybeSuppressResult(c, scope, result_used, sub_expr_node); |
| 1778 | } | 1371 | } |
| 1779 | 1372 | ||
| 1780 | const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); | 1373 | const addr = try Tag.address_of.create(c.arena, try transExpr(c, scope, sub_expr, .used)); |
| 1781 | prefix_op.rhs = try transExpr(rp, scope, sub_expr, .used, .r_value); | 1374 | return maybeSuppressResult(c, scope, result_used, addr); |
| 1782 | |||
| 1783 | return maybeSuppressResult(rp, scope, result_used, &prefix_op.base); | ||
| 1784 | }, | 1375 | }, |
| 1785 | .NullToPointer => { | 1376 | .NullToPointer => { |
| 1786 | return try transCreateNodeNullLiteral(rp.c); | 1377 | return Tag.null_literal.init(); |
| 1787 | }, | 1378 | }, |
| 1788 | .PointerToBoolean => { | 1379 | .PointerToBoolean => { |
| 1789 | // @ptrToInt(val) != 0 | 1380 | // @ptrToInt(val) != 0 |
| 1790 | const ptr_to_int = try rp.c.createBuiltinCall("@ptrToInt", 1); | 1381 | const ptr_to_int = try Tag.ptr_to_int.create(c.arena, try transExpr(c, scope, sub_expr, .used)); |
| 1791 | ptr_to_int.params()[0] = try transExpr(rp, scope, sub_expr, .used, .r_value); | ||
| 1792 | ptr_to_int.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 1793 | 1382 | ||
| 1794 | const op_token = try appendToken(rp.c, .BangEqual, "!="); | 1383 | const ne = try Tag.not_equal.create(c.arena, .{ .lhs = ptr_to_int, .rhs = Tag.zero_literal.init() }); |
| 1795 | const rhs_node = try transCreateNodeInt(rp.c, 0); | 1384 | return maybeSuppressResult(c, scope, result_used, ne); |
| 1796 | return transCreateNodeInfixOp(rp, scope, &ptr_to_int.base, .BangEqual, op_token, rhs_node, result_used, false); | ||
| 1797 | }, | 1385 | }, |
| 1798 | .IntegralToBoolean => { | 1386 | .IntegralToBoolean => { |
| 1799 | const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value); | 1387 | const sub_expr_node = try transExpr(c, scope, sub_expr, .used); |
| 1800 | 1388 | ||
| 1801 | // The expression is already a boolean one, return it as-is | 1389 | // The expression is already a boolean one, return it as-is |
| 1802 | if (isBoolRes(sub_expr_node)) | 1390 | if (isBoolRes(sub_expr_node)) |
| 1803 | return sub_expr_node; | 1391 | return maybeSuppressResult(c, scope, result_used, sub_expr_node); |
| 1804 | 1392 | ||
| 1805 | // val != 0 | 1393 | // val != 0 |
| 1806 | const op_token = try appendToken(rp.c, .BangEqual, "!="); | 1394 | const ne = try Tag.not_equal.create(c.arena, .{ .lhs = sub_expr_node, .rhs = Tag.zero_literal.init() }); |
| 1807 | const rhs_node = try transCreateNodeInt(rp.c, 0); | 1395 | return maybeSuppressResult(c, scope, result_used, ne); |
| 1808 | return transCreateNodeInfixOp(rp, scope, sub_expr_node, .BangEqual, op_token, rhs_node, result_used, false); | ||
| 1809 | }, | 1396 | }, |
| 1810 | .BuiltinFnToFnPtr => { | 1397 | .BuiltinFnToFnPtr => { |
| 1811 | return transExpr(rp, scope, sub_expr, .used, .r_value); | 1398 | return transExpr(c, scope, sub_expr, result_used); |
| 1399 | }, | ||
| 1400 | .ToVoid => { | ||
| 1401 | // Should only appear in the rhs and lhs of a ConditionalOperator | ||
| 1402 | return transExpr(c, scope, sub_expr, .unused); | ||
| 1812 | }, | 1403 | }, |
| 1813 | else => |kind| return revertAndWarn( | 1404 | else => |kind| return fail( |
| 1814 | rp, | 1405 | c, |
| 1815 | error.UnsupportedTranslation, | 1406 | error.UnsupportedTranslation, |
| 1816 | @ptrCast(*const clang.Stmt, expr).getBeginLoc(), | 1407 | @ptrCast(*const clang.Stmt, expr).getBeginLoc(), |
| 1817 | "TODO implement translation of CastKind {s}", | 1408 | "TODO implement translation of CastKind {s}", |
| ... | @@ -1821,52 +1412,28 @@ fn transImplicitCastExpr( | ... | @@ -1821,52 +1412,28 @@ fn transImplicitCastExpr( |
| 1821 | } | 1412 | } |
| 1822 | 1413 | ||
| 1823 | fn transBoolExpr( | 1414 | fn transBoolExpr( |
| 1824 | rp: RestorePoint, | 1415 | c: *Context, |
| 1825 | scope: *Scope, | 1416 | scope: *Scope, |
| 1826 | expr: *const clang.Expr, | 1417 | expr: *const clang.Expr, |
| 1827 | used: ResultUsed, | 1418 | used: ResultUsed, |
| 1828 | lrvalue: LRValue, | 1419 | ) TransError!Node { |
| 1829 | grouped: bool, | ||
| 1830 | ) TransError!*ast.Node { | ||
| 1831 | if (@ptrCast(*const clang.Stmt, expr).getStmtClass() == .IntegerLiteralClass) { | 1420 | if (@ptrCast(*const clang.Stmt, expr).getStmtClass() == .IntegerLiteralClass) { |
| 1832 | var is_zero: bool = undefined; | 1421 | var is_zero: bool = undefined; |
| 1833 | if (!(@ptrCast(*const clang.IntegerLiteral, expr).isZero(&is_zero, rp.c.clang_context))) { | 1422 | if (!(@ptrCast(*const clang.IntegerLiteral, expr).isZero(&is_zero, c.clang_context))) { |
| 1834 | return revertAndWarn(rp, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{}); | 1423 | return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{}); |
| 1835 | } | 1424 | } |
| 1836 | return try transCreateNodeBoolLiteral(rp.c, !is_zero); | 1425 | return Node{ .tag_if_small_enough = @enumToInt(([2]Tag{ .true_literal, .false_literal })[@boolToInt(is_zero)]) }; |
| 1837 | } | 1426 | } |
| 1838 | 1427 | ||
| 1839 | const lparen = if (grouped) | 1428 | var res = try transExpr(c, scope, expr, used); |
| 1840 | try appendToken(rp.c, .LParen, "(") | ||
| 1841 | else | ||
| 1842 | undefined; | ||
| 1843 | var res = try transExpr(rp, scope, expr, used, lrvalue); | ||
| 1844 | |||
| 1845 | if (isBoolRes(res)) { | 1429 | if (isBoolRes(res)) { |
| 1846 | if (!grouped and res.tag == .GroupedExpression) { | 1430 | return maybeSuppressResult(c, scope, used, res); |
| 1847 | const group = @fieldParentPtr(ast.Node.GroupedExpression, "base", res); | ||
| 1848 | res = group.expr; | ||
| 1849 | // get zig fmt to work properly | ||
| 1850 | tokenSlice(rp.c, group.lparen)[0] = ')'; | ||
| 1851 | } | ||
| 1852 | return res; | ||
| 1853 | } | 1431 | } |
| 1854 | 1432 | ||
| 1855 | const ty = getExprQualType(rp.c, expr).getTypePtr(); | 1433 | const ty = getExprQualType(c, expr).getTypePtr(); |
| 1856 | const node = try finishBoolExpr(rp, scope, expr.getBeginLoc(), ty, res, used); | 1434 | const node = try finishBoolExpr(c, scope, expr.getBeginLoc(), ty, res, used); |
| 1857 | 1435 | ||
| 1858 | if (grouped) { | 1436 | return maybeSuppressResult(c, scope, used, node); |
| 1859 | const rparen = try appendToken(rp.c, .RParen, ")"); | ||
| 1860 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); | ||
| 1861 | grouped_expr.* = .{ | ||
| 1862 | .lparen = lparen, | ||
| 1863 | .expr = node, | ||
| 1864 | .rparen = rparen, | ||
| 1865 | }; | ||
| 1866 | return maybeSuppressResult(rp, scope, used, &grouped_expr.base); | ||
| 1867 | } else { | ||
| 1868 | return maybeSuppressResult(rp, scope, used, node); | ||
| 1869 | } | ||
| 1870 | } | 1437 | } |
| 1871 | 1438 | ||
| 1872 | fn exprIsBooleanType(expr: *const clang.Expr) bool { | 1439 | fn exprIsBooleanType(expr: *const clang.Expr) bool { |
| ... | @@ -1892,34 +1459,32 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool { | ... | @@ -1892,34 +1459,32 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool { |
| 1892 | } | 1459 | } |
| 1893 | } | 1460 | } |
| 1894 | 1461 | ||
| 1895 | fn isBoolRes(res: *ast.Node) bool { | 1462 | fn isBoolRes(res: Node) bool { |
| 1896 | switch (res.tag) { | 1463 | switch (res.tag()) { |
| 1897 | .BoolOr, | 1464 | .@"or", |
| 1898 | .BoolAnd, | 1465 | .@"and", |
| 1899 | .EqualEqual, | 1466 | .equal, |
| 1900 | .BangEqual, | 1467 | .not_equal, |
| 1901 | .LessThan, | 1468 | .less_than, |
| 1902 | .GreaterThan, | 1469 | .less_than_equal, |
| 1903 | .LessOrEqual, | 1470 | .greater_than, |
| 1904 | .GreaterOrEqual, | 1471 | .greater_than_equal, |
| 1905 | .BoolNot, | 1472 | .not, |
| 1906 | .BoolLiteral, | 1473 | .false_literal, |
| 1474 | .true_literal, | ||
| 1907 | => return true, | 1475 | => return true, |
| 1908 | |||
| 1909 | .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr), | ||
| 1910 | |||
| 1911 | else => return false, | 1476 | else => return false, |
| 1912 | } | 1477 | } |
| 1913 | } | 1478 | } |
| 1914 | 1479 | ||
| 1915 | fn finishBoolExpr( | 1480 | fn finishBoolExpr( |
| 1916 | rp: RestorePoint, | 1481 | c: *Context, |
| 1917 | scope: *Scope, | 1482 | scope: *Scope, |
| 1918 | loc: clang.SourceLocation, | 1483 | loc: clang.SourceLocation, |
| 1919 | ty: *const clang.Type, | 1484 | ty: *const clang.Type, |
| 1920 | node: *ast.Node, | 1485 | node: Node, |
| 1921 | used: ResultUsed, | 1486 | used: ResultUsed, |
| 1922 | ) TransError!*ast.Node { | 1487 | ) TransError!Node { |
| 1923 | switch (ty.getTypeClass()) { | 1488 | switch (ty.getTypeClass()) { |
| 1924 | .Builtin => { | 1489 | .Builtin => { |
| 1925 | const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); | 1490 | const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); |
| ... | @@ -1951,42 +1516,38 @@ fn finishBoolExpr( | ... | @@ -1951,42 +1516,38 @@ fn finishBoolExpr( |
| 1951 | .WChar_S, | 1516 | .WChar_S, |
| 1952 | .Float16, | 1517 | .Float16, |
| 1953 | => { | 1518 | => { |
| 1954 | const op_token = try appendToken(rp.c, .BangEqual, "!="); | 1519 | // node != 0 |
| 1955 | const rhs_node = try transCreateNodeInt(rp.c, 0); | 1520 | return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() }); |
| 1956 | return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false); | ||
| 1957 | }, | 1521 | }, |
| 1958 | .NullPtr => { | 1522 | .NullPtr => { |
| 1959 | const op_token = try appendToken(rp.c, .EqualEqual, "=="); | 1523 | // node == null |
| 1960 | const rhs_node = try transCreateNodeNullLiteral(rp.c); | 1524 | return Tag.equal.create(c.arena, .{ .lhs = node, .rhs = Tag.null_literal.init() }); |
| 1961 | return transCreateNodeInfixOp(rp, scope, node, .EqualEqual, op_token, rhs_node, used, false); | ||
| 1962 | }, | 1525 | }, |
| 1963 | else => {}, | 1526 | else => {}, |
| 1964 | } | 1527 | } |
| 1965 | }, | 1528 | }, |
| 1966 | .Pointer => { | 1529 | .Pointer => { |
| 1967 | const op_token = try appendToken(rp.c, .BangEqual, "!="); | 1530 | // node != null |
| 1968 | const rhs_node = try transCreateNodeNullLiteral(rp.c); | 1531 | return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.null_literal.init() }); |
| 1969 | return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false); | ||
| 1970 | }, | 1532 | }, |
| 1971 | .Typedef => { | 1533 | .Typedef => { |
| 1972 | const typedef_ty = @ptrCast(*const clang.TypedefType, ty); | 1534 | const typedef_ty = @ptrCast(*const clang.TypedefType, ty); |
| 1973 | const typedef_decl = typedef_ty.getDecl(); | 1535 | const typedef_decl = typedef_ty.getDecl(); |
| 1974 | const underlying_type = typedef_decl.getUnderlyingType(); | 1536 | const underlying_type = typedef_decl.getUnderlyingType(); |
| 1975 | return finishBoolExpr(rp, scope, loc, underlying_type.getTypePtr(), node, used); | 1537 | return finishBoolExpr(c, scope, loc, underlying_type.getTypePtr(), node, used); |
| 1976 | }, | 1538 | }, |
| 1977 | .Enum => { | 1539 | .Enum => { |
| 1978 | const op_token = try appendToken(rp.c, .BangEqual, "!="); | 1540 | // node != 0 |
| 1979 | const rhs_node = try transCreateNodeInt(rp.c, 0); | 1541 | return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() }); |
| 1980 | return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false); | ||
| 1981 | }, | 1542 | }, |
| 1982 | .Elaborated => { | 1543 | .Elaborated => { |
| 1983 | const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty); | 1544 | const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty); |
| 1984 | const named_type = elaborated_ty.getNamedType(); | 1545 | const named_type = elaborated_ty.getNamedType(); |
| 1985 | return finishBoolExpr(rp, scope, loc, named_type.getTypePtr(), node, used); | 1546 | return finishBoolExpr(c, scope, loc, named_type.getTypePtr(), node, used); |
| 1986 | }, | 1547 | }, |
| 1987 | else => {}, | 1548 | else => {}, |
| 1988 | } | 1549 | } |
| 1989 | return revertAndWarn(rp, error.UnsupportedType, loc, "unsupported bool expression type", .{}); | 1550 | return fail(c, error.UnsupportedType, loc, "unsupported bool expression type", .{}); |
| 1990 | } | 1551 | } |
| 1991 | 1552 | ||
| 1992 | const SuppressCast = enum { | 1553 | const SuppressCast = enum { |
| ... | @@ -1994,21 +1555,21 @@ const SuppressCast = enum { | ... | @@ -1994,21 +1555,21 @@ const SuppressCast = enum { |
| 1994 | no_as, | 1555 | no_as, |
| 1995 | }; | 1556 | }; |
| 1996 | fn transIntegerLiteral( | 1557 | fn transIntegerLiteral( |
| 1997 | rp: RestorePoint, | 1558 | c: *Context, |
| 1998 | scope: *Scope, | 1559 | scope: *Scope, |
| 1999 | expr: *const clang.IntegerLiteral, | 1560 | expr: *const clang.IntegerLiteral, |
| 2000 | result_used: ResultUsed, | 1561 | result_used: ResultUsed, |
| 2001 | suppress_as: SuppressCast, | 1562 | suppress_as: SuppressCast, |
| 2002 | ) TransError!*ast.Node { | 1563 | ) TransError!Node { |
| 2003 | var eval_result: clang.ExprEvalResult = undefined; | 1564 | var eval_result: clang.ExprEvalResult = undefined; |
| 2004 | if (!expr.EvaluateAsInt(&eval_result, rp.c.clang_context)) { | 1565 | if (!expr.EvaluateAsInt(&eval_result, c.clang_context)) { |
| 2005 | const loc = expr.getBeginLoc(); | 1566 | const loc = expr.getBeginLoc(); |
| 2006 | return revertAndWarn(rp, error.UnsupportedTranslation, loc, "invalid integer literal", .{}); | 1567 | return fail(c, error.UnsupportedTranslation, loc, "invalid integer literal", .{}); |
| 2007 | } | 1568 | } |
| 2008 | 1569 | ||
| 2009 | if (suppress_as == .no_as) { | 1570 | if (suppress_as == .no_as) { |
| 2010 | const int_lit_node = try transCreateNodeAPInt(rp.c, eval_result.Val.getInt()); | 1571 | const int_lit_node = try transCreateNodeAPInt(c, eval_result.Val.getInt()); |
| 2011 | return maybeSuppressResult(rp, scope, result_used, int_lit_node); | 1572 | return maybeSuppressResult(c, scope, result_used, int_lit_node); |
| 2012 | } | 1573 | } |
| 2013 | 1574 | ||
| 2014 | // Integer literals in C have types, and this can matter for several reasons. | 1575 | // Integer literals in C have types, and this can matter for several reasons. |
| ... | @@ -2023,115 +1584,61 @@ fn transIntegerLiteral( | ... | @@ -2023,115 +1584,61 @@ fn transIntegerLiteral( |
| 2023 | 1584 | ||
| 2024 | // @as(T, x) | 1585 | // @as(T, x) |
| 2025 | const expr_base = @ptrCast(*const clang.Expr, expr); | 1586 | const expr_base = @ptrCast(*const clang.Expr, expr); |
| 2026 | const as_node = try rp.c.createBuiltinCall("@as", 2); | 1587 | const ty_node = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()); |
| 2027 | const ty_node = try transQualType(rp, expr_base.getType(), expr_base.getBeginLoc()); | 1588 | const rhs = try transCreateNodeAPInt(c, eval_result.Val.getInt()); |
| 2028 | as_node.params()[0] = ty_node; | 1589 | const as = try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = rhs }); |
| 2029 | _ = try appendToken(rp.c, .Comma, ","); | 1590 | return maybeSuppressResult(c, scope, result_used, as); |
| 2030 | as_node.params()[1] = try transCreateNodeAPInt(rp.c, eval_result.Val.getInt()); | ||
| 2031 | |||
| 2032 | as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 2033 | return maybeSuppressResult(rp, scope, result_used, &as_node.base); | ||
| 2034 | } | ||
| 2035 | |||
| 2036 | /// In C if a function has return type `int` and the return value is a boolean | ||
| 2037 | /// expression, there is no implicit cast. So the translated Zig will need to | ||
| 2038 | /// call @boolToInt | ||
| 2039 | fn zigShouldCastBooleanReturnToInt(node: ?*ast.Node, qt: ?clang.QualType) bool { | ||
| 2040 | if (node == null or qt == null) return false; | ||
| 2041 | return isBoolRes(node.?) and cIsNativeInt(qt.?); | ||
| 2042 | } | 1591 | } |
| 2043 | 1592 | ||
| 2044 | fn transReturnStmt( | 1593 | fn transReturnStmt( |
| 2045 | rp: RestorePoint, | 1594 | c: *Context, |
| 2046 | scope: *Scope, | 1595 | scope: *Scope, |
| 2047 | expr: *const clang.ReturnStmt, | 1596 | expr: *const clang.ReturnStmt, |
| 2048 | ) TransError!*ast.Node { | 1597 | ) TransError!Node { |
| 2049 | const return_kw = try appendToken(rp.c, .Keyword_return, "return"); | 1598 | const val_expr = expr.getRetValue() orelse |
| 2050 | var rhs: ?*ast.Node = if (expr.getRetValue()) |val_expr| | 1599 | return Tag.return_void.init(); |
| 2051 | try transExprCoercing(rp, scope, val_expr, .used, .r_value) | 1600 | |
| 2052 | else | 1601 | var rhs = try transExprCoercing(c, scope, val_expr, .used); |
| 2053 | null; | 1602 | const return_qt = scope.findBlockReturnType(c); |
| 2054 | const return_qt = scope.findBlockReturnType(rp.c); | 1603 | if (isBoolRes(rhs) and !qualTypeIsBoolean(return_qt)) { |
| 2055 | if (zigShouldCastBooleanReturnToInt(rhs, return_qt)) { | 1604 | rhs = try Tag.bool_to_int.create(c.arena, rhs); |
| 2056 | const bool_to_int_node = try rp.c.createBuiltinCall("@boolToInt", 1); | 1605 | } |
| 2057 | bool_to_int_node.params()[0] = rhs.?; | 1606 | return Tag.@"return".create(c.arena, rhs); |
| 2058 | bool_to_int_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 2059 | |||
| 2060 | rhs = &bool_to_int_node.base; | ||
| 2061 | } | ||
| 2062 | const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{ | ||
| 2063 | .ltoken = return_kw, | ||
| 2064 | .tag = .Return, | ||
| 2065 | }, .{ | ||
| 2066 | .rhs = rhs, | ||
| 2067 | }); | ||
| 2068 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 2069 | return &return_expr.base; | ||
| 2070 | } | 1607 | } |
| 2071 | 1608 | ||
| 2072 | fn transStringLiteral( | 1609 | fn transStringLiteral( |
| 2073 | rp: RestorePoint, | 1610 | c: *Context, |
| 2074 | scope: *Scope, | 1611 | scope: *Scope, |
| 2075 | stmt: *const clang.StringLiteral, | 1612 | stmt: *const clang.StringLiteral, |
| 2076 | result_used: ResultUsed, | 1613 | result_used: ResultUsed, |
| 2077 | ) TransError!*ast.Node { | 1614 | ) TransError!Node { |
| 2078 | const kind = stmt.getKind(); | 1615 | const kind = stmt.getKind(); |
| 2079 | switch (kind) { | 1616 | switch (kind) { |
| 2080 | .Ascii, .UTF8 => { | 1617 | .Ascii, .UTF8 => { |
| 2081 | var len: usize = undefined; | 1618 | var len: usize = undefined; |
| 2082 | const bytes_ptr = stmt.getString_bytes_begin_size(&len); | 1619 | const bytes_ptr = stmt.getString_bytes_begin_size(&len); |
| 2083 | const str = bytes_ptr[0..len]; | ||
| 2084 | 1620 | ||
| 2085 | const token = try appendTokenFmt(rp.c, .StringLiteral, "\"{}\"", .{std.zig.fmtEscapes(str)}); | 1621 | const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])}); |
| 2086 | const node = try rp.c.arena.create(ast.Node.OneToken); | 1622 | const node = try Tag.string_literal.create(c.arena, str); |
| 2087 | node.* = .{ | 1623 | return maybeSuppressResult(c, scope, result_used, node); |
| 2088 | .base = .{ .tag = .StringLiteral }, | ||
| 2089 | .token = token, | ||
| 2090 | }; | ||
| 2091 | return maybeSuppressResult(rp, scope, result_used, &node.base); | ||
| 2092 | }, | 1624 | }, |
| 2093 | .UTF16, .UTF32, .Wide => { | 1625 | .UTF16, .UTF32, .Wide => { |
| 2094 | const node = try transWideStringLiteral(rp, scope, stmt); | 1626 | const str_type = @tagName(stmt.getKind()); |
| 2095 | return maybeSuppressResult(rp, scope, result_used, node); | 1627 | const name = try std.fmt.allocPrint(c.arena, "zig.{s}_string_{d}", .{ str_type, c.getMangle() }); |
| 1628 | const lit_array = try transStringLiteralAsArray(c, scope, stmt, stmt.getLength() + 1); | ||
| 1629 | |||
| 1630 | const decl = try Tag.var_simple.create(c.arena, .{ .name = name, .init = lit_array }); | ||
| 1631 | try scope.appendNode(decl); | ||
| 1632 | const node = try Tag.identifier.create(c.arena, name); | ||
| 1633 | return maybeSuppressResult(c, scope, result_used, node); | ||
| 2096 | }, | 1634 | }, |
| 2097 | } | 1635 | } |
| 2098 | } | 1636 | } |
| 2099 | 1637 | ||
| 2100 | /// Translates a wide string literal as a global "anonymous" array of the relevant-sized | ||
| 2101 | /// integer type + null terminator, and returns an identifier node for it | ||
| 2102 | fn transWideStringLiteral(rp: RestorePoint, scope: *Scope, stmt: *const clang.StringLiteral) TransError!*ast.Node { | ||
| 2103 | const str_type = @tagName(stmt.getKind()); | ||
| 2104 | const mangle = rp.c.getMangle(); | ||
| 2105 | const name = try std.fmt.allocPrint(rp.c.arena, "zig.{s}_string_{d}", .{ str_type, mangle }); | ||
| 2106 | |||
| 2107 | const const_tok = try appendToken(rp.c, .Keyword_const, "const"); | ||
| 2108 | const name_tok = try appendIdentifier(rp.c, name); | ||
| 2109 | const eq_tok = try appendToken(rp.c, .Equal, "="); | ||
| 2110 | var semi_tok: ast.TokenIndex = undefined; | ||
| 2111 | |||
| 2112 | const lit_array = try transStringLiteralAsArray(rp, scope, stmt, stmt.getLength() + 1); | ||
| 2113 | |||
| 2114 | semi_tok = try appendToken(rp.c, .Semicolon, ";"); | ||
| 2115 | const var_decl_node = try ast.Node.VarDecl.create(rp.c.arena, .{ | ||
| 2116 | .name_token = name_tok, | ||
| 2117 | .mut_token = const_tok, | ||
| 2118 | .semicolon_token = semi_tok, | ||
| 2119 | }, .{ | ||
| 2120 | .visib_token = null, | ||
| 2121 | .eq_token = eq_tok, | ||
| 2122 | .init_node = lit_array, | ||
| 2123 | }); | ||
| 2124 | try addTopLevelDecl(rp.c, name, &var_decl_node.base); | ||
| 2125 | return transCreateNodeIdentifier(rp.c, name); | ||
| 2126 | } | ||
| 2127 | |||
| 2128 | /// Parse the size of an array back out from an ast Node. | 1638 | /// Parse the size of an array back out from an ast Node. |
| 2129 | fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize { | 1639 | fn zigArraySize(c: *Context, node: Node) TransError!usize { |
| 2130 | if (node.castTag(.ArrayType)) |array| { | 1640 | if (node.castTag(.array_type)) |array| { |
| 2131 | if (array.len_expr.castTag(.IntegerLiteral)) |int_lit| { | 1641 | return array.data.len; |
| 2132 | const tok = tokenSlice(c, int_lit.token); | ||
| 2133 | return std.fmt.parseUnsigned(usize, tok, 10) catch error.UnsupportedTranslation; | ||
| 2134 | } | ||
| 2135 | } | 1642 | } |
| 2136 | return error.UnsupportedTranslation; | 1643 | return error.UnsupportedTranslation; |
| 2137 | } | 1644 | } |
| ... | @@ -2142,11 +1649,11 @@ fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize { | ... | @@ -2142,11 +1649,11 @@ fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize { |
| 2142 | /// than the array, truncate the string. If the array is larger than the | 1649 | /// than the array, truncate the string. If the array is larger than the |
| 2143 | /// string literal, pad the array with 0's | 1650 | /// string literal, pad the array with 0's |
| 2144 | fn transStringLiteralAsArray( | 1651 | fn transStringLiteralAsArray( |
| 2145 | rp: RestorePoint, | 1652 | c: *Context, |
| 2146 | scope: *Scope, | 1653 | scope: *Scope, |
| 2147 | stmt: *const clang.StringLiteral, | 1654 | stmt: *const clang.StringLiteral, |
| 2148 | array_size: usize, | 1655 | array_size: usize, |
| 2149 | ) TransError!*ast.Node { | 1656 | ) TransError!Node { |
| 2150 | if (array_size == 0) return error.UnsupportedType; | 1657 | if (array_size == 0) return error.UnsupportedType; |
| 2151 | 1658 | ||
| 2152 | const str_length = stmt.getLength(); | 1659 | const str_length = stmt.getLength(); |
| ... | @@ -2155,40 +1662,25 @@ fn transStringLiteralAsArray( | ... | @@ -2155,40 +1662,25 @@ fn transStringLiteralAsArray( |
| 2155 | const ty = expr_base.getType().getTypePtr(); | 1662 | const ty = expr_base.getType().getTypePtr(); |
| 2156 | const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); | 1663 | const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); |
| 2157 | 1664 | ||
| 2158 | const ty_node = try rp.c.arena.create(ast.Node.ArrayType); | 1665 | const elem_type = try transQualType(c, scope, const_arr_ty.getElementType(), expr_base.getBeginLoc()); |
| 2159 | const op_token = try appendToken(rp.c, .LBracket, "["); | 1666 | const arr_type = try Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_type }); |
| 2160 | const len_expr = try transCreateNodeInt(rp.c, array_size); | 1667 | const init_list = try c.arena.alloc(Node, array_size); |
| 2161 | _ = try appendToken(rp.c, .RBracket, "]"); | ||
| 2162 | |||
| 2163 | ty_node.* = .{ | ||
| 2164 | .op_token = op_token, | ||
| 2165 | .rhs = try transQualType(rp, const_arr_ty.getElementType(), expr_base.getBeginLoc()), | ||
| 2166 | .len_expr = len_expr, | ||
| 2167 | }; | ||
| 2168 | _ = try appendToken(rp.c, .LBrace, "{"); | ||
| 2169 | var init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, array_size); | ||
| 2170 | init_node.* = .{ | ||
| 2171 | .lhs = &ty_node.base, | ||
| 2172 | .rtoken = undefined, | ||
| 2173 | .list_len = array_size, | ||
| 2174 | }; | ||
| 2175 | const init_list = init_node.list(); | ||
| 2176 | 1668 | ||
| 2177 | var i: c_uint = 0; | 1669 | var i: c_uint = 0; |
| 2178 | const kind = stmt.getKind(); | 1670 | const kind = stmt.getKind(); |
| 2179 | const narrow = kind == .Ascii or kind == .UTF8; | 1671 | const narrow = kind == .Ascii or kind == .UTF8; |
| 2180 | while (i < str_length and i < array_size) : (i += 1) { | 1672 | while (i < str_length and i < array_size) : (i += 1) { |
| 2181 | const code_unit = stmt.getCodeUnit(i); | 1673 | const code_unit = stmt.getCodeUnit(i); |
| 2182 | init_list[i] = try transCreateCharLitNode(rp.c, narrow, code_unit); | 1674 | init_list[i] = try transCreateCharLitNode(c, narrow, code_unit); |
| 2183 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2184 | } | 1675 | } |
| 2185 | while (i < array_size) : (i += 1) { | 1676 | while (i < array_size) : (i += 1) { |
| 2186 | init_list[i] = try transCreateNodeInt(rp.c, 0); | 1677 | init_list[i] = try transCreateNodeNumber(c, 0, .int); |
| 2187 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2188 | } | 1678 | } |
| 2189 | init_node.rtoken = try appendToken(rp.c, .RBrace, "}"); | ||
| 2190 | 1679 | ||
| 2191 | return &init_node.base; | 1680 | return Tag.array_init.create(c.arena, .{ |
| 1681 | .cond = arr_type, | ||
| 1682 | .cases = init_list, | ||
| 1683 | }); | ||
| 2192 | } | 1684 | } |
| 2193 | 1685 | ||
| 2194 | fn cIsEnum(qt: clang.QualType) bool { | 1686 | fn cIsEnum(qt: clang.QualType) bool { |
| ... | @@ -2207,199 +1699,164 @@ fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType { | ... | @@ -2207,199 +1699,164 @@ fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType { |
| 2207 | } | 1699 | } |
| 2208 | 1700 | ||
| 2209 | fn transCCast( | 1701 | fn transCCast( |
| 2210 | rp: RestorePoint, | 1702 | c: *Context, |
| 2211 | scope: *Scope, | 1703 | scope: *Scope, |
| 2212 | loc: clang.SourceLocation, | 1704 | loc: clang.SourceLocation, |
| 2213 | dst_type: clang.QualType, | 1705 | dst_type: clang.QualType, |
| 2214 | src_type: clang.QualType, | 1706 | src_type: clang.QualType, |
| 2215 | expr: *ast.Node, | 1707 | expr: Node, |
| 2216 | ) !*ast.Node { | 1708 | ) !Node { |
| 2217 | if (qualTypeCanon(dst_type).isVoidType()) return expr; | 1709 | if (qualTypeCanon(dst_type).isVoidType()) return expr; |
| 2218 | if (dst_type.eq(src_type)) return expr; | 1710 | if (dst_type.eq(src_type)) return expr; |
| 2219 | if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type)) | 1711 | if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type)) |
| 2220 | return transCPtrCast(rp, loc, dst_type, src_type, expr); | 1712 | return transCPtrCast(c, scope, loc, dst_type, src_type, expr); |
| 1713 | |||
| 1714 | const dst_node = try transQualType(c, scope, dst_type, loc); | ||
| 2221 | if (cIsInteger(dst_type) and (cIsInteger(src_type) or cIsEnum(src_type))) { | 1715 | if (cIsInteger(dst_type) and (cIsInteger(src_type) or cIsEnum(src_type))) { |
| 2222 | // 1. If src_type is an enum, determine the underlying signed int type | 1716 | // 1. If src_type is an enum, determine the underlying signed int type |
| 2223 | // 2. Extend or truncate without changing signed-ness. | 1717 | // 2. Extend or truncate without changing signed-ness. |
| 2224 | // 3. Bit-cast to correct signed-ness | 1718 | // 3. Bit-cast to correct signed-ness |
| 2225 | const src_type_is_signed = cIsSignedInteger(src_type) or cIsEnum(src_type); | 1719 | const src_type_is_signed = cIsSignedInteger(src_type) or cIsEnum(src_type); |
| 2226 | const src_int_type = if (cIsInteger(src_type)) src_type else cIntTypeForEnum(src_type); | 1720 | const src_int_type = if (cIsInteger(src_type)) src_type else cIntTypeForEnum(src_type); |
| 2227 | var src_int_expr = if (cIsInteger(src_type)) expr else try transEnumToInt(rp.c, expr); | 1721 | var src_int_expr = if (cIsInteger(src_type)) expr else try Tag.enum_to_int.create(c.arena, expr); |
| 2228 | |||
| 2229 | // @bitCast(dest_type, intermediate_value) | ||
| 2230 | const cast_node = try rp.c.createBuiltinCall("@bitCast", 2); | ||
| 2231 | cast_node.params()[0] = try transQualType(rp, dst_type, loc); | ||
| 2232 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2233 | 1722 | ||
| 2234 | if (isBoolRes(src_int_expr)) { | 1723 | if (isBoolRes(src_int_expr)) { |
| 2235 | const bool_to_int_node = try rp.c.createBuiltinCall("@boolToInt", 1); | 1724 | src_int_expr = try Tag.bool_to_int.create(c.arena, src_int_expr); |
| 2236 | bool_to_int_node.params()[0] = src_int_expr; | ||
| 2237 | bool_to_int_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 2238 | src_int_expr = &bool_to_int_node.base; | ||
| 2239 | } | 1725 | } |
| 2240 | 1726 | ||
| 2241 | switch (cIntTypeCmp(dst_type, src_int_type)) { | 1727 | switch (cIntTypeCmp(dst_type, src_int_type)) { |
| 2242 | .lt => { | 1728 | .lt => { |
| 2243 | // @truncate(SameSignSmallerInt, src_int_expr) | 1729 | // @truncate(SameSignSmallerInt, src_int_expr) |
| 2244 | const trunc_node = try rp.c.createBuiltinCall("@truncate", 2); | 1730 | const ty_node = try transQualTypeIntWidthOf(c, dst_type, src_type_is_signed); |
| 2245 | const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, src_type_is_signed); | 1731 | src_int_expr = try Tag.truncate.create(c.arena, .{ .lhs = ty_node, .rhs = src_int_expr }); |
| 2246 | trunc_node.params()[0] = ty_node; | ||
| 2247 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2248 | trunc_node.params()[1] = src_int_expr; | ||
| 2249 | trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 2250 | |||
| 2251 | cast_node.params()[1] = &trunc_node.base; | ||
| 2252 | }, | 1732 | }, |
| 2253 | .gt => { | 1733 | .gt => { |
| 2254 | // @as(SameSignBiggerInt, src_int_expr) | 1734 | // @as(SameSignBiggerInt, src_int_expr) |
| 2255 | const as_node = try rp.c.createBuiltinCall("@as", 2); | 1735 | const ty_node = try transQualTypeIntWidthOf(c, dst_type, src_type_is_signed); |
| 2256 | const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, src_type_is_signed); | 1736 | src_int_expr = try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = src_int_expr }); |
| 2257 | as_node.params()[0] = ty_node; | ||
| 2258 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2259 | as_node.params()[1] = src_int_expr; | ||
| 2260 | as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 2261 | |||
| 2262 | cast_node.params()[1] = &as_node.base; | ||
| 2263 | }, | 1737 | }, |
| 2264 | .eq => { | 1738 | .eq => { |
| 2265 | cast_node.params()[1] = src_int_expr; | 1739 | // src_int_expr = src_int_expr |
| 2266 | }, | 1740 | }, |
| 2267 | } | 1741 | } |
| 2268 | cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | 1742 | // @bitCast(dest_type, intermediate_value) |
| 2269 | return &cast_node.base; | 1743 | return Tag.bit_cast.create(c.arena, .{ .lhs = dst_node, .rhs = src_int_expr }); |
| 2270 | } | 1744 | } |
| 2271 | if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) { | 1745 | if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) { |
| 2272 | // @intCast(dest_type, @ptrToInt(val)) | 1746 | // @intCast(dest_type, @ptrToInt(val)) |
| 2273 | const cast_node = try rp.c.createBuiltinCall("@intCast", 2); | 1747 | const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr); |
| 2274 | cast_node.params()[0] = try transQualType(rp, dst_type, loc); | 1748 | return Tag.int_cast.create(c.arena, .{ .lhs = dst_node, .rhs = ptr_to_int }); |
| 2275 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2276 | const builtin_node = try rp.c.createBuiltinCall("@ptrToInt", 1); | ||
| 2277 | builtin_node.params()[0] = expr; | ||
| 2278 | builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 2279 | cast_node.params()[1] = &builtin_node.base; | ||
| 2280 | cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 2281 | return &cast_node.base; | ||
| 2282 | } | 1749 | } |
| 2283 | if (cIsInteger(src_type) and qualTypeIsPtr(dst_type)) { | 1750 | if (cIsInteger(src_type) and qualTypeIsPtr(dst_type)) { |
| 2284 | // @intToPtr(dest_type, val) | 1751 | // @intToPtr(dest_type, val) |
| 2285 | const builtin_node = try rp.c.createBuiltinCall("@intToPtr", 2); | 1752 | return Tag.int_to_ptr.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); |
| 2286 | builtin_node.params()[0] = try transQualType(rp, dst_type, loc); | ||
| 2287 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2288 | builtin_node.params()[1] = expr; | ||
| 2289 | builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 2290 | return &builtin_node.base; | ||
| 2291 | } | 1753 | } |
| 2292 | if (cIsFloating(src_type) and cIsFloating(dst_type)) { | 1754 | if (cIsFloating(src_type) and cIsFloating(dst_type)) { |
| 2293 | const builtin_node = try rp.c.createBuiltinCall("@floatCast", 2); | 1755 | // @floatCast(dest_type, val) |
| 2294 | builtin_node.params()[0] = try transQualType(rp, dst_type, loc); | 1756 | return Tag.float_cast.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); |
| 2295 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2296 | builtin_node.params()[1] = expr; | ||
| 2297 | builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 2298 | return &builtin_node.base; | ||
| 2299 | } | 1757 | } |
| 2300 | if (cIsFloating(src_type) and !cIsFloating(dst_type)) { | 1758 | if (cIsFloating(src_type) and !cIsFloating(dst_type)) { |
| 2301 | const builtin_node = try rp.c.createBuiltinCall("@floatToInt", 2); | 1759 | // @floatToInt(dest_type, val) |
| 2302 | builtin_node.params()[0] = try transQualType(rp, dst_type, loc); | 1760 | return Tag.float_to_int.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); |
| 2303 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2304 | builtin_node.params()[1] = expr; | ||
| 2305 | builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 2306 | return &builtin_node.base; | ||
| 2307 | } | 1761 | } |
| 2308 | if (!cIsFloating(src_type) and cIsFloating(dst_type)) { | 1762 | if (!cIsFloating(src_type) and cIsFloating(dst_type)) { |
| 2309 | const builtin_node = try rp.c.createBuiltinCall("@intToFloat", 2); | 1763 | // @intToFloat(dest_type, val) |
| 2310 | builtin_node.params()[0] = try transQualType(rp, dst_type, loc); | 1764 | return Tag.int_to_float.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); |
| 2311 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2312 | builtin_node.params()[1] = expr; | ||
| 2313 | builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 2314 | return &builtin_node.base; | ||
| 2315 | } | 1765 | } |
| 2316 | if (qualTypeIsBoolean(src_type) and !qualTypeIsBoolean(dst_type)) { | 1766 | if (qualTypeIsBoolean(src_type) and !qualTypeIsBoolean(dst_type)) { |
| 2317 | // @boolToInt returns either a comptime_int or a u1 | 1767 | // @boolToInt returns either a comptime_int or a u1 |
| 2318 | // TODO: if dst_type is 1 bit & signed (bitfield) we need @bitCast | 1768 | // TODO: if dst_type is 1 bit & signed (bitfield) we need @bitCast |
| 2319 | // instead of @as | 1769 | // instead of @as |
| 2320 | const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1); | 1770 | const bool_to_int = try Tag.bool_to_int.create(c.arena, expr); |
| 2321 | builtin_node.params()[0] = expr; | 1771 | return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = bool_to_int }); |
| 2322 | builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 2323 | |||
| 2324 | const as_node = try rp.c.createBuiltinCall("@as", 2); | ||
| 2325 | as_node.params()[0] = try transQualType(rp, dst_type, loc); | ||
| 2326 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2327 | as_node.params()[1] = &builtin_node.base; | ||
| 2328 | as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 2329 | |||
| 2330 | return &as_node.base; | ||
| 2331 | } | 1772 | } |
| 2332 | if (cIsEnum(dst_type)) { | 1773 | if (cIsEnum(dst_type)) { |
| 2333 | const builtin_node = try rp.c.createBuiltinCall("@intToEnum", 2); | 1774 | // @intToEnum(dest_type, val) |
| 2334 | builtin_node.params()[0] = try transQualType(rp, dst_type, loc); | 1775 | return Tag.int_to_enum.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); |
| 2335 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2336 | builtin_node.params()[1] = expr; | ||
| 2337 | builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 2338 | return &builtin_node.base; | ||
| 2339 | } | 1776 | } |
| 2340 | if (cIsEnum(src_type) and !cIsEnum(dst_type)) { | 1777 | if (cIsEnum(src_type) and !cIsEnum(dst_type)) { |
| 2341 | return transEnumToInt(rp.c, expr); | 1778 | // @enumToInt(val) |
| 1779 | return Tag.enum_to_int.create(c.arena, expr); | ||
| 2342 | } | 1780 | } |
| 2343 | const cast_node = try rp.c.createBuiltinCall("@as", 2); | 1781 | // @as(dest_type, val) |
| 2344 | cast_node.params()[0] = try transQualType(rp, dst_type, loc); | 1782 | return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); |
| 2345 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2346 | cast_node.params()[1] = expr; | ||
| 2347 | cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 2348 | return &cast_node.base; | ||
| 2349 | } | ||
| 2350 | |||
| 2351 | fn transEnumToInt(c: *Context, enum_expr: *ast.Node) TypeError!*ast.Node { | ||
| 2352 | const builtin_node = try c.createBuiltinCall("@enumToInt", 1); | ||
| 2353 | builtin_node.params()[0] = enum_expr; | ||
| 2354 | builtin_node.rparen_token = try appendToken(c, .RParen, ")"); | ||
| 2355 | return &builtin_node.base; | ||
| 2356 | } | 1783 | } |
| 2357 | 1784 | ||
| 2358 | fn transExpr( | 1785 | fn transExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { |
| 2359 | rp: RestorePoint, | 1786 | return transStmt(c, scope, @ptrCast(*const clang.Stmt, expr), used); |
| 2360 | scope: *Scope, | ||
| 2361 | expr: *const clang.Expr, | ||
| 2362 | used: ResultUsed, | ||
| 2363 | lrvalue: LRValue, | ||
| 2364 | ) TransError!*ast.Node { | ||
| 2365 | return transStmt(rp, scope, @ptrCast(*const clang.Stmt, expr), used, lrvalue); | ||
| 2366 | } | 1787 | } |
| 2367 | 1788 | ||
| 2368 | /// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore | 1789 | /// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore |
| 2369 | /// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals. | 1790 | /// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals. |
| 2370 | fn transExprCoercing( | 1791 | fn transExprCoercing(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { |
| 2371 | rp: RestorePoint, | ||
| 2372 | scope: *Scope, | ||
| 2373 | expr: *const clang.Expr, | ||
| 2374 | used: ResultUsed, | ||
| 2375 | lrvalue: LRValue, | ||
| 2376 | ) TransError!*ast.Node { | ||
| 2377 | switch (@ptrCast(*const clang.Stmt, expr).getStmtClass()) { | 1792 | switch (@ptrCast(*const clang.Stmt, expr).getStmtClass()) { |
| 2378 | .IntegerLiteralClass => { | 1793 | .IntegerLiteralClass => { |
| 2379 | return transIntegerLiteral(rp, scope, @ptrCast(*const clang.IntegerLiteral, expr), .used, .no_as); | 1794 | return transIntegerLiteral(c, scope, @ptrCast(*const clang.IntegerLiteral, expr), .used, .no_as); |
| 2380 | }, | 1795 | }, |
| 2381 | .CharacterLiteralClass => { | 1796 | .CharacterLiteralClass => { |
| 2382 | return transCharLiteral(rp, scope, @ptrCast(*const clang.CharacterLiteral, expr), .used, .no_as); | 1797 | return transCharLiteral(c, scope, @ptrCast(*const clang.CharacterLiteral, expr), .used, .no_as); |
| 2383 | }, | 1798 | }, |
| 2384 | .UnaryOperatorClass => { | 1799 | .UnaryOperatorClass => { |
| 2385 | const un_expr = @ptrCast(*const clang.UnaryOperator, expr); | 1800 | const un_expr = @ptrCast(*const clang.UnaryOperator, expr); |
| 2386 | if (un_expr.getOpcode() == .Extension) { | 1801 | if (un_expr.getOpcode() == .Extension) { |
| 2387 | return transExprCoercing(rp, scope, un_expr.getSubExpr(), used, lrvalue); | 1802 | return transExprCoercing(c, scope, un_expr.getSubExpr(), used); |
| 1803 | } | ||
| 1804 | }, | ||
| 1805 | .ImplicitCastExprClass => { | ||
| 1806 | const cast_expr = @ptrCast(*const clang.ImplicitCastExpr, expr); | ||
| 1807 | const sub_expr = cast_expr.getSubExpr(); | ||
| 1808 | switch (@ptrCast(*const clang.Stmt, sub_expr).getStmtClass()) { | ||
| 1809 | .IntegerLiteralClass, .CharacterLiteralClass => switch (cast_expr.getCastKind()) { | ||
| 1810 | .IntegralToFloating => return transExprCoercing(c, scope, sub_expr, used), | ||
| 1811 | .IntegralCast => { | ||
| 1812 | const dest_type = getExprQualType(c, expr); | ||
| 1813 | if (literalFitsInType(c, sub_expr, dest_type)) | ||
| 1814 | return transExprCoercing(c, scope, sub_expr, used); | ||
| 1815 | }, | ||
| 1816 | else => {}, | ||
| 1817 | }, | ||
| 1818 | else => {}, | ||
| 2388 | } | 1819 | } |
| 2389 | }, | 1820 | }, |
| 2390 | else => {}, | 1821 | else => {}, |
| 2391 | } | 1822 | } |
| 2392 | return transExpr(rp, scope, expr, .used, .r_value); | 1823 | return transExpr(c, scope, expr, .used); |
| 1824 | } | ||
| 1825 | |||
| 1826 | fn literalFitsInType(c: *Context, expr: *const clang.Expr, qt: clang.QualType) bool { | ||
| 1827 | var width = qualTypeIntBitWidth(c, qt) catch 8; | ||
| 1828 | if (width == 0) width = 8; // Byte is the smallest type. | ||
| 1829 | const is_signed = cIsSignedInteger(qt); | ||
| 1830 | const width_max_int = (@as(u64, 1) << math.lossyCast(u6, width - @boolToInt(is_signed))) - 1; | ||
| 1831 | |||
| 1832 | switch (@ptrCast(*const clang.Stmt, expr).getStmtClass()) { | ||
| 1833 | .CharacterLiteralClass => { | ||
| 1834 | const char_lit = @ptrCast(*const clang.CharacterLiteral, expr); | ||
| 1835 | const val = char_lit.getValue(); | ||
| 1836 | // If the val is less than the max int then it fits. | ||
| 1837 | return val <= width_max_int; | ||
| 1838 | }, | ||
| 1839 | .IntegerLiteralClass => { | ||
| 1840 | const int_lit = @ptrCast(*const clang.IntegerLiteral, expr); | ||
| 1841 | var eval_result: clang.ExprEvalResult = undefined; | ||
| 1842 | if (!int_lit.EvaluateAsInt(&eval_result, c.clang_context)) { | ||
| 1843 | return false; | ||
| 1844 | } | ||
| 1845 | |||
| 1846 | const int = eval_result.Val.getInt(); | ||
| 1847 | return int.lessThanEqual(width_max_int); | ||
| 1848 | }, | ||
| 1849 | else => unreachable, | ||
| 1850 | } | ||
| 2393 | } | 1851 | } |
| 2394 | 1852 | ||
| 2395 | fn transInitListExprRecord( | 1853 | fn transInitListExprRecord( |
| 2396 | rp: RestorePoint, | 1854 | c: *Context, |
| 2397 | scope: *Scope, | 1855 | scope: *Scope, |
| 2398 | loc: clang.SourceLocation, | 1856 | loc: clang.SourceLocation, |
| 2399 | expr: *const clang.InitListExpr, | 1857 | expr: *const clang.InitListExpr, |
| 2400 | ty: *const clang.Type, | 1858 | ty: *const clang.Type, |
| 2401 | used: ResultUsed, | 1859 | ) TransError!Node { |
| 2402 | ) TransError!*ast.Node { | ||
| 2403 | var is_union_type = false; | 1860 | var is_union_type = false; |
| 2404 | // Unions and Structs are both represented as RecordDecl | 1861 | // Unions and Structs are both represented as RecordDecl |
| 2405 | const record_ty = ty.getAsRecordType() orelse | 1862 | const record_ty = ty.getAsRecordType() orelse |
| ... | @@ -2411,13 +1868,11 @@ fn transInitListExprRecord( | ... | @@ -2411,13 +1868,11 @@ fn transInitListExprRecord( |
| 2411 | const record_def = record_decl.getDefinition() orelse | 1868 | const record_def = record_decl.getDefinition() orelse |
| 2412 | unreachable; | 1869 | unreachable; |
| 2413 | 1870 | ||
| 2414 | const ty_node = try transType(rp, ty, loc); | 1871 | const ty_node = try transType(c, scope, ty, loc); |
| 2415 | const init_count = expr.getNumInits(); | 1872 | const init_count = expr.getNumInits(); |
| 2416 | var field_inits = std.ArrayList(*ast.Node).init(rp.c.gpa); | 1873 | var field_inits = std.ArrayList(ast.Payload.ContainerInit.Initializer).init(c.gpa); |
| 2417 | defer field_inits.deinit(); | 1874 | defer field_inits.deinit(); |
| 2418 | 1875 | ||
| 2419 | _ = try appendToken(rp.c, .LBrace, "{"); | ||
| 2420 | |||
| 2421 | var init_i: c_uint = 0; | 1876 | var init_i: c_uint = 0; |
| 2422 | var it = record_def.field_begin(); | 1877 | var it = record_def.field_begin(); |
| 2423 | const end_it = record_def.field_end(); | 1878 | const end_it = record_def.field_end(); |
| ... | @@ -2435,78 +1890,34 @@ fn transInitListExprRecord( | ... | @@ -2435,78 +1890,34 @@ fn transInitListExprRecord( |
| 2435 | 1890 | ||
| 2436 | // Generate the field assignment expression: | 1891 | // Generate the field assignment expression: |
| 2437 | // .field_name = expr | 1892 | // .field_name = expr |
| 2438 | const period_tok = try appendToken(rp.c, .Period, "."); | 1893 | var raw_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin()); |
| 2439 | |||
| 2440 | var raw_name = try rp.c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin()); | ||
| 2441 | if (field_decl.isAnonymousStructOrUnion()) { | 1894 | if (field_decl.isAnonymousStructOrUnion()) { |
| 2442 | const name = rp.c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?; | 1895 | const name = c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?; |
| 2443 | raw_name = try mem.dupe(rp.c.arena, u8, name); | 1896 | raw_name = try mem.dupe(c.arena, u8, name); |
| 2444 | } | 1897 | } |
| 2445 | const field_name_tok = try appendIdentifier(rp.c, raw_name); | ||
| 2446 | |||
| 2447 | _ = try appendToken(rp.c, .Equal, "="); | ||
| 2448 | 1898 | ||
| 2449 | const field_init_node = try rp.c.arena.create(ast.Node.FieldInitializer); | 1899 | try field_inits.append(.{ |
| 2450 | field_init_node.* = .{ | 1900 | .name = raw_name, |
| 2451 | .period_token = period_tok, | 1901 | .value = try transExpr(c, scope, elem_expr, .used), |
| 2452 | .name_token = field_name_tok, | 1902 | }); |
| 2453 | .expr = try transExpr(rp, scope, elem_expr, .used, .r_value), | ||
| 2454 | }; | ||
| 2455 | |||
| 2456 | try field_inits.append(&field_init_node.base); | ||
| 2457 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2458 | } | 1903 | } |
| 2459 | 1904 | ||
| 2460 | const node = try ast.Node.StructInitializer.alloc(rp.c.arena, field_inits.items.len); | 1905 | return Tag.container_init.create(c.arena, .{ |
| 2461 | node.* = .{ | ||
| 2462 | .lhs = ty_node, | ||
| 2463 | .rtoken = try appendToken(rp.c, .RBrace, "}"), | ||
| 2464 | .list_len = field_inits.items.len, | ||
| 2465 | }; | ||
| 2466 | mem.copy(*ast.Node, node.list(), field_inits.items); | ||
| 2467 | return &node.base; | ||
| 2468 | } | ||
| 2469 | |||
| 2470 | fn transCreateNodeArrayType( | ||
| 2471 | rp: RestorePoint, | ||
| 2472 | source_loc: clang.SourceLocation, | ||
| 2473 | ty: *const clang.Type, | ||
| 2474 | len: anytype, | ||
| 2475 | ) !*ast.Node { | ||
| 2476 | const node = try rp.c.arena.create(ast.Node.ArrayType); | ||
| 2477 | const op_token = try appendToken(rp.c, .LBracket, "["); | ||
| 2478 | const len_expr = try transCreateNodeInt(rp.c, len); | ||
| 2479 | _ = try appendToken(rp.c, .RBracket, "]"); | ||
| 2480 | node.* = .{ | ||
| 2481 | .op_token = op_token, | ||
| 2482 | .rhs = try transType(rp, ty, source_loc), | ||
| 2483 | .len_expr = len_expr, | ||
| 2484 | }; | ||
| 2485 | return &node.base; | ||
| 2486 | } | ||
| 2487 | |||
| 2488 | fn transCreateEmptyArray(rp: RestorePoint, loc: clang.SourceLocation, ty: *const clang.Type) TransError!*ast.Node { | ||
| 2489 | const ty_node = try transCreateNodeArrayType(rp, loc, ty, 0); | ||
| 2490 | _ = try appendToken(rp.c, .LBrace, "{"); | ||
| 2491 | const filler_init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, 0); | ||
| 2492 | filler_init_node.* = .{ | ||
| 2493 | .lhs = ty_node, | 1906 | .lhs = ty_node, |
| 2494 | .rtoken = try appendToken(rp.c, .RBrace, "}"), | 1907 | .inits = try c.arena.dupe(ast.Payload.ContainerInit.Initializer, field_inits.items), |
| 2495 | .list_len = 0, | 1908 | }); |
| 2496 | }; | ||
| 2497 | return &filler_init_node.base; | ||
| 2498 | } | 1909 | } |
| 2499 | 1910 | ||
| 2500 | fn transInitListExprArray( | 1911 | fn transInitListExprArray( |
| 2501 | rp: RestorePoint, | 1912 | c: *Context, |
| 2502 | scope: *Scope, | 1913 | scope: *Scope, |
| 2503 | loc: clang.SourceLocation, | 1914 | loc: clang.SourceLocation, |
| 2504 | expr: *const clang.InitListExpr, | 1915 | expr: *const clang.InitListExpr, |
| 2505 | ty: *const clang.Type, | 1916 | ty: *const clang.Type, |
| 2506 | used: ResultUsed, | 1917 | ) TransError!Node { |
| 2507 | ) TransError!*ast.Node { | ||
| 2508 | const arr_type = ty.getAsArrayTypeUnsafe(); | 1918 | const arr_type = ty.getAsArrayTypeUnsafe(); |
| 2509 | const child_qt = arr_type.getElementType(); | 1919 | const child_qt = arr_type.getElementType(); |
| 1920 | const child_type = try transQualType(c, scope, child_qt, loc); | ||
| 2510 | const init_count = expr.getNumInits(); | 1921 | const init_count = expr.getNumInits(); |
| 2511 | assert(@ptrCast(*const clang.Type, arr_type).isConstantArrayType()); | 1922 | assert(@ptrCast(*const clang.Type, arr_type).isConstantArrayType()); |
| 2512 | const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, arr_type); | 1923 | const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, arr_type); |
| ... | @@ -2515,125 +1926,83 @@ fn transInitListExprArray( | ... | @@ -2515,125 +1926,83 @@ fn transInitListExprArray( |
| 2515 | const leftover_count = all_count - init_count; | 1926 | const leftover_count = all_count - init_count; |
| 2516 | 1927 | ||
| 2517 | if (all_count == 0) { | 1928 | if (all_count == 0) { |
| 2518 | return transCreateEmptyArray(rp, loc, child_qt.getTypePtr()); | 1929 | return Tag.empty_array.create(c.arena, child_type); |
| 2519 | } | 1930 | } |
| 2520 | 1931 | ||
| 2521 | var init_node: *ast.Node.ArrayInitializer = undefined; | 1932 | const init_node = if (init_count != 0) blk: { |
| 2522 | var cat_tok: ast.TokenIndex = undefined; | 1933 | const init_list = try c.arena.alloc(Node, init_count); |
| 2523 | if (init_count != 0) { | ||
| 2524 | const ty_node = try transCreateNodeArrayType( | ||
| 2525 | rp, | ||
| 2526 | loc, | ||
| 2527 | child_qt.getTypePtr(), | ||
| 2528 | init_count, | ||
| 2529 | ); | ||
| 2530 | _ = try appendToken(rp.c, .LBrace, "{"); | ||
| 2531 | init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, init_count); | ||
| 2532 | init_node.* = .{ | ||
| 2533 | .lhs = ty_node, | ||
| 2534 | .rtoken = undefined, | ||
| 2535 | .list_len = init_count, | ||
| 2536 | }; | ||
| 2537 | const init_list = init_node.list(); | ||
| 2538 | 1934 | ||
| 2539 | var i: c_uint = 0; | 1935 | for (init_list) |*init, i| { |
| 2540 | while (i < init_count) : (i += 1) { | 1936 | const elem_expr = expr.getInit(@intCast(c_uint, i)); |
| 2541 | const elem_expr = expr.getInit(i); | 1937 | init.* = try transExprCoercing(c, scope, elem_expr, .used); |
| 2542 | init_list[i] = try transExpr(rp, scope, elem_expr, .used, .r_value); | ||
| 2543 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2544 | } | 1938 | } |
| 2545 | init_node.rtoken = try appendToken(rp.c, .RBrace, "}"); | 1939 | const init_node = try Tag.array_init.create(c.arena, .{ |
| 1940 | .cond = try Tag.array_type.create(c.arena, .{ .len = init_count, .elem_type = child_type }), | ||
| 1941 | .cases = init_list, | ||
| 1942 | }); | ||
| 2546 | if (leftover_count == 0) { | 1943 | if (leftover_count == 0) { |
| 2547 | return &init_node.base; | 1944 | return init_node; |
| 2548 | } | 1945 | } |
| 2549 | cat_tok = try appendToken(rp.c, .PlusPlus, "++"); | 1946 | break :blk init_node; |
| 2550 | } | 1947 | } else null; |
| 2551 | 1948 | ||
| 2552 | const ty_node = try transCreateNodeArrayType(rp, loc, child_qt.getTypePtr(), 1); | ||
| 2553 | _ = try appendToken(rp.c, .LBrace, "{"); | ||
| 2554 | const filler_init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, 1); | ||
| 2555 | filler_init_node.* = .{ | ||
| 2556 | .lhs = ty_node, | ||
| 2557 | .rtoken = undefined, | ||
| 2558 | .list_len = 1, | ||
| 2559 | }; | ||
| 2560 | const filler_val_expr = expr.getArrayFiller(); | 1949 | const filler_val_expr = expr.getArrayFiller(); |
| 2561 | filler_init_node.list()[0] = try transExpr(rp, scope, filler_val_expr, .used, .r_value); | 1950 | const filler_node = try Tag.array_filler.create(c.arena, .{ |
| 2562 | filler_init_node.rtoken = try appendToken(rp.c, .RBrace, "}"); | 1951 | .type = child_type, |
| 2563 | 1952 | .filler = try transExprCoercing(c, scope, filler_val_expr, .used), | |
| 2564 | const rhs_node = if (leftover_count == 1) | 1953 | .count = leftover_count, |
| 2565 | &filler_init_node.base | 1954 | }); |
| 2566 | else blk: { | ||
| 2567 | const mul_tok = try appendToken(rp.c, .AsteriskAsterisk, "**"); | ||
| 2568 | const mul_node = try rp.c.arena.create(ast.Node.SimpleInfixOp); | ||
| 2569 | mul_node.* = .{ | ||
| 2570 | .base = .{ .tag = .ArrayMult }, | ||
| 2571 | .op_token = mul_tok, | ||
| 2572 | .lhs = &filler_init_node.base, | ||
| 2573 | .rhs = try transCreateNodeInt(rp.c, leftover_count), | ||
| 2574 | }; | ||
| 2575 | break :blk &mul_node.base; | ||
| 2576 | }; | ||
| 2577 | 1955 | ||
| 2578 | if (init_count == 0) { | 1956 | if (init_node) |some| { |
| 2579 | return rhs_node; | 1957 | return Tag.array_cat.create(c.arena, .{ .lhs = some, .rhs = filler_node }); |
| 1958 | } else { | ||
| 1959 | return filler_node; | ||
| 2580 | } | 1960 | } |
| 2581 | |||
| 2582 | const cat_node = try rp.c.arena.create(ast.Node.SimpleInfixOp); | ||
| 2583 | cat_node.* = .{ | ||
| 2584 | .base = .{ .tag = .ArrayCat }, | ||
| 2585 | .op_token = cat_tok, | ||
| 2586 | .lhs = &init_node.base, | ||
| 2587 | .rhs = rhs_node, | ||
| 2588 | }; | ||
| 2589 | return &cat_node.base; | ||
| 2590 | } | 1961 | } |
| 2591 | 1962 | ||
| 2592 | fn transInitListExpr( | 1963 | fn transInitListExpr( |
| 2593 | rp: RestorePoint, | 1964 | c: *Context, |
| 2594 | scope: *Scope, | 1965 | scope: *Scope, |
| 2595 | expr: *const clang.InitListExpr, | 1966 | expr: *const clang.InitListExpr, |
| 2596 | used: ResultUsed, | 1967 | used: ResultUsed, |
| 2597 | ) TransError!*ast.Node { | 1968 | ) TransError!Node { |
| 2598 | const qt = getExprQualType(rp.c, @ptrCast(*const clang.Expr, expr)); | 1969 | const qt = getExprQualType(c, @ptrCast(*const clang.Expr, expr)); |
| 2599 | var qual_type = qt.getTypePtr(); | 1970 | var qual_type = qt.getTypePtr(); |
| 2600 | const source_loc = @ptrCast(*const clang.Expr, expr).getBeginLoc(); | 1971 | const source_loc = @ptrCast(*const clang.Expr, expr).getBeginLoc(); |
| 2601 | 1972 | ||
| 2602 | if (qual_type.isRecordType()) { | 1973 | if (qual_type.isRecordType()) { |
| 2603 | return transInitListExprRecord( | 1974 | return maybeSuppressResult(c, scope, used, try transInitListExprRecord( |
| 2604 | rp, | 1975 | c, |
| 2605 | scope, | 1976 | scope, |
| 2606 | source_loc, | 1977 | source_loc, |
| 2607 | expr, | 1978 | expr, |
| 2608 | qual_type, | 1979 | qual_type, |
| 2609 | used, | 1980 | )); |
| 2610 | ); | ||
| 2611 | } else if (qual_type.isArrayType()) { | 1981 | } else if (qual_type.isArrayType()) { |
| 2612 | return transInitListExprArray( | 1982 | return maybeSuppressResult(c, scope, used, try transInitListExprArray( |
| 2613 | rp, | 1983 | c, |
| 2614 | scope, | 1984 | scope, |
| 2615 | source_loc, | 1985 | source_loc, |
| 2616 | expr, | 1986 | expr, |
| 2617 | qual_type, | 1987 | qual_type, |
| 2618 | used, | 1988 | )); |
| 2619 | ); | ||
| 2620 | } else { | 1989 | } else { |
| 2621 | const type_name = rp.c.str(qual_type.getTypeClassName()); | 1990 | const type_name = c.str(qual_type.getTypeClassName()); |
| 2622 | return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported initlist type: '{s}'", .{type_name}); | 1991 | return fail(c, error.UnsupportedType, source_loc, "unsupported initlist type: '{s}'", .{type_name}); |
| 2623 | } | 1992 | } |
| 2624 | } | 1993 | } |
| 2625 | 1994 | ||
| 2626 | fn transZeroInitExpr( | 1995 | fn transZeroInitExpr( |
| 2627 | rp: RestorePoint, | 1996 | c: *Context, |
| 2628 | scope: *Scope, | 1997 | scope: *Scope, |
| 2629 | source_loc: clang.SourceLocation, | 1998 | source_loc: clang.SourceLocation, |
| 2630 | ty: *const clang.Type, | 1999 | ty: *const clang.Type, |
| 2631 | ) TransError!*ast.Node { | 2000 | ) TransError!Node { |
| 2632 | switch (ty.getTypeClass()) { | 2001 | switch (ty.getTypeClass()) { |
| 2633 | .Builtin => { | 2002 | .Builtin => { |
| 2634 | const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); | 2003 | const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); |
| 2635 | switch (builtin_ty.getKind()) { | 2004 | switch (builtin_ty.getKind()) { |
| 2636 | .Bool => return try transCreateNodeBoolLiteral(rp.c, false), | 2005 | .Bool => return Tag.false_literal.init(), |
| 2637 | .Char_U, | 2006 | .Char_U, |
| 2638 | .UChar, | 2007 | .UChar, |
| 2639 | .Char_S, | 2008 | .Char_S, |
| ... | @@ -2654,126 +2023,112 @@ fn transZeroInitExpr( | ... | @@ -2654,126 +2023,112 @@ fn transZeroInitExpr( |
| 2654 | .Float128, | 2023 | .Float128, |
| 2655 | .Float16, | 2024 | .Float16, |
| 2656 | .LongDouble, | 2025 | .LongDouble, |
| 2657 | => return transCreateNodeInt(rp.c, 0), | 2026 | => return Tag.zero_literal.init(), |
| 2658 | else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), | 2027 | else => return fail(c, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), |
| 2659 | } | 2028 | } |
| 2660 | }, | 2029 | }, |
| 2661 | .Pointer => return transCreateNodeNullLiteral(rp.c), | 2030 | .Pointer => return Tag.null_literal.init(), |
| 2662 | .Typedef => { | 2031 | .Typedef => { |
| 2663 | const typedef_ty = @ptrCast(*const clang.TypedefType, ty); | 2032 | const typedef_ty = @ptrCast(*const clang.TypedefType, ty); |
| 2664 | const typedef_decl = typedef_ty.getDecl(); | 2033 | const typedef_decl = typedef_ty.getDecl(); |
| 2665 | return transZeroInitExpr( | 2034 | return transZeroInitExpr( |
| 2666 | rp, | 2035 | c, |
| 2667 | scope, | 2036 | scope, |
| 2668 | source_loc, | 2037 | source_loc, |
| 2669 | typedef_decl.getUnderlyingType().getTypePtr(), | 2038 | typedef_decl.getUnderlyingType().getTypePtr(), |
| 2670 | ); | 2039 | ); |
| 2671 | }, | 2040 | }, |
| 2672 | else => {}, | 2041 | else => return Tag.std_mem_zeroes.create(c.arena, try transType(c, scope, ty, source_loc)), |
| 2673 | } | 2042 | } |
| 2674 | |||
| 2675 | return revertAndWarn(rp, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{}); | ||
| 2676 | } | 2043 | } |
| 2677 | 2044 | ||
| 2678 | fn transImplicitValueInitExpr( | 2045 | fn transImplicitValueInitExpr( |
| 2679 | rp: RestorePoint, | 2046 | c: *Context, |
| 2680 | scope: *Scope, | 2047 | scope: *Scope, |
| 2681 | expr: *const clang.Expr, | 2048 | expr: *const clang.Expr, |
| 2682 | used: ResultUsed, | 2049 | used: ResultUsed, |
| 2683 | ) TransError!*ast.Node { | 2050 | ) TransError!Node { |
| 2684 | const source_loc = expr.getBeginLoc(); | 2051 | const source_loc = expr.getBeginLoc(); |
| 2685 | const qt = getExprQualType(rp.c, expr); | 2052 | const qt = getExprQualType(c, expr); |
| 2686 | const ty = qt.getTypePtr(); | 2053 | const ty = qt.getTypePtr(); |
| 2687 | return transZeroInitExpr(rp, scope, source_loc, ty); | 2054 | return transZeroInitExpr(c, scope, source_loc, ty); |
| 2688 | } | 2055 | } |
| 2689 | 2056 | ||
| 2690 | fn transIfStmt( | 2057 | fn transIfStmt( |
| 2691 | rp: RestorePoint, | 2058 | c: *Context, |
| 2692 | scope: *Scope, | 2059 | scope: *Scope, |
| 2693 | stmt: *const clang.IfStmt, | 2060 | stmt: *const clang.IfStmt, |
| 2694 | ) TransError!*ast.Node { | 2061 | ) TransError!Node { |
| 2695 | // if (c) t | 2062 | // if (c) t |
| 2696 | // if (c) t else e | 2063 | // if (c) t else e |
| 2697 | const if_node = try transCreateNodeIf(rp.c); | ||
| 2698 | |||
| 2699 | var cond_scope = Scope.Condition{ | 2064 | var cond_scope = Scope.Condition{ |
| 2700 | .base = .{ | 2065 | .base = .{ |
| 2701 | .parent = scope, | 2066 | .parent = scope, |
| 2702 | .id = .Condition, | 2067 | .id = .condition, |
| 2703 | }, | 2068 | }, |
| 2704 | }; | 2069 | }; |
| 2705 | defer cond_scope.deinit(); | 2070 | defer cond_scope.deinit(); |
| 2706 | const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond()); | 2071 | const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond()); |
| 2707 | if_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false); | 2072 | const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); |
| 2708 | _ = try appendToken(rp.c, .RParen, ")"); | ||
| 2709 | 2073 | ||
| 2710 | if_node.body = try transStmt(rp, scope, stmt.getThen(), .unused, .r_value); | 2074 | const then_body = try transStmt(c, scope, stmt.getThen(), .unused); |
| 2711 | 2075 | const else_body = if (stmt.getElse()) |expr| | |
| 2712 | if (stmt.getElse()) |expr| { | 2076 | try transStmt(c, scope, expr, .unused) |
| 2713 | if_node.@"else" = try transCreateNodeElse(rp.c); | 2077 | else |
| 2714 | if_node.@"else".?.body = try transStmt(rp, scope, expr, .unused, .r_value); | 2078 | null; |
| 2715 | } | 2079 | return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body }); |
| 2716 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 2717 | return &if_node.base; | ||
| 2718 | } | 2080 | } |
| 2719 | 2081 | ||
| 2720 | fn transWhileLoop( | 2082 | fn transWhileLoop( |
| 2721 | rp: RestorePoint, | 2083 | c: *Context, |
| 2722 | scope: *Scope, | 2084 | scope: *Scope, |
| 2723 | stmt: *const clang.WhileStmt, | 2085 | stmt: *const clang.WhileStmt, |
| 2724 | ) TransError!*ast.Node { | 2086 | ) TransError!Node { |
| 2725 | const while_node = try transCreateNodeWhile(rp.c); | ||
| 2726 | |||
| 2727 | var cond_scope = Scope.Condition{ | 2087 | var cond_scope = Scope.Condition{ |
| 2728 | .base = .{ | 2088 | .base = .{ |
| 2729 | .parent = scope, | 2089 | .parent = scope, |
| 2730 | .id = .Condition, | 2090 | .id = .condition, |
| 2731 | }, | 2091 | }, |
| 2732 | }; | 2092 | }; |
| 2733 | defer cond_scope.deinit(); | 2093 | defer cond_scope.deinit(); |
| 2734 | const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond()); | 2094 | const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond()); |
| 2735 | while_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false); | 2095 | const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); |
| 2736 | _ = try appendToken(rp.c, .RParen, ")"); | ||
| 2737 | 2096 | ||
| 2738 | var loop_scope = Scope{ | 2097 | var loop_scope = Scope{ |
| 2739 | .parent = scope, | 2098 | .parent = scope, |
| 2740 | .id = .Loop, | 2099 | .id = .loop, |
| 2741 | }; | 2100 | }; |
| 2742 | while_node.body = try transStmt(rp, &loop_scope, stmt.getBody(), .unused, .r_value); | 2101 | const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused); |
| 2743 | _ = try appendToken(rp.c, .Semicolon, ";"); | 2102 | return Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null }); |
| 2744 | return &while_node.base; | ||
| 2745 | } | 2103 | } |
| 2746 | 2104 | ||
| 2747 | fn transDoWhileLoop( | 2105 | fn transDoWhileLoop( |
| 2748 | rp: RestorePoint, | 2106 | c: *Context, |
| 2749 | scope: *Scope, | 2107 | scope: *Scope, |
| 2750 | stmt: *const clang.DoStmt, | 2108 | stmt: *const clang.DoStmt, |
| 2751 | ) TransError!*ast.Node { | 2109 | ) TransError!Node { |
| 2752 | const while_node = try transCreateNodeWhile(rp.c); | ||
| 2753 | |||
| 2754 | while_node.condition = try transCreateNodeBoolLiteral(rp.c, true); | ||
| 2755 | _ = try appendToken(rp.c, .RParen, ")"); | ||
| 2756 | var new = false; | ||
| 2757 | var loop_scope = Scope{ | 2110 | var loop_scope = Scope{ |
| 2758 | .parent = scope, | 2111 | .parent = scope, |
| 2759 | .id = .Loop, | 2112 | .id = .do_loop, |
| 2760 | }; | 2113 | }; |
| 2761 | 2114 | ||
| 2762 | // if (!cond) break; | 2115 | // if (!cond) break; |
| 2763 | const if_node = try transCreateNodeIf(rp.c); | ||
| 2764 | var cond_scope = Scope.Condition{ | 2116 | var cond_scope = Scope.Condition{ |
| 2765 | .base = .{ | 2117 | .base = .{ |
| 2766 | .parent = scope, | 2118 | .parent = scope, |
| 2767 | .id = .Condition, | 2119 | .id = .condition, |
| 2768 | }, | 2120 | }, |
| 2769 | }; | 2121 | }; |
| 2770 | defer cond_scope.deinit(); | 2122 | defer cond_scope.deinit(); |
| 2771 | const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!"); | 2123 | const cond = try transBoolExpr(c, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used); |
| 2772 | prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used, .r_value, true); | 2124 | const if_not_break = switch (cond.tag()) { |
| 2773 | _ = try appendToken(rp.c, .RParen, ")"); | 2125 | .false_literal => return transStmt(c, scope, stmt.getBody(), .unused), |
| 2774 | if_node.condition = &prefix_op.base; | 2126 | .true_literal => { |
| 2775 | if_node.body = &(try transCreateNodeBreak(rp.c, null, null)).base; | 2127 | const body_node = try transStmt(c, scope, stmt.getBody(), .unused); |
| 2776 | _ = try appendToken(rp.c, .Semicolon, ";"); | 2128 | return Tag.while_true.create(c.arena, body_node); |
| 2129 | }, | ||
| 2130 | else => try Tag.if_not_break.create(c.arena, cond), | ||
| 2131 | }; | ||
| 2777 | 2132 | ||
| 2778 | const body_node = if (stmt.getBody().getStmtClass() == .CompoundStmtClass) blk: { | 2133 | const body_node = if (stmt.getBody().getStmtClass() == .CompoundStmtClass) blk: { |
| 2779 | // there's already a block in C, so we'll append our condition to it. | 2134 | // there's already a block in C, so we'll append our condition to it. |
| ... | @@ -2786,8 +2141,11 @@ fn transDoWhileLoop( | ... | @@ -2786,8 +2141,11 @@ fn transDoWhileLoop( |
| 2786 | // zig: b; | 2141 | // zig: b; |
| 2787 | // zig: if (!cond) break; | 2142 | // zig: if (!cond) break; |
| 2788 | // zig: } | 2143 | // zig: } |
| 2789 | const node = try transStmt(rp, &loop_scope, stmt.getBody(), .unused, .r_value); | 2144 | const node = try transStmt(c, &loop_scope, stmt.getBody(), .unused); |
| 2790 | break :blk node.castTag(.Block).?; | 2145 | const block = node.castTag(.block).?; |
| 2146 | block.data.stmts.len += 1; // This is safe since we reserve one extra space in Scope.Block.complete. | ||
| 2147 | block.data.stmts[block.data.stmts.len - 1] = if_not_break; | ||
| 2148 | break :blk node; | ||
| 2791 | } else blk: { | 2149 | } else blk: { |
| 2792 | // the C statement is without a block, so we need to create a block to contain it. | 2150 | // the C statement is without a block, so we need to create a block to contain it. |
| 2793 | // c: do | 2151 | // c: do |
| ... | @@ -2797,387 +2155,356 @@ fn transDoWhileLoop( | ... | @@ -2797,387 +2155,356 @@ fn transDoWhileLoop( |
| 2797 | // zig: a; | 2155 | // zig: a; |
| 2798 | // zig: if (!cond) break; | 2156 | // zig: if (!cond) break; |
| 2799 | // zig: } | 2157 | // zig: } |
| 2800 | new = true; | 2158 | const statements = try c.arena.alloc(Node, 2); |
| 2801 | const block = try rp.c.createBlock(2); | 2159 | statements[0] = try transStmt(c, &loop_scope, stmt.getBody(), .unused); |
| 2802 | block.statements_len = 1; // over-allocated so we can add another below | 2160 | statements[1] = if_not_break; |
| 2803 | block.statements()[0] = try transStmt(rp, &loop_scope, stmt.getBody(), .unused, .r_value); | 2161 | break :blk try Tag.block.create(c.arena, .{ .label = null, .stmts = statements }); |
| 2804 | break :blk block; | ||
| 2805 | }; | 2162 | }; |
| 2806 | 2163 | return Tag.while_true.create(c.arena, body_node); | |
| 2807 | // In both cases above, we reserved 1 extra statement. | ||
| 2808 | body_node.statements_len += 1; | ||
| 2809 | body_node.statements()[body_node.statements_len - 1] = &if_node.base; | ||
| 2810 | if (new) | ||
| 2811 | body_node.rbrace = try appendToken(rp.c, .RBrace, "}"); | ||
| 2812 | while_node.body = &body_node.base; | ||
| 2813 | return &while_node.base; | ||
| 2814 | } | 2164 | } |
| 2815 | 2165 | ||
| 2816 | fn transForLoop( | 2166 | fn transForLoop( |
| 2817 | rp: RestorePoint, | 2167 | c: *Context, |
| 2818 | scope: *Scope, | 2168 | scope: *Scope, |
| 2819 | stmt: *const clang.ForStmt, | 2169 | stmt: *const clang.ForStmt, |
| 2820 | ) TransError!*ast.Node { | 2170 | ) TransError!Node { |
| 2821 | var loop_scope = Scope{ | 2171 | var loop_scope = Scope{ |
| 2822 | .parent = scope, | 2172 | .parent = scope, |
| 2823 | .id = .Loop, | 2173 | .id = .loop, |
| 2824 | }; | 2174 | }; |
| 2825 | 2175 | ||
| 2826 | var block_scope: ?Scope.Block = null; | 2176 | var block_scope: ?Scope.Block = null; |
| 2827 | defer if (block_scope) |*bs| bs.deinit(); | 2177 | defer if (block_scope) |*bs| bs.deinit(); |
| 2828 | 2178 | ||
| 2829 | if (stmt.getInit()) |init| { | 2179 | if (stmt.getInit()) |init| { |
| 2830 | block_scope = try Scope.Block.init(rp.c, scope, false); | 2180 | block_scope = try Scope.Block.init(c, scope, false); |
| 2831 | loop_scope.parent = &block_scope.?.base; | 2181 | loop_scope.parent = &block_scope.?.base; |
| 2832 | const init_node = try transStmt(rp, &block_scope.?.base, init, .unused, .r_value); | 2182 | const init_node = try transStmt(c, &block_scope.?.base, init, .unused); |
| 2833 | try block_scope.?.statements.append(init_node); | 2183 | if (init_node.tag() != .declaration) try block_scope.?.statements.append(init_node); |
| 2834 | } | 2184 | } |
| 2835 | var cond_scope = Scope.Condition{ | 2185 | var cond_scope = Scope.Condition{ |
| 2836 | .base = .{ | 2186 | .base = .{ |
| 2837 | .parent = &loop_scope, | 2187 | .parent = &loop_scope, |
| 2838 | .id = .Condition, | 2188 | .id = .condition, |
| 2839 | }, | 2189 | }, |
| 2840 | }; | 2190 | }; |
| 2841 | defer cond_scope.deinit(); | 2191 | defer cond_scope.deinit(); |
| 2842 | 2192 | ||
| 2843 | const while_node = try transCreateNodeWhile(rp.c); | 2193 | const cond = if (stmt.getCond()) |cond| |
| 2844 | while_node.condition = if (stmt.getCond()) |cond| | 2194 | try transBoolExpr(c, &cond_scope.base, cond, .used) |
| 2845 | try transBoolExpr(rp, &cond_scope.base, cond, .used, .r_value, false) | ||
| 2846 | else | 2195 | else |
| 2847 | try transCreateNodeBoolLiteral(rp.c, true); | 2196 | Tag.true_literal.init(); |
| 2848 | _ = try appendToken(rp.c, .RParen, ")"); | ||
| 2849 | 2197 | ||
| 2850 | if (stmt.getInc()) |incr| { | 2198 | const cont_expr = if (stmt.getInc()) |incr| |
| 2851 | _ = try appendToken(rp.c, .Colon, ":"); | 2199 | try transExpr(c, &cond_scope.base, incr, .unused) |
| 2852 | _ = try appendToken(rp.c, .LParen, "("); | 2200 | else |
| 2853 | while_node.continue_expr = try transExpr(rp, &cond_scope.base, incr, .unused, .r_value); | 2201 | null; |
| 2854 | _ = try appendToken(rp.c, .RParen, ")"); | ||
| 2855 | } | ||
| 2856 | 2202 | ||
| 2857 | while_node.body = try transStmt(rp, &loop_scope, stmt.getBody(), .unused, .r_value); | 2203 | const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused); |
| 2204 | const while_node = try Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr }); | ||
| 2858 | if (block_scope) |*bs| { | 2205 | if (block_scope) |*bs| { |
| 2859 | try bs.statements.append(&while_node.base); | 2206 | try bs.statements.append(while_node); |
| 2860 | return try bs.complete(rp.c); | 2207 | return try bs.complete(c); |
| 2861 | } else { | 2208 | } else { |
| 2862 | _ = try appendToken(rp.c, .Semicolon, ";"); | 2209 | return while_node; |
| 2863 | return &while_node.base; | ||
| 2864 | } | 2210 | } |
| 2865 | } | 2211 | } |
| 2866 | 2212 | ||
| 2867 | fn getSwitchCaseCount(stmt: *const clang.SwitchStmt) usize { | ||
| 2868 | const body = stmt.getBody(); | ||
| 2869 | assert(body.getStmtClass() == .CompoundStmtClass); | ||
| 2870 | const comp = @ptrCast(*const clang.CompoundStmt, body); | ||
| 2871 | // TODO https://github.com/ziglang/zig/issues/1738 | ||
| 2872 | // return comp.body_end() - comp.body_begin(); | ||
| 2873 | const start_addr = @ptrToInt(comp.body_begin()); | ||
| 2874 | const end_addr = @ptrToInt(comp.body_end()); | ||
| 2875 | return (end_addr - start_addr) / @sizeOf(*clang.Stmt); | ||
| 2876 | } | ||
| 2877 | |||
| 2878 | fn transSwitch( | 2213 | fn transSwitch( |
| 2879 | rp: RestorePoint, | 2214 | c: *Context, |
| 2880 | scope: *Scope, | 2215 | scope: *Scope, |
| 2881 | stmt: *const clang.SwitchStmt, | 2216 | stmt: *const clang.SwitchStmt, |
| 2882 | ) TransError!*ast.Node { | 2217 | ) TransError!Node { |
| 2883 | const switch_tok = try appendToken(rp.c, .Keyword_switch, "switch"); | ||
| 2884 | _ = try appendToken(rp.c, .LParen, "("); | ||
| 2885 | |||
| 2886 | const cases_len = getSwitchCaseCount(stmt); | ||
| 2887 | |||
| 2888 | var cond_scope = Scope.Condition{ | 2218 | var cond_scope = Scope.Condition{ |
| 2889 | .base = .{ | 2219 | .base = .{ |
| 2890 | .parent = scope, | 2220 | .parent = scope, |
| 2891 | .id = .Condition, | 2221 | .id = .condition, |
| 2892 | }, | 2222 | }, |
| 2893 | }; | 2223 | }; |
| 2894 | defer cond_scope.deinit(); | 2224 | defer cond_scope.deinit(); |
| 2895 | const switch_expr = try transExpr(rp, &cond_scope.base, stmt.getCond(), .used, .r_value); | 2225 | const switch_expr = try transExpr(c, &cond_scope.base, stmt.getCond(), .used); |
| 2896 | _ = try appendToken(rp.c, .RParen, ")"); | ||
| 2897 | _ = try appendToken(rp.c, .LBrace, "{"); | ||
| 2898 | // reserve +1 case in case there is no default case | ||
| 2899 | const switch_node = try ast.Node.Switch.alloc(rp.c.arena, cases_len + 1); | ||
| 2900 | switch_node.* = .{ | ||
| 2901 | .switch_token = switch_tok, | ||
| 2902 | .expr = switch_expr, | ||
| 2903 | .cases_len = cases_len + 1, | ||
| 2904 | .rbrace = try appendToken(rp.c, .RBrace, "}"), | ||
| 2905 | }; | ||
| 2906 | 2226 | ||
| 2907 | var switch_scope = Scope.Switch{ | 2227 | var cases = std.ArrayList(Node).init(c.gpa); |
| 2908 | .base = .{ | 2228 | defer cases.deinit(); |
| 2909 | .id = .Switch, | 2229 | var has_default = false; |
| 2910 | .parent = scope, | ||
| 2911 | }, | ||
| 2912 | .cases = switch_node.cases(), | ||
| 2913 | .case_index = 0, | ||
| 2914 | .pending_block = undefined, | ||
| 2915 | .default_label = null, | ||
| 2916 | .switch_label = null, | ||
| 2917 | }; | ||
| 2918 | 2230 | ||
| 2919 | // tmp block that all statements will go before being picked up by a case or default | 2231 | const body = stmt.getBody(); |
| 2920 | var block_scope = try Scope.Block.init(rp.c, &switch_scope.base, false); | 2232 | assert(body.getStmtClass() == .CompoundStmtClass); |
| 2921 | defer block_scope.deinit(); | 2233 | const compound_stmt = @ptrCast(*const clang.CompoundStmt, body); |
| 2234 | var it = compound_stmt.body_begin(); | ||
| 2235 | const end_it = compound_stmt.body_end(); | ||
| 2236 | // Iterate over switch body and collect all cases. | ||
| 2237 | // Fallthrough is handled by duplicating statements. | ||
| 2238 | while (it != end_it) : (it += 1) { | ||
| 2239 | switch (it[0].getStmtClass()) { | ||
| 2240 | .CaseStmtClass => { | ||
| 2241 | var items = std.ArrayList(Node).init(c.gpa); | ||
| 2242 | defer items.deinit(); | ||
| 2243 | const sub = try transCaseStmt(c, scope, it[0], &items); | ||
| 2244 | const res = try transSwitchProngStmt(c, scope, sub, it, end_it); | ||
| 2245 | |||
| 2246 | if (items.items.len == 0) { | ||
| 2247 | has_default = true; | ||
| 2248 | const switch_else = try Tag.switch_else.create(c.arena, res); | ||
| 2249 | try cases.append(switch_else); | ||
| 2250 | } else { | ||
| 2251 | const switch_prong = try Tag.switch_prong.create(c.arena, .{ | ||
| 2252 | .cases = try c.arena.dupe(Node, items.items), | ||
| 2253 | .cond = res, | ||
| 2254 | }); | ||
| 2255 | try cases.append(switch_prong); | ||
| 2256 | } | ||
| 2257 | }, | ||
| 2258 | .DefaultStmtClass => { | ||
| 2259 | has_default = true; | ||
| 2260 | const default_stmt = @ptrCast(*const clang.DefaultStmt, it[0]); | ||
| 2261 | |||
| 2262 | var sub = default_stmt.getSubStmt(); | ||
| 2263 | while (true) switch (sub.getStmtClass()) { | ||
| 2264 | .CaseStmtClass => sub = @ptrCast(*const clang.CaseStmt, sub).getSubStmt(), | ||
| 2265 | .DefaultStmtClass => sub = @ptrCast(*const clang.DefaultStmt, sub).getSubStmt(), | ||
| 2266 | else => break, | ||
| 2267 | }; | ||
| 2922 | 2268 | ||
| 2923 | // Note that we do not defer a deinit here; the switch_scope.pending_block field | 2269 | const res = try transSwitchProngStmt(c, scope, sub, it, end_it); |
| 2924 | // has its own memory management. This resource is freed inside `transCase` and | ||
| 2925 | // then the final pending_block is freed at the bottom of this function with | ||
| 2926 | // pending_block.deinit(). | ||
| 2927 | switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false); | ||
| 2928 | try switch_scope.pending_block.statements.append(&switch_node.base); | ||
| 2929 | |||
| 2930 | const last = try transStmt(rp, &block_scope.base, stmt.getBody(), .unused, .r_value); | ||
| 2931 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 2932 | |||
| 2933 | // take all pending statements | ||
| 2934 | const last_block_stmts = last.cast(ast.Node.Block).?.statements(); | ||
| 2935 | try switch_scope.pending_block.statements.ensureCapacity( | ||
| 2936 | switch_scope.pending_block.statements.items.len + last_block_stmts.len, | ||
| 2937 | ); | ||
| 2938 | for (last_block_stmts) |n| { | ||
| 2939 | switch_scope.pending_block.statements.appendAssumeCapacity(n); | ||
| 2940 | } | ||
| 2941 | |||
| 2942 | if (switch_scope.default_label == null) { | ||
| 2943 | switch_scope.switch_label = try block_scope.makeMangledName(rp.c, "switch"); | ||
| 2944 | } | ||
| 2945 | if (switch_scope.switch_label) |l| { | ||
| 2946 | switch_scope.pending_block.label = try appendIdentifier(rp.c, l); | ||
| 2947 | _ = try appendToken(rp.c, .Colon, ":"); | ||
| 2948 | } | ||
| 2949 | if (switch_scope.default_label == null) { | ||
| 2950 | const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c)); | ||
| 2951 | else_prong.expr = blk: { | ||
| 2952 | var br = try CtrlFlow.init(rp.c, .Break, switch_scope.switch_label.?); | ||
| 2953 | break :blk &(try br.finish(null)).base; | ||
| 2954 | }; | ||
| 2955 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 2956 | 2270 | ||
| 2957 | if (switch_scope.case_index >= switch_scope.cases.len) | 2271 | const switch_else = try Tag.switch_else.create(c.arena, res); |
| 2958 | return revertAndWarn(rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, stmt).getBeginLoc(), "TODO complex switch cases", .{}); | 2272 | try cases.append(switch_else); |
| 2959 | switch_scope.cases[switch_scope.case_index] = &else_prong.base; | 2273 | }, |
| 2960 | switch_scope.case_index += 1; | 2274 | else => {}, // collected in transSwitchProngStmt |
| 2275 | } | ||
| 2961 | } | 2276 | } |
| 2962 | // We overallocated in case there was no default, so now we correct | ||
| 2963 | // the number of cases in the AST node. | ||
| 2964 | switch_node.cases_len = switch_scope.case_index; | ||
| 2965 | |||
| 2966 | const result_node = try switch_scope.pending_block.complete(rp.c); | ||
| 2967 | switch_scope.pending_block.deinit(); | ||
| 2968 | return result_node; | ||
| 2969 | } | ||
| 2970 | |||
| 2971 | fn transCase( | ||
| 2972 | rp: RestorePoint, | ||
| 2973 | scope: *Scope, | ||
| 2974 | stmt: *const clang.CaseStmt, | ||
| 2975 | ) TransError!*ast.Node { | ||
| 2976 | const block_scope = scope.findBlockScope(rp.c) catch unreachable; | ||
| 2977 | const switch_scope = scope.getSwitch(); | ||
| 2978 | const label = try block_scope.makeMangledName(rp.c, "case"); | ||
| 2979 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 2980 | |||
| 2981 | const expr = if (stmt.getRHS()) |rhs| blk: { | ||
| 2982 | const lhs_node = try transExpr(rp, scope, stmt.getLHS(), .used, .r_value); | ||
| 2983 | const ellips = try appendToken(rp.c, .Ellipsis3, "..."); | ||
| 2984 | const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); | ||
| 2985 | |||
| 2986 | const node = try rp.c.arena.create(ast.Node.SimpleInfixOp); | ||
| 2987 | node.* = .{ | ||
| 2988 | .base = .{ .tag = .Range }, | ||
| 2989 | .op_token = ellips, | ||
| 2990 | .lhs = lhs_node, | ||
| 2991 | .rhs = rhs_node, | ||
| 2992 | }; | ||
| 2993 | break :blk &node.base; | ||
| 2994 | } else | ||
| 2995 | try transExpr(rp, scope, stmt.getLHS(), .used, .r_value); | ||
| 2996 | 2277 | ||
| 2997 | const switch_prong = try transCreateNodeSwitchCase(rp.c, expr); | 2278 | if (!has_default) { |
| 2998 | switch_prong.expr = blk: { | 2279 | const else_prong = try Tag.switch_else.create(c.arena, Tag.empty_block.init()); |
| 2999 | var br = try CtrlFlow.init(rp.c, .Break, label); | 2280 | try cases.append(else_prong); |
| 3000 | break :blk &(try br.finish(null)).base; | 2281 | } |
| 3001 | }; | ||
| 3002 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 3003 | 2282 | ||
| 3004 | if (switch_scope.case_index >= switch_scope.cases.len) | 2283 | return Tag.@"switch".create(c.arena, .{ |
| 3005 | return revertAndWarn(rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, stmt).getBeginLoc(), "TODO complex switch cases", .{}); | 2284 | .cond = switch_expr, |
| 3006 | switch_scope.cases[switch_scope.case_index] = &switch_prong.base; | 2285 | .cases = try c.arena.dupe(Node, cases.items), |
| 3007 | switch_scope.case_index += 1; | 2286 | }); |
| 2287 | } | ||
| 3008 | 2288 | ||
| 3009 | switch_scope.pending_block.label = try appendIdentifier(rp.c, label); | 2289 | /// Collects all items for this case, returns the first statement after the labels. |
| 3010 | _ = try appendToken(rp.c, .Colon, ":"); | 2290 | /// If items ends up empty, the prong should be translated as an else. |
| 2291 | fn transCaseStmt(c: *Context, scope: *Scope, stmt: *const clang.Stmt, items: *std.ArrayList(Node)) TransError!*const clang.Stmt { | ||
| 2292 | var sub = stmt; | ||
| 2293 | var seen_default = false; | ||
| 2294 | while (true) { | ||
| 2295 | switch (sub.getStmtClass()) { | ||
| 2296 | .DefaultStmtClass => { | ||
| 2297 | seen_default = true; | ||
| 2298 | items.items.len = 0; | ||
| 2299 | const default_stmt = @ptrCast(*const clang.DefaultStmt, sub); | ||
| 2300 | sub = default_stmt.getSubStmt(); | ||
| 2301 | }, | ||
| 2302 | .CaseStmtClass => { | ||
| 2303 | const case_stmt = @ptrCast(*const clang.CaseStmt, sub); | ||
| 3011 | 2304 | ||
| 3012 | // take all pending statements | 2305 | if (seen_default) { |
| 3013 | try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items); | 2306 | items.items.len = 0; |
| 3014 | block_scope.statements.shrinkAndFree(0); | 2307 | sub = case_stmt.getSubStmt(); |
| 2308 | continue; | ||
| 2309 | } | ||
| 3015 | 2310 | ||
| 3016 | const pending_node = try switch_scope.pending_block.complete(rp.c); | 2311 | const expr = if (case_stmt.getRHS()) |rhs| blk: { |
| 3017 | switch_scope.pending_block.deinit(); | 2312 | const lhs_node = try transExprCoercing(c, scope, case_stmt.getLHS(), .used); |
| 3018 | switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false); | 2313 | const rhs_node = try transExprCoercing(c, scope, rhs, .used); |
| 3019 | 2314 | ||
| 3020 | try switch_scope.pending_block.statements.append(pending_node); | 2315 | break :blk try Tag.ellipsis3.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); |
| 2316 | } else | ||
| 2317 | try transExprCoercing(c, scope, case_stmt.getLHS(), .used); | ||
| 3021 | 2318 | ||
| 3022 | return transStmt(rp, scope, stmt.getSubStmt(), .unused, .r_value); | 2319 | try items.append(expr); |
| 2320 | sub = case_stmt.getSubStmt(); | ||
| 2321 | }, | ||
| 2322 | else => return sub, | ||
| 2323 | } | ||
| 2324 | } | ||
| 3023 | } | 2325 | } |
| 3024 | 2326 | ||
| 3025 | fn transDefault( | 2327 | /// Collects all statements seen by this case into a block. |
| 3026 | rp: RestorePoint, | 2328 | /// Avoids creating a block if the first statement is a break or return. |
| 2329 | fn transSwitchProngStmt( | ||
| 2330 | c: *Context, | ||
| 3027 | scope: *Scope, | 2331 | scope: *Scope, |
| 3028 | stmt: *const clang.DefaultStmt, | 2332 | stmt: *const clang.Stmt, |
| 3029 | ) TransError!*ast.Node { | 2333 | parent_it: clang.CompoundStmt.ConstBodyIterator, |
| 3030 | const block_scope = scope.findBlockScope(rp.c) catch unreachable; | 2334 | parent_end_it: clang.CompoundStmt.ConstBodyIterator, |
| 3031 | const switch_scope = scope.getSwitch(); | 2335 | ) TransError!Node { |
| 3032 | switch_scope.default_label = try block_scope.makeMangledName(rp.c, "default"); | 2336 | switch (stmt.getStmtClass()) { |
| 3033 | _ = try appendToken(rp.c, .Semicolon, ";"); | 2337 | .BreakStmtClass => return Tag.empty_block.init(), |
| 3034 | 2338 | .ReturnStmtClass => return transStmt(c, scope, stmt, .unused), | |
| 3035 | const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c)); | 2339 | .CaseStmtClass, .DefaultStmtClass => unreachable, |
| 3036 | else_prong.expr = blk: { | 2340 | else => { |
| 3037 | var br = try CtrlFlow.init(rp.c, .Break, switch_scope.default_label.?); | 2341 | var block_scope = try Scope.Block.init(c, scope, false); |
| 3038 | break :blk &(try br.finish(null)).base; | 2342 | defer block_scope.deinit(); |
| 3039 | }; | ||
| 3040 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 3041 | |||
| 3042 | if (switch_scope.case_index >= switch_scope.cases.len) | ||
| 3043 | return revertAndWarn(rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, stmt).getBeginLoc(), "TODO complex switch cases", .{}); | ||
| 3044 | switch_scope.cases[switch_scope.case_index] = &else_prong.base; | ||
| 3045 | switch_scope.case_index += 1; | ||
| 3046 | |||
| 3047 | switch_scope.pending_block.label = try appendIdentifier(rp.c, switch_scope.default_label.?); | ||
| 3048 | _ = try appendToken(rp.c, .Colon, ":"); | ||
| 3049 | |||
| 3050 | // take all pending statements | ||
| 3051 | try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items); | ||
| 3052 | block_scope.statements.shrinkAndFree(0); | ||
| 3053 | 2343 | ||
| 3054 | const pending_node = try switch_scope.pending_block.complete(rp.c); | 2344 | // we do not need to translate `stmt` since it is the first stmt of `parent_it` |
| 3055 | switch_scope.pending_block.deinit(); | 2345 | try transSwitchProngStmtInline(c, &block_scope, parent_it, parent_end_it); |
| 3056 | switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false); | 2346 | return try block_scope.complete(c); |
| 3057 | try switch_scope.pending_block.statements.append(pending_node); | 2347 | }, |
| 2348 | } | ||
| 2349 | } | ||
| 3058 | 2350 | ||
| 3059 | return transStmt(rp, scope, stmt.getSubStmt(), .unused, .r_value); | 2351 | /// Collects all statements seen by this case into a block. |
| 2352 | fn transSwitchProngStmtInline( | ||
| 2353 | c: *Context, | ||
| 2354 | block: *Scope.Block, | ||
| 2355 | start_it: clang.CompoundStmt.ConstBodyIterator, | ||
| 2356 | end_it: clang.CompoundStmt.ConstBodyIterator, | ||
| 2357 | ) TransError!void { | ||
| 2358 | var it = start_it; | ||
| 2359 | while (it != end_it) : (it += 1) { | ||
| 2360 | switch (it[0].getStmtClass()) { | ||
| 2361 | .ReturnStmtClass => { | ||
| 2362 | const result = try transStmt(c, &block.base, it[0], .unused); | ||
| 2363 | try block.statements.append(result); | ||
| 2364 | return; | ||
| 2365 | }, | ||
| 2366 | .BreakStmtClass => return, | ||
| 2367 | .CaseStmtClass => { | ||
| 2368 | var sub = @ptrCast(*const clang.CaseStmt, it[0]).getSubStmt(); | ||
| 2369 | while (true) switch (sub.getStmtClass()) { | ||
| 2370 | .CaseStmtClass => sub = @ptrCast(*const clang.CaseStmt, sub).getSubStmt(), | ||
| 2371 | .DefaultStmtClass => sub = @ptrCast(*const clang.DefaultStmt, sub).getSubStmt(), | ||
| 2372 | else => break, | ||
| 2373 | }; | ||
| 2374 | const result = try transStmt(c, &block.base, sub, .unused); | ||
| 2375 | assert(result.tag() != .declaration); | ||
| 2376 | try block.statements.append(result); | ||
| 2377 | if (result.isNoreturn(true)) { | ||
| 2378 | return; | ||
| 2379 | } | ||
| 2380 | }, | ||
| 2381 | .DefaultStmtClass => { | ||
| 2382 | var sub = @ptrCast(*const clang.DefaultStmt, it[0]).getSubStmt(); | ||
| 2383 | while (true) switch (sub.getStmtClass()) { | ||
| 2384 | .CaseStmtClass => sub = @ptrCast(*const clang.CaseStmt, sub).getSubStmt(), | ||
| 2385 | .DefaultStmtClass => sub = @ptrCast(*const clang.DefaultStmt, sub).getSubStmt(), | ||
| 2386 | else => break, | ||
| 2387 | }; | ||
| 2388 | const result = try transStmt(c, &block.base, sub, .unused); | ||
| 2389 | assert(result.tag() != .declaration); | ||
| 2390 | try block.statements.append(result); | ||
| 2391 | if (result.isNoreturn(true)) { | ||
| 2392 | return; | ||
| 2393 | } | ||
| 2394 | }, | ||
| 2395 | .CompoundStmtClass => { | ||
| 2396 | const result = try transCompoundStmt(c, &block.base, @ptrCast(*const clang.CompoundStmt, it[0])); | ||
| 2397 | try block.statements.append(result); | ||
| 2398 | if (result.isNoreturn(true)) { | ||
| 2399 | return; | ||
| 2400 | } | ||
| 2401 | }, | ||
| 2402 | else => { | ||
| 2403 | const result = try transStmt(c, &block.base, it[0], .unused); | ||
| 2404 | switch (result.tag()) { | ||
| 2405 | .declaration, .empty_block => {}, | ||
| 2406 | else => try block.statements.append(result), | ||
| 2407 | } | ||
| 2408 | }, | ||
| 2409 | } | ||
| 2410 | } | ||
| 2411 | return; | ||
| 3060 | } | 2412 | } |
| 3061 | 2413 | ||
| 3062 | fn transConstantExpr(rp: RestorePoint, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!*ast.Node { | 2414 | fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { |
| 3063 | var result: clang.ExprEvalResult = undefined; | 2415 | var result: clang.ExprEvalResult = undefined; |
| 3064 | if (!expr.EvaluateAsConstantExpr(&result, .EvaluateForCodeGen, rp.c.clang_context)) | 2416 | if (!expr.evaluateAsConstantExpr(&result, .EvaluateForCodeGen, c.clang_context)) |
| 3065 | return revertAndWarn(rp, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid constant expression", .{}); | 2417 | return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid constant expression", .{}); |
| 3066 | 2418 | ||
| 3067 | var val_node: ?*ast.Node = null; | ||
| 3068 | switch (result.Val.getKind()) { | 2419 | switch (result.Val.getKind()) { |
| 3069 | .Int => { | 2420 | .Int => { |
| 3070 | // See comment in `transIntegerLiteral` for why this code is here. | 2421 | // See comment in `transIntegerLiteral` for why this code is here. |
| 3071 | // @as(T, x) | 2422 | // @as(T, x) |
| 3072 | const expr_base = @ptrCast(*const clang.Expr, expr); | 2423 | const expr_base = @ptrCast(*const clang.Expr, expr); |
| 3073 | const as_node = try rp.c.createBuiltinCall("@as", 2); | 2424 | const as_node = try Tag.as.create(c.arena, .{ |
| 3074 | const ty_node = try transQualType(rp, expr_base.getType(), expr_base.getBeginLoc()); | 2425 | .lhs = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()), |
| 3075 | as_node.params()[0] = ty_node; | 2426 | .rhs = try transCreateNodeAPInt(c, result.Val.getInt()), |
| 3076 | _ = try appendToken(rp.c, .Comma, ","); | 2427 | }); |
| 3077 | 2428 | return maybeSuppressResult(c, scope, used, as_node); | |
| 3078 | const int_lit_node = try transCreateNodeAPInt(rp.c, result.Val.getInt()); | ||
| 3079 | as_node.params()[1] = int_lit_node; | ||
| 3080 | |||
| 3081 | as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 3082 | |||
| 3083 | return maybeSuppressResult(rp, scope, used, &as_node.base); | ||
| 3084 | }, | 2429 | }, |
| 3085 | else => { | 2430 | else => { |
| 3086 | return revertAndWarn(rp, error.UnsupportedTranslation, expr.getBeginLoc(), "unsupported constant expression kind", .{}); | 2431 | return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "unsupported constant expression kind", .{}); |
| 3087 | }, | 2432 | }, |
| 3088 | } | 2433 | } |
| 3089 | } | 2434 | } |
| 3090 | 2435 | ||
| 3091 | fn transPredefinedExpr(rp: RestorePoint, scope: *Scope, expr: *const clang.PredefinedExpr, used: ResultUsed) TransError!*ast.Node { | 2436 | fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.PredefinedExpr, used: ResultUsed) TransError!Node { |
| 3092 | return transStringLiteral(rp, scope, expr.getFunctionName(), used); | 2437 | return transStringLiteral(c, scope, expr.getFunctionName(), used); |
| 3093 | } | 2438 | } |
| 3094 | 2439 | ||
| 3095 | fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!*ast.Node { | 2440 | fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node { |
| 3096 | const node = try c.arena.create(ast.Node.OneToken); | 2441 | return Tag.char_literal.create(c.arena, if (narrow) |
| 3097 | node.* = .{ | 2442 | try std.fmt.allocPrint(c.arena, "'{s}'", .{std.zig.fmtEscapes(&.{@intCast(u8, val)})}) |
| 3098 | .base = .{ .tag = .CharLiteral }, | 2443 | else |
| 3099 | .token = undefined, | 2444 | try std.fmt.allocPrint(c.arena, "'\\u{{{x}}}'", .{val})); |
| 3100 | }; | ||
| 3101 | if (narrow) { | ||
| 3102 | const val_array = [_]u8{@intCast(u8, val)}; | ||
| 3103 | node.token = try appendTokenFmt(c, .CharLiteral, "'{}'", .{std.zig.fmtEscapes(&val_array)}); | ||
| 3104 | } else { | ||
| 3105 | node.token = try appendTokenFmt(c, .CharLiteral, "'\\u{{{x}}}'", .{val}); | ||
| 3106 | } | ||
| 3107 | return &node.base; | ||
| 3108 | } | 2445 | } |
| 3109 | 2446 | ||
| 3110 | fn transCharLiteral( | 2447 | fn transCharLiteral( |
| 3111 | rp: RestorePoint, | 2448 | c: *Context, |
| 3112 | scope: *Scope, | 2449 | scope: *Scope, |
| 3113 | stmt: *const clang.CharacterLiteral, | 2450 | stmt: *const clang.CharacterLiteral, |
| 3114 | result_used: ResultUsed, | 2451 | result_used: ResultUsed, |
| 3115 | suppress_as: SuppressCast, | 2452 | suppress_as: SuppressCast, |
| 3116 | ) TransError!*ast.Node { | 2453 | ) TransError!Node { |
| 3117 | const kind = stmt.getKind(); | 2454 | const kind = stmt.getKind(); |
| 3118 | const val = stmt.getValue(); | 2455 | const val = stmt.getValue(); |
| 3119 | const narrow = kind == .Ascii or kind == .UTF8; | 2456 | const narrow = kind == .Ascii or kind == .UTF8; |
| 3120 | // C has a somewhat obscure feature called multi-character character constant | 2457 | // C has a somewhat obscure feature called multi-character character constant |
| 3121 | // e.g. 'abcd' | 2458 | // e.g. 'abcd' |
| 3122 | const int_lit_node = if (kind == .Ascii and val > 255) | 2459 | const int_lit_node = if (kind == .Ascii and val > 255) |
| 3123 | try transCreateNodeInt(rp.c, val) | 2460 | try transCreateNodeNumber(c, val, .int) |
| 3124 | else | 2461 | else |
| 3125 | try transCreateCharLitNode(rp.c, narrow, val); | 2462 | try transCreateCharLitNode(c, narrow, val); |
| 3126 | 2463 | ||
| 3127 | if (suppress_as == .no_as) { | 2464 | if (suppress_as == .no_as) { |
| 3128 | return maybeSuppressResult(rp, scope, result_used, int_lit_node); | 2465 | return maybeSuppressResult(c, scope, result_used, int_lit_node); |
| 3129 | } | 2466 | } |
| 3130 | // See comment in `transIntegerLiteral` for why this code is here. | 2467 | // See comment in `transIntegerLiteral` for why this code is here. |
| 3131 | // @as(T, x) | 2468 | // @as(T, x) |
| 3132 | const expr_base = @ptrCast(*const clang.Expr, stmt); | 2469 | const expr_base = @ptrCast(*const clang.Expr, stmt); |
| 3133 | const as_node = try rp.c.createBuiltinCall("@as", 2); | 2470 | const as_node = try Tag.as.create(c.arena, .{ |
| 3134 | const ty_node = try transQualType(rp, expr_base.getType(), expr_base.getBeginLoc()); | 2471 | .lhs = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()), |
| 3135 | as_node.params()[0] = ty_node; | 2472 | .rhs = int_lit_node, |
| 3136 | _ = try appendToken(rp.c, .Comma, ","); | 2473 | }); |
| 3137 | as_node.params()[1] = int_lit_node; | 2474 | return maybeSuppressResult(c, scope, result_used, as_node); |
| 3138 | |||
| 3139 | as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 3140 | return maybeSuppressResult(rp, scope, result_used, &as_node.base); | ||
| 3141 | } | 2475 | } |
| 3142 | 2476 | ||
| 3143 | fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.StmtExpr, used: ResultUsed) TransError!*ast.Node { | 2477 | fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used: ResultUsed) TransError!Node { |
| 3144 | const comp = stmt.getSubStmt(); | 2478 | const comp = stmt.getSubStmt(); |
| 3145 | if (used == .unused) { | 2479 | if (used == .unused) { |
| 3146 | return transCompoundStmt(rp, scope, comp); | 2480 | return transCompoundStmt(c, scope, comp); |
| 3147 | } | 2481 | } |
| 3148 | const lparen = try appendToken(rp.c, .LParen, "("); | 2482 | var block_scope = try Scope.Block.init(c, scope, true); |
| 3149 | var block_scope = try Scope.Block.init(rp.c, scope, true); | ||
| 3150 | defer block_scope.deinit(); | 2483 | defer block_scope.deinit(); |
| 3151 | 2484 | ||
| 3152 | var it = comp.body_begin(); | 2485 | var it = comp.body_begin(); |
| 3153 | const end_it = comp.body_end(); | 2486 | const end_it = comp.body_end(); |
| 3154 | while (it != end_it - 1) : (it += 1) { | 2487 | while (it != end_it - 1) : (it += 1) { |
| 3155 | const result = try transStmt(rp, &block_scope.base, it[0], .unused, .r_value); | 2488 | const result = try transStmt(c, &block_scope.base, it[0], .unused); |
| 3156 | try block_scope.statements.append(result); | 2489 | switch (result.tag()) { |
| 2490 | .declaration, .empty_block => {}, | ||
| 2491 | else => try block_scope.statements.append(result), | ||
| 2492 | } | ||
| 3157 | } | 2493 | } |
| 3158 | const break_node = blk: { | 2494 | const break_node = try Tag.break_val.create(c.arena, .{ |
| 3159 | var tmp = try CtrlFlow.init(rp.c, .Break, "blk"); | 2495 | .label = block_scope.label, |
| 3160 | const rhs = try transStmt(rp, &block_scope.base, it[0], .used, .r_value); | 2496 | .val = try transStmt(c, &block_scope.base, it[0], .used), |
| 3161 | break :blk try tmp.finish(rhs); | 2497 | }); |
| 3162 | }; | 2498 | try block_scope.statements.append(break_node); |
| 3163 | _ = try appendToken(rp.c, .Semicolon, ";"); | 2499 | const res = try block_scope.complete(c); |
| 3164 | try block_scope.statements.append(&break_node.base); | 2500 | return maybeSuppressResult(c, scope, used, res); |
| 3165 | const block_node = try block_scope.complete(rp.c); | ||
| 3166 | const rparen = try appendToken(rp.c, .RParen, ")"); | ||
| 3167 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); | ||
| 3168 | grouped_expr.* = .{ | ||
| 3169 | .lparen = lparen, | ||
| 3170 | .expr = block_node, | ||
| 3171 | .rparen = rparen, | ||
| 3172 | }; | ||
| 3173 | return maybeSuppressResult(rp, scope, used, &grouped_expr.base); | ||
| 3174 | } | 2501 | } |
| 3175 | 2502 | ||
| 3176 | fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!*ast.Node { | 2503 | fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!Node { |
| 3177 | var container_node = try transExpr(rp, scope, stmt.getBase(), .used, .r_value); | 2504 | var container_node = try transExpr(c, scope, stmt.getBase(), .used); |
| 3178 | 2505 | ||
| 3179 | if (stmt.isArrow()) { | 2506 | if (stmt.isArrow()) { |
| 3180 | container_node = try transCreateNodePtrDeref(rp.c, container_node); | 2507 | container_node = try Tag.deref.create(c.arena, container_node); |
| 3181 | } | 2508 | } |
| 3182 | 2509 | ||
| 3183 | const member_decl = stmt.getMemberDecl(); | 2510 | const member_decl = stmt.getMemberDecl(); |
| ... | @@ -3188,19 +2515,18 @@ fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.MemberExp | ... | @@ -3188,19 +2515,18 @@ fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.MemberExp |
| 3188 | if (decl_kind == .Field) { | 2515 | if (decl_kind == .Field) { |
| 3189 | const field_decl = @ptrCast(*const clang.FieldDecl, member_decl); | 2516 | const field_decl = @ptrCast(*const clang.FieldDecl, member_decl); |
| 3190 | if (field_decl.isAnonymousStructOrUnion()) { | 2517 | if (field_decl.isAnonymousStructOrUnion()) { |
| 3191 | const name = rp.c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?; | 2518 | const name = c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?; |
| 3192 | break :blk try mem.dupe(rp.c.arena, u8, name); | 2519 | break :blk try mem.dupe(c.arena, u8, name); |
| 3193 | } | 2520 | } |
| 3194 | } | 2521 | } |
| 3195 | const decl = @ptrCast(*const clang.NamedDecl, member_decl); | 2522 | const decl = @ptrCast(*const clang.NamedDecl, member_decl); |
| 3196 | break :blk try rp.c.str(decl.getName_bytes_begin()); | 2523 | break :blk try c.str(decl.getName_bytes_begin()); |
| 3197 | }; | 2524 | }; |
| 3198 | 2525 | const node = try Tag.field_access.create(c.arena, .{ .lhs = container_node, .field_name = name }); | |
| 3199 | const node = try transCreateNodeFieldAccess(rp.c, container_node, name); | 2526 | return maybeSuppressResult(c, scope, result_used, node); |
| 3200 | return maybeSuppressResult(rp, scope, result_used, node); | ||
| 3201 | } | 2527 | } |
| 3202 | 2528 | ||
| 3203 | fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const clang.ArraySubscriptExpr, result_used: ResultUsed) TransError!*ast.Node { | 2529 | fn transArrayAccess(c: *Context, scope: *Scope, stmt: *const clang.ArraySubscriptExpr, result_used: ResultUsed) TransError!Node { |
| 3204 | var base_stmt = stmt.getBase(); | 2530 | var base_stmt = stmt.getBase(); |
| 3205 | 2531 | ||
| 3206 | // Unwrap the base statement if it's an array decayed to a bare pointer type | 2532 | // Unwrap the base statement if it's an array decayed to a bare pointer type |
| ... | @@ -3213,30 +2539,26 @@ fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const clang.ArraySub | ... | @@ -3213,30 +2539,26 @@ fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const clang.ArraySub |
| 3213 | } | 2539 | } |
| 3214 | } | 2540 | } |
| 3215 | 2541 | ||
| 3216 | const container_node = try transExpr(rp, scope, base_stmt, .used, .r_value); | 2542 | const container_node = try transExpr(c, scope, base_stmt, .used); |
| 3217 | const node = try transCreateNodeArrayAccess(rp.c, container_node); | ||
| 3218 | 2543 | ||
| 3219 | // cast if the index is long long or signed | 2544 | // cast if the index is long long or signed |
| 3220 | const subscr_expr = stmt.getIdx(); | 2545 | const subscr_expr = stmt.getIdx(); |
| 3221 | const qt = getExprQualType(rp.c, subscr_expr); | 2546 | const qt = getExprQualType(c, subscr_expr); |
| 3222 | const is_longlong = cIsLongLongInteger(qt); | 2547 | const is_longlong = cIsLongLongInteger(qt); |
| 3223 | const is_signed = cIsSignedInteger(qt); | 2548 | const is_signed = cIsSignedInteger(qt); |
| 3224 | 2549 | ||
| 3225 | if (is_longlong or is_signed) { | 2550 | const rhs = if (is_longlong or is_signed) blk: { |
| 3226 | const cast_node = try rp.c.createBuiltinCall("@intCast", 2); | ||
| 3227 | // check if long long first so that signed long long doesn't just become unsigned long long | 2551 | // check if long long first so that signed long long doesn't just become unsigned long long |
| 3228 | var typeid_node = if (is_longlong) try transCreateNodeIdentifier(rp.c, "usize") else try transQualTypeIntWidthOf(rp.c, qt, false); | 2552 | var typeid_node = if (is_longlong) try Tag.identifier.create(c.arena, "usize") else try transQualTypeIntWidthOf(c, qt, false); |
| 3229 | cast_node.params()[0] = typeid_node; | 2553 | break :blk try Tag.int_cast.create(c.arena, .{ .lhs = typeid_node, .rhs = try transExpr(c, scope, subscr_expr, .used) }); |
| 3230 | _ = try appendToken(rp.c, .Comma, ","); | 2554 | } else |
| 3231 | cast_node.params()[1] = try transExpr(rp, scope, subscr_expr, .used, .r_value); | 2555 | try transExpr(c, scope, subscr_expr, .used); |
| 3232 | cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | 2556 | |
| 3233 | node.rtoken = try appendToken(rp.c, .RBrace, "]"); | 2557 | const node = try Tag.array_access.create(c.arena, .{ |
| 3234 | node.index_expr = &cast_node.base; | 2558 | .lhs = container_node, |
| 3235 | } else { | 2559 | .rhs = rhs, |
| 3236 | node.index_expr = try transExpr(rp, scope, subscr_expr, .used, .r_value); | 2560 | }); |
| 3237 | node.rtoken = try appendToken(rp.c, .RBrace, "]"); | 2561 | return maybeSuppressResult(c, scope, result_used, node); |
| 3238 | } | ||
| 3239 | return maybeSuppressResult(rp, scope, result_used, &node.base); | ||
| 3240 | } | 2562 | } |
| 3241 | 2563 | ||
| 3242 | /// Check if an expression is ultimately a reference to a function declaration | 2564 | /// Check if an expression is ultimately a reference to a function declaration |
| ... | @@ -3271,29 +2593,25 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool { | ... | @@ -3271,29 +2593,25 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool { |
| 3271 | } | 2593 | } |
| 3272 | } | 2594 | } |
| 3273 | 2595 | ||
| 3274 | fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, result_used: ResultUsed) TransError!*ast.Node { | 2596 | fn transCallExpr(c: *Context, scope: *Scope, stmt: *const clang.CallExpr, result_used: ResultUsed) TransError!Node { |
| 3275 | const callee = stmt.getCallee(); | 2597 | const callee = stmt.getCallee(); |
| 3276 | var raw_fn_expr = try transExpr(rp, scope, callee, .used, .r_value); | 2598 | var raw_fn_expr = try transExpr(c, scope, callee, .used); |
| 3277 | 2599 | ||
| 3278 | var is_ptr = false; | 2600 | var is_ptr = false; |
| 3279 | const fn_ty = qualTypeGetFnProto(callee.getType(), &is_ptr); | 2601 | const fn_ty = qualTypeGetFnProto(callee.getType(), &is_ptr); |
| 3280 | 2602 | ||
| 3281 | const fn_expr = if (is_ptr and fn_ty != null and !cIsFunctionDeclRef(callee)) | 2603 | const fn_expr = if (is_ptr and fn_ty != null and !cIsFunctionDeclRef(callee)) |
| 3282 | try transCreateNodeUnwrapNull(rp.c, raw_fn_expr) | 2604 | try Tag.unwrap.create(c.arena, raw_fn_expr) |
| 3283 | else | 2605 | else |
| 3284 | raw_fn_expr; | 2606 | raw_fn_expr; |
| 3285 | 2607 | ||
| 3286 | const num_args = stmt.getNumArgs(); | 2608 | const num_args = stmt.getNumArgs(); |
| 3287 | const node = try rp.c.createCall(fn_expr, num_args); | 2609 | const args = try c.arena.alloc(Node, num_args); |
| 3288 | const call_params = node.params(); | ||
| 3289 | 2610 | ||
| 3290 | const args = stmt.getArgs(); | 2611 | const c_args = stmt.getArgs(); |
| 3291 | var i: usize = 0; | 2612 | var i: usize = 0; |
| 3292 | while (i < num_args) : (i += 1) { | 2613 | while (i < num_args) : (i += 1) { |
| 3293 | if (i != 0) { | 2614 | var arg = try transExpr(c, scope, c_args[i], .used); |
| 3294 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 3295 | } | ||
| 3296 | var call_param = try transExpr(rp, scope, args[i], .used, .r_value); | ||
| 3297 | 2615 | ||
| 3298 | // In C the result type of a boolean expression is int. If this result is passed as | 2616 | // In C the result type of a boolean expression is int. If this result is passed as |
| 3299 | // an argument to a function whose parameter is also int, there is no cast. Therefore | 2617 | // an argument to a function whose parameter is also int, there is no cast. Therefore |
| ... | @@ -3304,31 +2622,26 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, r | ... | @@ -3304,31 +2622,26 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, r |
| 3304 | const param_count = fn_proto.getNumParams(); | 2622 | const param_count = fn_proto.getNumParams(); |
| 3305 | if (i < param_count) { | 2623 | if (i < param_count) { |
| 3306 | const param_qt = fn_proto.getParamType(@intCast(c_uint, i)); | 2624 | const param_qt = fn_proto.getParamType(@intCast(c_uint, i)); |
| 3307 | if (isBoolRes(call_param) and cIsNativeInt(param_qt)) { | 2625 | if (isBoolRes(arg) and cIsNativeInt(param_qt)) { |
| 3308 | const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1); | 2626 | arg = try Tag.bool_to_int.create(c.arena, arg); |
| 3309 | builtin_node.params()[0] = call_param; | ||
| 3310 | builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 3311 | call_param = &builtin_node.base; | ||
| 3312 | } | 2627 | } |
| 3313 | } | 2628 | } |
| 3314 | }, | 2629 | }, |
| 3315 | else => {}, | 2630 | else => {}, |
| 3316 | } | 2631 | } |
| 3317 | } | 2632 | } |
| 3318 | call_params[i] = call_param; | 2633 | args[i] = arg; |
| 3319 | } | 2634 | } |
| 3320 | node.rtoken = try appendToken(rp.c, .RParen, ")"); | 2635 | const node = try Tag.call.create(c.arena, .{ .lhs = fn_expr, .args = args }); |
| 3321 | |||
| 3322 | if (fn_ty) |ty| { | 2636 | if (fn_ty) |ty| { |
| 3323 | const canon = ty.getReturnType().getCanonicalType(); | 2637 | const canon = ty.getReturnType().getCanonicalType(); |
| 3324 | const ret_ty = canon.getTypePtr(); | 2638 | const ret_ty = canon.getTypePtr(); |
| 3325 | if (ret_ty.isVoidType()) { | 2639 | if (ret_ty.isVoidType()) { |
| 3326 | _ = try appendToken(rp.c, .Semicolon, ";"); | 2640 | return node; |
| 3327 | return &node.base; | ||
| 3328 | } | 2641 | } |
| 3329 | } | 2642 | } |
| 3330 | 2643 | ||
| 3331 | return maybeSuppressResult(rp, scope, result_used, &node.base); | 2644 | return maybeSuppressResult(c, scope, result_used, node); |
| 3332 | } | 2645 | } |
| 3333 | 2646 | ||
| 3334 | const ClangFunctionType = union(enum) { | 2647 | const ClangFunctionType = union(enum) { |
| ... | @@ -3363,38 +2676,29 @@ fn qualTypeGetFnProto(qt: clang.QualType, is_ptr: *bool) ?ClangFunctionType { | ... | @@ -3363,38 +2676,29 @@ fn qualTypeGetFnProto(qt: clang.QualType, is_ptr: *bool) ?ClangFunctionType { |
| 3363 | } | 2676 | } |
| 3364 | 2677 | ||
| 3365 | fn transUnaryExprOrTypeTraitExpr( | 2678 | fn transUnaryExprOrTypeTraitExpr( |
| 3366 | rp: RestorePoint, | 2679 | c: *Context, |
| 3367 | scope: *Scope, | 2680 | scope: *Scope, |
| 3368 | stmt: *const clang.UnaryExprOrTypeTraitExpr, | 2681 | stmt: *const clang.UnaryExprOrTypeTraitExpr, |
| 3369 | result_used: ResultUsed, | 2682 | result_used: ResultUsed, |
| 3370 | ) TransError!*ast.Node { | 2683 | ) TransError!Node { |
| 3371 | const loc = stmt.getBeginLoc(); | 2684 | const loc = stmt.getBeginLoc(); |
| 3372 | const type_node = try transQualType( | 2685 | const type_node = try transQualType(c, scope, stmt.getTypeOfArgument(), loc); |
| 3373 | rp, | ||
| 3374 | stmt.getTypeOfArgument(), | ||
| 3375 | loc, | ||
| 3376 | ); | ||
| 3377 | 2686 | ||
| 3378 | const kind = stmt.getKind(); | 2687 | const kind = stmt.getKind(); |
| 3379 | const kind_str = switch (kind) { | 2688 | switch (kind) { |
| 3380 | .SizeOf => "@sizeOf", | 2689 | .SizeOf => return Tag.sizeof.create(c.arena, type_node), |
| 3381 | .AlignOf => "@alignOf", | 2690 | .AlignOf => return Tag.alignof.create(c.arena, type_node), |
| 3382 | .PreferredAlignOf, | 2691 | .PreferredAlignOf, |
| 3383 | .VecStep, | 2692 | .VecStep, |
| 3384 | .OpenMPRequiredSimdAlign, | 2693 | .OpenMPRequiredSimdAlign, |
| 3385 | => return revertAndWarn( | 2694 | => return fail( |
| 3386 | rp, | 2695 | c, |
| 3387 | error.UnsupportedTranslation, | 2696 | error.UnsupportedTranslation, |
| 3388 | loc, | 2697 | loc, |
| 3389 | "Unsupported type trait kind {}", | 2698 | "Unsupported type trait kind {}", |
| 3390 | .{kind}, | 2699 | .{kind}, |
| 3391 | ), | 2700 | ), |
| 3392 | }; | 2701 | } |
| 3393 | |||
| 3394 | const builtin_node = try rp.c.createBuiltinCall(kind_str, 1); | ||
| 3395 | builtin_node.params()[0] = type_node; | ||
| 3396 | builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 3397 | return maybeSuppressResult(rp, scope, result_used, &builtin_node.base); | ||
| 3398 | } | 2702 | } |
| 3399 | 2703 | ||
| 3400 | fn qualTypeHasWrappingOverflow(qt: clang.QualType) bool { | 2704 | fn qualTypeHasWrappingOverflow(qt: clang.QualType) bool { |
| ... | @@ -3407,95 +2711,79 @@ fn qualTypeHasWrappingOverflow(qt: clang.QualType) bool { | ... | @@ -3407,95 +2711,79 @@ fn qualTypeHasWrappingOverflow(qt: clang.QualType) bool { |
| 3407 | } | 2711 | } |
| 3408 | } | 2712 | } |
| 3409 | 2713 | ||
| 3410 | fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.UnaryOperator, used: ResultUsed) TransError!*ast.Node { | 2714 | fn transUnaryOperator(c: *Context, scope: *Scope, stmt: *const clang.UnaryOperator, used: ResultUsed) TransError!Node { |
| 3411 | const op_expr = stmt.getSubExpr(); | 2715 | const op_expr = stmt.getSubExpr(); |
| 3412 | switch (stmt.getOpcode()) { | 2716 | switch (stmt.getOpcode()) { |
| 3413 | .PostInc => if (qualTypeHasWrappingOverflow(stmt.getType())) | 2717 | .PostInc => if (qualTypeHasWrappingOverflow(stmt.getType())) |
| 3414 | return transCreatePostCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used) | 2718 | return transCreatePostCrement(c, scope, stmt, .add_wrap_assign, used) |
| 3415 | else | 2719 | else |
| 3416 | return transCreatePostCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used), | 2720 | return transCreatePostCrement(c, scope, stmt, .add_assign, used), |
| 3417 | .PostDec => if (qualTypeHasWrappingOverflow(stmt.getType())) | 2721 | .PostDec => if (qualTypeHasWrappingOverflow(stmt.getType())) |
| 3418 | return transCreatePostCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used) | 2722 | return transCreatePostCrement(c, scope, stmt, .sub_wrap_assign, used) |
| 3419 | else | 2723 | else |
| 3420 | return transCreatePostCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used), | 2724 | return transCreatePostCrement(c, scope, stmt, .sub_assign, used), |
| 3421 | .PreInc => if (qualTypeHasWrappingOverflow(stmt.getType())) | 2725 | .PreInc => if (qualTypeHasWrappingOverflow(stmt.getType())) |
| 3422 | return transCreatePreCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used) | 2726 | return transCreatePreCrement(c, scope, stmt, .add_wrap_assign, used) |
| 3423 | else | 2727 | else |
| 3424 | return transCreatePreCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used), | 2728 | return transCreatePreCrement(c, scope, stmt, .add_assign, used), |
| 3425 | .PreDec => if (qualTypeHasWrappingOverflow(stmt.getType())) | 2729 | .PreDec => if (qualTypeHasWrappingOverflow(stmt.getType())) |
| 3426 | return transCreatePreCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used) | 2730 | return transCreatePreCrement(c, scope, stmt, .sub_wrap_assign, used) |
| 3427 | else | 2731 | else |
| 3428 | return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used), | 2732 | return transCreatePreCrement(c, scope, stmt, .sub_assign, used), |
| 3429 | .AddrOf => { | 2733 | .AddrOf => { |
| 3430 | if (cIsFunctionDeclRef(op_expr)) { | 2734 | if (cIsFunctionDeclRef(op_expr)) { |
| 3431 | return transExpr(rp, scope, op_expr, used, .r_value); | 2735 | return transExpr(c, scope, op_expr, used); |
| 3432 | } | 2736 | } |
| 3433 | const op_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); | 2737 | return Tag.address_of.create(c.arena, try transExpr(c, scope, op_expr, used)); |
| 3434 | op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value); | ||
| 3435 | return &op_node.base; | ||
| 3436 | }, | 2738 | }, |
| 3437 | .Deref => { | 2739 | .Deref => { |
| 3438 | const value_node = try transExpr(rp, scope, op_expr, used, .r_value); | 2740 | const node = try transExpr(c, scope, op_expr, used); |
| 3439 | var is_ptr = false; | 2741 | var is_ptr = false; |
| 3440 | const fn_ty = qualTypeGetFnProto(op_expr.getType(), &is_ptr); | 2742 | const fn_ty = qualTypeGetFnProto(op_expr.getType(), &is_ptr); |
| 3441 | if (fn_ty != null and is_ptr) | 2743 | if (fn_ty != null and is_ptr) |
| 3442 | return value_node; | 2744 | return node; |
| 3443 | const unwrapped = try transCreateNodeUnwrapNull(rp.c, value_node); | 2745 | const unwrapped = try Tag.unwrap.create(c.arena, node); |
| 3444 | return transCreateNodePtrDeref(rp.c, unwrapped); | 2746 | return Tag.deref.create(c.arena, unwrapped); |
| 3445 | }, | 2747 | }, |
| 3446 | .Plus => return transExpr(rp, scope, op_expr, used, .r_value), | 2748 | .Plus => return transExpr(c, scope, op_expr, used), |
| 3447 | .Minus => { | 2749 | .Minus => { |
| 3448 | if (!qualTypeHasWrappingOverflow(op_expr.getType())) { | 2750 | if (!qualTypeHasWrappingOverflow(op_expr.getType())) { |
| 3449 | const op_node = try transCreateNodeSimplePrefixOp(rp.c, .Negation, .Minus, "-"); | 2751 | return Tag.negate.create(c.arena, try transExpr(c, scope, op_expr, .used)); |
| 3450 | op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); | ||
| 3451 | return &op_node.base; | ||
| 3452 | } else if (cIsUnsignedInteger(op_expr.getType())) { | 2752 | } else if (cIsUnsignedInteger(op_expr.getType())) { |
| 3453 | // we gotta emit 0 -% x | 2753 | // use -% x for unsigned integers |
| 3454 | const zero = try transCreateNodeInt(rp.c, 0); | 2754 | return Tag.negate_wrap.create(c.arena, try transExpr(c, scope, op_expr, .used)); |
| 3455 | const token = try appendToken(rp.c, .MinusPercent, "-%"); | ||
| 3456 | const expr = try transExpr(rp, scope, op_expr, .used, .r_value); | ||
| 3457 | return transCreateNodeInfixOp(rp, scope, zero, .SubWrap, token, expr, used, true); | ||
| 3458 | } else | 2755 | } else |
| 3459 | return revertAndWarn(rp, error.UnsupportedTranslation, stmt.getBeginLoc(), "C negation with non float non integer", .{}); | 2756 | return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "C negation with non float non integer", .{}); |
| 3460 | }, | 2757 | }, |
| 3461 | .Not => { | 2758 | .Not => { |
| 3462 | const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BitNot, .Tilde, "~"); | 2759 | return Tag.bit_not.create(c.arena, try transExpr(c, scope, op_expr, .used)); |
| 3463 | op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); | ||
| 3464 | return &op_node.base; | ||
| 3465 | }, | 2760 | }, |
| 3466 | .LNot => { | 2761 | .LNot => { |
| 3467 | const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!"); | 2762 | return Tag.not.create(c.arena, try transBoolExpr(c, scope, op_expr, .used)); |
| 3468 | op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true); | ||
| 3469 | return &op_node.base; | ||
| 3470 | }, | 2763 | }, |
| 3471 | .Extension => { | 2764 | .Extension => { |
| 3472 | return transExpr(rp, scope, stmt.getSubExpr(), used, .l_value); | 2765 | return transExpr(c, scope, stmt.getSubExpr(), used); |
| 3473 | }, | 2766 | }, |
| 3474 | else => return revertAndWarn(rp, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported C translation {}", .{stmt.getOpcode()}), | 2767 | else => return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported C translation {}", .{stmt.getOpcode()}), |
| 3475 | } | 2768 | } |
| 3476 | } | 2769 | } |
| 3477 | 2770 | ||
| 3478 | fn transCreatePreCrement( | 2771 | fn transCreatePreCrement( |
| 3479 | rp: RestorePoint, | 2772 | c: *Context, |
| 3480 | scope: *Scope, | 2773 | scope: *Scope, |
| 3481 | stmt: *const clang.UnaryOperator, | 2774 | stmt: *const clang.UnaryOperator, |
| 3482 | op: ast.Node.Tag, | 2775 | op: Tag, |
| 3483 | op_tok_id: std.zig.Token.Id, | ||
| 3484 | bytes: []const u8, | ||
| 3485 | used: ResultUsed, | 2776 | used: ResultUsed, |
| 3486 | ) TransError!*ast.Node { | 2777 | ) TransError!Node { |
| 3487 | const op_expr = stmt.getSubExpr(); | 2778 | const op_expr = stmt.getSubExpr(); |
| 3488 | 2779 | ||
| 3489 | if (used == .unused) { | 2780 | if (used == .unused) { |
| 3490 | // common case | 2781 | // common case |
| 3491 | // c: ++expr | 2782 | // c: ++expr |
| 3492 | // zig: expr += 1 | 2783 | // zig: expr += 1 |
| 3493 | const expr = try transExpr(rp, scope, op_expr, .used, .r_value); | 2784 | const lhs = try transExpr(c, scope, op_expr, .used); |
| 3494 | const token = try appendToken(rp.c, op_tok_id, bytes); | 2785 | const rhs = Tag.one_literal.init(); |
| 3495 | const one = try transCreateNodeInt(rp.c, 1); | 2786 | return transCreateNodeInfixOp(c, scope, op, lhs, rhs, .used); |
| 3496 | if (scope.id != .Condition) | ||
| 3497 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 3498 | return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false); | ||
| 3499 | } | 2787 | } |
| 3500 | // worst case | 2788 | // worst case |
| 3501 | // c: ++expr | 2789 | // c: ++expr |
| ... | @@ -3504,71 +2792,44 @@ fn transCreatePreCrement( | ... | @@ -3504,71 +2792,44 @@ fn transCreatePreCrement( |
| 3504 | // zig: _ref.* += 1; | 2792 | // zig: _ref.* += 1; |
| 3505 | // zig: break :blk _ref.* | 2793 | // zig: break :blk _ref.* |
| 3506 | // zig: }) | 2794 | // zig: }) |
| 3507 | var block_scope = try Scope.Block.init(rp.c, scope, true); | 2795 | var block_scope = try Scope.Block.init(c, scope, true); |
| 3508 | defer block_scope.deinit(); | 2796 | defer block_scope.deinit(); |
| 3509 | const ref = try block_scope.makeMangledName(rp.c, "ref"); | 2797 | const ref = try block_scope.makeMangledName(c, "ref"); |
| 3510 | |||
| 3511 | const mut_tok = try appendToken(rp.c, .Keyword_const, "const"); | ||
| 3512 | const name_tok = try appendIdentifier(rp.c, ref); | ||
| 3513 | const eq_token = try appendToken(rp.c, .Equal, "="); | ||
| 3514 | const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); | ||
| 3515 | rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); | ||
| 3516 | const init_node = &rhs_node.base; | ||
| 3517 | const semicolon_token = try appendToken(rp.c, .Semicolon, ";"); | ||
| 3518 | const node = try ast.Node.VarDecl.create(rp.c.arena, .{ | ||
| 3519 | .name_token = name_tok, | ||
| 3520 | .mut_token = mut_tok, | ||
| 3521 | .semicolon_token = semicolon_token, | ||
| 3522 | }, .{ | ||
| 3523 | .eq_token = eq_token, | ||
| 3524 | .init_node = init_node, | ||
| 3525 | }); | ||
| 3526 | try block_scope.statements.append(&node.base); | ||
| 3527 | |||
| 3528 | const lhs_node = try transCreateNodeIdentifier(rp.c, ref); | ||
| 3529 | const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node); | ||
| 3530 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 3531 | const token = try appendToken(rp.c, op_tok_id, bytes); | ||
| 3532 | const one = try transCreateNodeInt(rp.c, 1); | ||
| 3533 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 3534 | const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false); | ||
| 3535 | try block_scope.statements.append(assign); | ||
| 3536 | 2798 | ||
| 3537 | const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node); | 2799 | const expr = try transExpr(c, &block_scope.base, op_expr, .used); |
| 3538 | try block_scope.statements.append(&break_node.base); | 2800 | const addr_of = try Tag.address_of.create(c.arena, expr); |
| 3539 | const block_node = try block_scope.complete(rp.c); | 2801 | const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of }); |
| 3540 | // semicolon must immediately follow rbrace because it is the last token in a block | 2802 | try block_scope.statements.append(ref_decl); |
| 3541 | _ = try appendToken(rp.c, .Semicolon, ";"); | 2803 | |
| 3542 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); | 2804 | const lhs_node = try Tag.identifier.create(c.arena, ref); |
| 3543 | grouped_expr.* = .{ | 2805 | const ref_node = try Tag.deref.create(c.arena, lhs_node); |
| 3544 | .lparen = try appendToken(rp.c, .LParen, "("), | 2806 | const node = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, Tag.one_literal.init(), .used); |
| 3545 | .expr = block_node, | 2807 | try block_scope.statements.append(node); |
| 3546 | .rparen = try appendToken(rp.c, .RParen, ")"), | 2808 | |
| 3547 | }; | 2809 | const break_node = try Tag.break_val.create(c.arena, .{ |
| 3548 | return &grouped_expr.base; | 2810 | .label = block_scope.label, |
| 2811 | .val = ref_node, | ||
| 2812 | }); | ||
| 2813 | try block_scope.statements.append(break_node); | ||
| 2814 | return block_scope.complete(c); | ||
| 3549 | } | 2815 | } |
| 3550 | 2816 | ||
| 3551 | fn transCreatePostCrement( | 2817 | fn transCreatePostCrement( |
| 3552 | rp: RestorePoint, | 2818 | c: *Context, |
| 3553 | scope: *Scope, | 2819 | scope: *Scope, |
| 3554 | stmt: *const clang.UnaryOperator, | 2820 | stmt: *const clang.UnaryOperator, |
| 3555 | op: ast.Node.Tag, | 2821 | op: Tag, |
| 3556 | op_tok_id: std.zig.Token.Id, | ||
| 3557 | bytes: []const u8, | ||
| 3558 | used: ResultUsed, | 2822 | used: ResultUsed, |
| 3559 | ) TransError!*ast.Node { | 2823 | ) TransError!Node { |
| 3560 | const op_expr = stmt.getSubExpr(); | 2824 | const op_expr = stmt.getSubExpr(); |
| 3561 | 2825 | ||
| 3562 | if (used == .unused) { | 2826 | if (used == .unused) { |
| 3563 | // common case | 2827 | // common case |
| 3564 | // c: ++expr | 2828 | // c: expr++ |
| 3565 | // zig: expr += 1 | 2829 | // zig: expr += 1 |
| 3566 | const expr = try transExpr(rp, scope, op_expr, .used, .r_value); | 2830 | const lhs = try transExpr(c, scope, op_expr, .used); |
| 3567 | const token = try appendToken(rp.c, op_tok_id, bytes); | 2831 | const rhs = Tag.one_literal.init(); |
| 3568 | const one = try transCreateNodeInt(rp.c, 1); | 2832 | return transCreateNodeInfixOp(c, scope, op, lhs, rhs, .used); |
| 3569 | if (scope.id != .Condition) | ||
| 3570 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 3571 | return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false); | ||
| 3572 | } | 2833 | } |
| 3573 | // worst case | 2834 | // worst case |
| 3574 | // c: expr++ | 2835 | // c: expr++ |
| ... | @@ -3578,93 +2839,56 @@ fn transCreatePostCrement( | ... | @@ -3578,93 +2839,56 @@ fn transCreatePostCrement( |
| 3578 | // zig: _ref.* += 1; | 2839 | // zig: _ref.* += 1; |
| 3579 | // zig: break :blk _tmp | 2840 | // zig: break :blk _tmp |
| 3580 | // zig: }) | 2841 | // zig: }) |
| 3581 | var block_scope = try Scope.Block.init(rp.c, scope, true); | 2842 | var block_scope = try Scope.Block.init(c, scope, true); |
| 3582 | defer block_scope.deinit(); | 2843 | defer block_scope.deinit(); |
| 3583 | const ref = try block_scope.makeMangledName(rp.c, "ref"); | 2844 | const ref = try block_scope.makeMangledName(c, "ref"); |
| 3584 | |||
| 3585 | const mut_tok = try appendToken(rp.c, .Keyword_const, "const"); | ||
| 3586 | const name_tok = try appendIdentifier(rp.c, ref); | ||
| 3587 | const eq_token = try appendToken(rp.c, .Equal, "="); | ||
| 3588 | const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); | ||
| 3589 | rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); | ||
| 3590 | const init_node = &rhs_node.base; | ||
| 3591 | const semicolon_token = try appendToken(rp.c, .Semicolon, ";"); | ||
| 3592 | const node = try ast.Node.VarDecl.create(rp.c.arena, .{ | ||
| 3593 | .name_token = name_tok, | ||
| 3594 | .mut_token = mut_tok, | ||
| 3595 | .semicolon_token = semicolon_token, | ||
| 3596 | }, .{ | ||
| 3597 | .eq_token = eq_token, | ||
| 3598 | .init_node = init_node, | ||
| 3599 | }); | ||
| 3600 | try block_scope.statements.append(&node.base); | ||
| 3601 | |||
| 3602 | const lhs_node = try transCreateNodeIdentifier(rp.c, ref); | ||
| 3603 | const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node); | ||
| 3604 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 3605 | |||
| 3606 | const tmp = try block_scope.makeMangledName(rp.c, "tmp"); | ||
| 3607 | const tmp_mut_tok = try appendToken(rp.c, .Keyword_const, "const"); | ||
| 3608 | const tmp_name_tok = try appendIdentifier(rp.c, tmp); | ||
| 3609 | const tmp_eq_token = try appendToken(rp.c, .Equal, "="); | ||
| 3610 | const tmp_init_node = ref_node; | ||
| 3611 | const tmp_semicolon_token = try appendToken(rp.c, .Semicolon, ";"); | ||
| 3612 | const tmp_node = try ast.Node.VarDecl.create(rp.c.arena, .{ | ||
| 3613 | .name_token = tmp_name_tok, | ||
| 3614 | .mut_token = tmp_mut_tok, | ||
| 3615 | .semicolon_token = semicolon_token, | ||
| 3616 | }, .{ | ||
| 3617 | .eq_token = tmp_eq_token, | ||
| 3618 | .init_node = tmp_init_node, | ||
| 3619 | }); | ||
| 3620 | try block_scope.statements.append(&tmp_node.base); | ||
| 3621 | 2845 | ||
| 3622 | const token = try appendToken(rp.c, op_tok_id, bytes); | 2846 | const expr = try transExpr(c, &block_scope.base, op_expr, .used); |
| 3623 | const one = try transCreateNodeInt(rp.c, 1); | 2847 | const addr_of = try Tag.address_of.create(c.arena, expr); |
| 3624 | _ = try appendToken(rp.c, .Semicolon, ";"); | 2848 | const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of }); |
| 3625 | const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false); | 2849 | try block_scope.statements.append(ref_decl); |
| 3626 | try block_scope.statements.append(assign); | ||
| 3627 | 2850 | ||
| 3628 | const break_node = blk: { | 2851 | const lhs_node = try Tag.identifier.create(c.arena, ref); |
| 3629 | var tmp_ctrl_flow = try CtrlFlow.initToken(rp.c, .Break, block_scope.label); | 2852 | const ref_node = try Tag.deref.create(c.arena, lhs_node); |
| 3630 | const rhs = try transCreateNodeIdentifier(rp.c, tmp); | 2853 | |
| 3631 | break :blk try tmp_ctrl_flow.finish(rhs); | 2854 | const tmp = try block_scope.makeMangledName(c, "tmp"); |
| 3632 | }; | 2855 | const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = ref_node }); |
| 3633 | try block_scope.statements.append(&break_node.base); | 2856 | try block_scope.statements.append(tmp_decl); |
| 3634 | _ = try appendToken(rp.c, .Semicolon, ";"); | 2857 | |
| 3635 | const block_node = try block_scope.complete(rp.c); | 2858 | const node = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, Tag.one_literal.init(), .used); |
| 3636 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); | 2859 | try block_scope.statements.append(node); |
| 3637 | grouped_expr.* = .{ | 2860 | |
| 3638 | .lparen = try appendToken(rp.c, .LParen, "("), | 2861 | const break_node = try Tag.break_val.create(c.arena, .{ |
| 3639 | .expr = block_node, | 2862 | .label = block_scope.label, |
| 3640 | .rparen = try appendToken(rp.c, .RParen, ")"), | 2863 | .val = try Tag.identifier.create(c.arena, tmp), |
| 3641 | }; | 2864 | }); |
| 3642 | return &grouped_expr.base; | 2865 | try block_scope.statements.append(break_node); |
| 2866 | return block_scope.complete(c); | ||
| 3643 | } | 2867 | } |
| 3644 | 2868 | ||
| 3645 | fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.CompoundAssignOperator, used: ResultUsed) TransError!*ast.Node { | 2869 | fn transCompoundAssignOperator(c: *Context, scope: *Scope, stmt: *const clang.CompoundAssignOperator, used: ResultUsed) TransError!Node { |
| 3646 | switch (stmt.getOpcode()) { | 2870 | switch (stmt.getOpcode()) { |
| 3647 | .MulAssign => if (qualTypeHasWrappingOverflow(stmt.getType())) | 2871 | .MulAssign => if (qualTypeHasWrappingOverflow(stmt.getType())) |
| 3648 | return transCreateCompoundAssign(rp, scope, stmt, .AssignMulWrap, .AsteriskPercentEqual, "*%=", .MulWrap, .AsteriskPercent, "*%", used) | 2872 | return transCreateCompoundAssign(c, scope, stmt, .mul_wrap_assign, used) |
| 3649 | else | 2873 | else |
| 3650 | return transCreateCompoundAssign(rp, scope, stmt, .AssignMul, .AsteriskEqual, "*=", .Mul, .Asterisk, "*", used), | 2874 | return transCreateCompoundAssign(c, scope, stmt, .mul_assign, used), |
| 3651 | .AddAssign => if (qualTypeHasWrappingOverflow(stmt.getType())) | 2875 | .AddAssign => if (qualTypeHasWrappingOverflow(stmt.getType())) |
| 3652 | return transCreateCompoundAssign(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", .AddWrap, .PlusPercent, "+%", used) | 2876 | return transCreateCompoundAssign(c, scope, stmt, .add_wrap_assign, used) |
| 3653 | else | 2877 | else |
| 3654 | return transCreateCompoundAssign(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", .Add, .Plus, "+", used), | 2878 | return transCreateCompoundAssign(c, scope, stmt, .add_assign, used), |
| 3655 | .SubAssign => if (qualTypeHasWrappingOverflow(stmt.getType())) | 2879 | .SubAssign => if (qualTypeHasWrappingOverflow(stmt.getType())) |
| 3656 | return transCreateCompoundAssign(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", .SubWrap, .MinusPercent, "-%", used) | 2880 | return transCreateCompoundAssign(c, scope, stmt, .sub_wrap_assign, used) |
| 3657 | else | 2881 | else |
| 3658 | return transCreateCompoundAssign(rp, scope, stmt, .AssignSub, .MinusPercentEqual, "-=", .Sub, .Minus, "-", used), | 2882 | return transCreateCompoundAssign(c, scope, stmt, .sub_assign, used), |
| 3659 | .DivAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignDiv, .SlashEqual, "/=", .Div, .Slash, "/", used), | 2883 | .DivAssign => return transCreateCompoundAssign(c, scope, stmt, .div_assign, used), |
| 3660 | .RemAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignMod, .PercentEqual, "%=", .Mod, .Percent, "%", used), | 2884 | .RemAssign => return transCreateCompoundAssign(c, scope, stmt, .mod_assign, used), |
| 3661 | .ShlAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftLeft, .AngleBracketAngleBracketLeftEqual, "<<=", .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<", used), | 2885 | .ShlAssign => return transCreateCompoundAssign(c, scope, stmt, .shl_assign, used), |
| 3662 | .ShrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftRight, .AngleBracketAngleBracketRightEqual, ">>=", .BitShiftRight, .AngleBracketAngleBracketRight, ">>", used), | 2886 | .ShrAssign => return transCreateCompoundAssign(c, scope, stmt, .shr_assign, used), |
| 3663 | .AndAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitAnd, .AmpersandEqual, "&=", .BitAnd, .Ampersand, "&", used), | 2887 | .AndAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_and_assign, used), |
| 3664 | .XorAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitXor, .CaretEqual, "^=", .BitXor, .Caret, "^", used), | 2888 | .XorAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_xor_assign, used), |
| 3665 | .OrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitOr, .PipeEqual, "|=", .BitOr, .Pipe, "|", used), | 2889 | .OrAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_or_assign, used), |
| 3666 | else => return revertAndWarn( | 2890 | else => return fail( |
| 3667 | rp, | 2891 | c, |
| 3668 | error.UnsupportedTranslation, | 2892 | error.UnsupportedTranslation, |
| 3669 | stmt.getBeginLoc(), | 2893 | stmt.getBeginLoc(), |
| 3670 | "unsupported C translation {}", | 2894 | "unsupported C translation {}", |
| ... | @@ -3674,25 +2898,20 @@ fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const cla | ... | @@ -3674,25 +2898,20 @@ fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const cla |
| 3674 | } | 2898 | } |
| 3675 | 2899 | ||
| 3676 | fn transCreateCompoundAssign( | 2900 | fn transCreateCompoundAssign( |
| 3677 | rp: RestorePoint, | 2901 | c: *Context, |
| 3678 | scope: *Scope, | 2902 | scope: *Scope, |
| 3679 | stmt: *const clang.CompoundAssignOperator, | 2903 | stmt: *const clang.CompoundAssignOperator, |
| 3680 | assign_op: ast.Node.Tag, | 2904 | op: Tag, |
| 3681 | assign_tok_id: std.zig.Token.Id, | ||
| 3682 | assign_bytes: []const u8, | ||
| 3683 | bin_op: ast.Node.Tag, | ||
| 3684 | bin_tok_id: std.zig.Token.Id, | ||
| 3685 | bin_bytes: []const u8, | ||
| 3686 | used: ResultUsed, | 2905 | used: ResultUsed, |
| 3687 | ) TransError!*ast.Node { | 2906 | ) TransError!Node { |
| 3688 | const is_shift = bin_op == .BitShiftLeft or bin_op == .BitShiftRight; | 2907 | const is_shift = op == .shl_assign or op == .shr_assign; |
| 3689 | const is_div = bin_op == .Div; | 2908 | const is_div = op == .div_assign; |
| 3690 | const is_mod = bin_op == .Mod; | 2909 | const is_mod = op == .mod_assign; |
| 3691 | const lhs = stmt.getLHS(); | 2910 | const lhs = stmt.getLHS(); |
| 3692 | const rhs = stmt.getRHS(); | 2911 | const rhs = stmt.getRHS(); |
| 3693 | const loc = stmt.getBeginLoc(); | 2912 | const loc = stmt.getBeginLoc(); |
| 3694 | const lhs_qt = getExprQualType(rp.c, lhs); | 2913 | const lhs_qt = getExprQualType(c, lhs); |
| 3695 | const rhs_qt = getExprQualType(rp.c, rhs); | 2914 | const rhs_qt = getExprQualType(c, rhs); |
| 3696 | const is_signed = cIsSignedInteger(lhs_qt); | 2915 | const is_signed = cIsSignedInteger(lhs_qt); |
| 3697 | const requires_int_cast = blk: { | 2916 | const requires_int_cast = blk: { |
| 3698 | const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt); | 2917 | const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt); |
| ... | @@ -3704,146 +2923,100 @@ fn transCreateCompoundAssign( | ... | @@ -3704,146 +2923,100 @@ fn transCreateCompoundAssign( |
| 3704 | // c: lhs += rhs | 2923 | // c: lhs += rhs |
| 3705 | // zig: lhs += rhs | 2924 | // zig: lhs += rhs |
| 3706 | if ((is_mod or is_div) and is_signed) { | 2925 | if ((is_mod or is_div) and is_signed) { |
| 3707 | const op_token = try appendToken(rp.c, .Equal, "="); | 2926 | const lhs_node = try transExpr(c, scope, lhs, .used); |
| 3708 | const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp); | 2927 | const rhs_node = try transExpr(c, scope, rhs, .used); |
| 3709 | const builtin = if (is_mod) "@rem" else "@divTrunc"; | 2928 | const builtin = if (is_mod) |
| 3710 | const builtin_node = try rp.c.createBuiltinCall(builtin, 2); | 2929 | try Tag.rem.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }) |
| 3711 | const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value); | 2930 | else |
| 3712 | builtin_node.params()[0] = lhs_node; | 2931 | try Tag.div_trunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); |
| 3713 | _ = try appendToken(rp.c, .Comma, ","); | 2932 | |
| 3714 | builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value); | 2933 | return transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used); |
| 3715 | builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 3716 | op_node.* = .{ | ||
| 3717 | .base = .{ .tag = .Assign }, | ||
| 3718 | .op_token = op_token, | ||
| 3719 | .lhs = lhs_node, | ||
| 3720 | .rhs = &builtin_node.base, | ||
| 3721 | }; | ||
| 3722 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 3723 | return &op_node.base; | ||
| 3724 | } | 2934 | } |
| 3725 | 2935 | ||
| 3726 | const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value); | 2936 | const lhs_node = try transExpr(c, scope, lhs, .used); |
| 3727 | const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes); | ||
| 3728 | var rhs_node = if (is_shift or requires_int_cast) | 2937 | var rhs_node = if (is_shift or requires_int_cast) |
| 3729 | try transExprCoercing(rp, scope, rhs, .used, .r_value) | 2938 | try transExprCoercing(c, scope, rhs, .used) |
| 3730 | else | 2939 | else |
| 3731 | try transExpr(rp, scope, rhs, .used, .r_value); | 2940 | try transExpr(c, scope, rhs, .used); |
| 3732 | 2941 | ||
| 3733 | if (is_shift or requires_int_cast) { | 2942 | if (is_shift or requires_int_cast) { |
| 3734 | const cast_node = try rp.c.createBuiltinCall("@intCast", 2); | 2943 | // @intCast(rhs) |
| 3735 | const cast_to_type = if (is_shift) | 2944 | const cast_to_type = if (is_shift) |
| 3736 | try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc) | 2945 | try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc) |
| 3737 | else | 2946 | else |
| 3738 | try transQualType(rp, getExprQualType(rp.c, lhs), loc); | 2947 | try transQualType(c, scope, getExprQualType(c, lhs), loc); |
| 3739 | cast_node.params()[0] = cast_to_type; | 2948 | |
| 3740 | _ = try appendToken(rp.c, .Comma, ","); | 2949 | rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); |
| 3741 | cast_node.params()[1] = rhs_node; | ||
| 3742 | cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 3743 | rhs_node = &cast_node.base; | ||
| 3744 | } | 2950 | } |
| 3745 | if (scope.id != .Condition) | 2951 | |
| 3746 | _ = try appendToken(rp.c, .Semicolon, ";"); | 2952 | return transCreateNodeInfixOp(c, scope, op, lhs_node, rhs_node, .used); |
| 3747 | return transCreateNodeInfixOp(rp, scope, lhs_node, assign_op, eq_token, rhs_node, .used, false); | ||
| 3748 | } | 2953 | } |
| 3749 | // worst case | 2954 | // worst case |
| 3750 | // c: lhs += rhs | 2955 | // c: lhs += rhs |
| 3751 | // zig: (blk: { | 2956 | // zig: (blk: { |
| 3752 | // zig: const _ref = &lhs; | 2957 | // zig: const _ref = &lhs; |
| 3753 | // zig: _ref.* = _ref.* + rhs; | 2958 | // zig: _ref.* += rhs; |
| 3754 | // zig: break :blk _ref.* | 2959 | // zig: break :blk _ref.* |
| 3755 | // zig: }) | 2960 | // zig: }) |
| 3756 | var block_scope = try Scope.Block.init(rp.c, scope, true); | 2961 | var block_scope = try Scope.Block.init(c, scope, true); |
| 3757 | defer block_scope.deinit(); | 2962 | defer block_scope.deinit(); |
| 3758 | const ref = try block_scope.makeMangledName(rp.c, "ref"); | 2963 | const ref = try block_scope.makeMangledName(c, "ref"); |
| 3759 | 2964 | ||
| 3760 | const mut_tok = try appendToken(rp.c, .Keyword_const, "const"); | 2965 | const expr = try transExpr(c, &block_scope.base, lhs, .used); |
| 3761 | const name_tok = try appendIdentifier(rp.c, ref); | 2966 | const addr_of = try Tag.address_of.create(c.arena, expr); |
| 3762 | const eq_token = try appendToken(rp.c, .Equal, "="); | 2967 | const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of }); |
| 3763 | const addr_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); | 2968 | try block_scope.statements.append(ref_decl); |
| 3764 | addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value); | ||
| 3765 | const init_node = &addr_node.base; | ||
| 3766 | const semicolon_token = try appendToken(rp.c, .Semicolon, ";"); | ||
| 3767 | const node = try ast.Node.VarDecl.create(rp.c.arena, .{ | ||
| 3768 | .name_token = name_tok, | ||
| 3769 | .mut_token = mut_tok, | ||
| 3770 | .semicolon_token = semicolon_token, | ||
| 3771 | }, .{ | ||
| 3772 | .eq_token = eq_token, | ||
| 3773 | .init_node = init_node, | ||
| 3774 | }); | ||
| 3775 | try block_scope.statements.append(&node.base); | ||
| 3776 | 2969 | ||
| 3777 | const lhs_node = try transCreateNodeIdentifier(rp.c, ref); | 2970 | const lhs_node = try Tag.identifier.create(c.arena, ref); |
| 3778 | const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node); | 2971 | const ref_node = try Tag.deref.create(c.arena, lhs_node); |
| 3779 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 3780 | 2972 | ||
| 3781 | if ((is_mod or is_div) and is_signed) { | 2973 | if ((is_mod or is_div) and is_signed) { |
| 3782 | const op_token = try appendToken(rp.c, .Equal, "="); | 2974 | const rhs_node = try transExpr(c, &block_scope.base, rhs, .used); |
| 3783 | const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp); | 2975 | const builtin = if (is_mod) |
| 3784 | const builtin = if (is_mod) "@rem" else "@divTrunc"; | 2976 | try Tag.rem.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node }) |
| 3785 | const builtin_node = try rp.c.createBuiltinCall(builtin, 2); | 2977 | else |
| 3786 | builtin_node.params()[0] = try transCreateNodePtrDeref(rp.c, lhs_node); | 2978 | try Tag.div_trunc.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node }); |
| 3787 | _ = try appendToken(rp.c, .Comma, ","); | 2979 | |
| 3788 | builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value); | 2980 | const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, ref_node, builtin, .used); |
| 3789 | builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | 2981 | try block_scope.statements.append(assign); |
| 3790 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 3791 | op_node.* = .{ | ||
| 3792 | .base = .{ .tag = .Assign }, | ||
| 3793 | .op_token = op_token, | ||
| 3794 | .lhs = ref_node, | ||
| 3795 | .rhs = &builtin_node.base, | ||
| 3796 | }; | ||
| 3797 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 3798 | try block_scope.statements.append(&op_node.base); | ||
| 3799 | } else { | 2982 | } else { |
| 3800 | const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes); | 2983 | var rhs_node = try transExpr(c, &block_scope.base, rhs, .used); |
| 3801 | var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); | ||
| 3802 | 2984 | ||
| 3803 | if (is_shift or requires_int_cast) { | 2985 | if (is_shift or requires_int_cast) { |
| 3804 | const cast_node = try rp.c.createBuiltinCall("@intCast", 2); | 2986 | // @intCast(rhs) |
| 3805 | const cast_to_type = if (is_shift) | 2987 | const cast_to_type = if (is_shift) |
| 3806 | try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc) | 2988 | try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc) |
| 3807 | else | 2989 | else |
| 3808 | try transQualType(rp, getExprQualType(rp.c, lhs), loc); | 2990 | try transQualType(c, scope, getExprQualType(c, lhs), loc); |
| 3809 | cast_node.params()[0] = cast_to_type; | ||
| 3810 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 3811 | cast_node.params()[1] = rhs_node; | ||
| 3812 | cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 3813 | rhs_node = &cast_node.base; | ||
| 3814 | } | ||
| 3815 | 2991 | ||
| 3816 | const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false); | 2992 | rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); |
| 3817 | _ = try appendToken(rp.c, .Semicolon, ";"); | 2993 | } |
| 3818 | 2994 | ||
| 3819 | const ass_eq_token = try appendToken(rp.c, .Equal, "="); | 2995 | const assign = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, rhs_node, .used); |
| 3820 | const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, ass_eq_token, rhs_bin, .used, false); | ||
| 3821 | try block_scope.statements.append(assign); | 2996 | try block_scope.statements.append(assign); |
| 3822 | } | 2997 | } |
| 3823 | 2998 | ||
| 3824 | const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node); | 2999 | const break_node = try Tag.break_val.create(c.arena, .{ |
| 3825 | try block_scope.statements.append(&break_node.base); | 3000 | .label = block_scope.label, |
| 3826 | const block_node = try block_scope.complete(rp.c); | 3001 | .val = ref_node, |
| 3827 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); | 3002 | }); |
| 3828 | grouped_expr.* = .{ | 3003 | try block_scope.statements.append(break_node); |
| 3829 | .lparen = try appendToken(rp.c, .LParen, "("), | 3004 | return block_scope.complete(c); |
| 3830 | .expr = block_node, | ||
| 3831 | .rparen = try appendToken(rp.c, .RParen, ")"), | ||
| 3832 | }; | ||
| 3833 | return &grouped_expr.base; | ||
| 3834 | } | 3005 | } |
| 3835 | 3006 | ||
| 3836 | fn transCPtrCast( | 3007 | fn transCPtrCast( |
| 3837 | rp: RestorePoint, | 3008 | c: *Context, |
| 3009 | scope: *Scope, | ||
| 3838 | loc: clang.SourceLocation, | 3010 | loc: clang.SourceLocation, |
| 3839 | dst_type: clang.QualType, | 3011 | dst_type: clang.QualType, |
| 3840 | src_type: clang.QualType, | 3012 | src_type: clang.QualType, |
| 3841 | expr: *ast.Node, | 3013 | expr: Node, |
| 3842 | ) !*ast.Node { | 3014 | ) !Node { |
| 3843 | const ty = dst_type.getTypePtr(); | 3015 | const ty = dst_type.getTypePtr(); |
| 3844 | const child_type = ty.getPointeeType(); | 3016 | const child_type = ty.getPointeeType(); |
| 3845 | const src_ty = src_type.getTypePtr(); | 3017 | const src_ty = src_type.getTypePtr(); |
| 3846 | const src_child_type = src_ty.getPointeeType(); | 3018 | const src_child_type = src_ty.getPointeeType(); |
| 3019 | const dst_type_node = try transType(c, scope, ty, loc); | ||
| 3847 | 3020 | ||
| 3848 | if ((src_child_type.isConstQualified() and | 3021 | if ((src_child_type.isConstQualified() and |
| 3849 | !child_type.isConstQualified()) or | 3022 | !child_type.isConstQualified()) or |
| ... | @@ -3851,80 +3024,47 @@ fn transCPtrCast( | ... | @@ -3851,80 +3024,47 @@ fn transCPtrCast( |
| 3851 | !child_type.isVolatileQualified())) | 3024 | !child_type.isVolatileQualified())) |
| 3852 | { | 3025 | { |
| 3853 | // Casting away const or volatile requires us to use @intToPtr | 3026 | // Casting away const or volatile requires us to use @intToPtr |
| 3854 | const inttoptr_node = try rp.c.createBuiltinCall("@intToPtr", 2); | 3027 | const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr); |
| 3855 | const dst_type_node = try transType(rp, ty, loc); | 3028 | const int_to_ptr = try Tag.int_to_ptr.create(c.arena, .{ .lhs = dst_type_node, .rhs = ptr_to_int }); |
| 3856 | inttoptr_node.params()[0] = dst_type_node; | 3029 | return int_to_ptr; |
| 3857 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 3858 | |||
| 3859 | const ptrtoint_node = try rp.c.createBuiltinCall("@ptrToInt", 1); | ||
| 3860 | ptrtoint_node.params()[0] = expr; | ||
| 3861 | ptrtoint_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 3862 | |||
| 3863 | inttoptr_node.params()[1] = &ptrtoint_node.base; | ||
| 3864 | inttoptr_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 3865 | return &inttoptr_node.base; | ||
| 3866 | } else { | 3030 | } else { |
| 3867 | // Implicit downcasting from higher to lower alignment values is forbidden, | 3031 | // Implicit downcasting from higher to lower alignment values is forbidden, |
| 3868 | // use @alignCast to side-step this problem | 3032 | // use @alignCast to side-step this problem |
| 3869 | const ptrcast_node = try rp.c.createBuiltinCall("@ptrCast", 2); | 3033 | const rhs = if (qualTypeCanon(child_type).isVoidType()) |
| 3870 | const dst_type_node = try transType(rp, ty, loc); | ||
| 3871 | ptrcast_node.params()[0] = dst_type_node; | ||
| 3872 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 3873 | |||
| 3874 | if (qualTypeCanon(child_type).isVoidType()) { | ||
| 3875 | // void has 1-byte alignment, so @alignCast is not needed | 3034 | // void has 1-byte alignment, so @alignCast is not needed |
| 3876 | ptrcast_node.params()[1] = expr; | 3035 | expr |
| 3877 | } else if (typeIsOpaque(rp.c, qualTypeCanon(child_type), loc)) { | 3036 | else if (typeIsOpaque(c, qualTypeCanon(child_type), loc)) |
| 3878 | // For opaque types a ptrCast is enough | 3037 | // For opaque types a ptrCast is enough |
| 3879 | ptrcast_node.params()[1] = expr; | 3038 | expr |
| 3880 | } else { | 3039 | else blk: { |
| 3881 | const aligncast_node = try rp.c.createBuiltinCall("@alignCast", 2); | 3040 | const child_type_node = try transQualType(c, scope, child_type, loc); |
| 3882 | const alignof_node = try rp.c.createBuiltinCall("@alignOf", 1); | 3041 | const alignof = try Tag.alignof.create(c.arena, child_type_node); |
| 3883 | const child_type_node = try transQualType(rp, child_type, loc); | 3042 | const align_cast = try Tag.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr }); |
| 3884 | alignof_node.params()[0] = child_type_node; | 3043 | break :blk align_cast; |
| 3885 | alignof_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | 3044 | }; |
| 3886 | aligncast_node.params()[0] = &alignof_node.base; | 3045 | return Tag.ptr_cast.create(c.arena, .{ .lhs = dst_type_node, .rhs = rhs }); |
| 3887 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 3888 | aligncast_node.params()[1] = expr; | ||
| 3889 | aligncast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 3890 | ptrcast_node.params()[1] = &aligncast_node.base; | ||
| 3891 | } | ||
| 3892 | ptrcast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 3893 | |||
| 3894 | return &ptrcast_node.base; | ||
| 3895 | } | 3046 | } |
| 3896 | } | 3047 | } |
| 3897 | 3048 | ||
| 3898 | fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node { | 3049 | fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node { |
| 3899 | const break_scope = scope.getBreakableScope(); | ||
| 3900 | const label_text: ?[]const u8 = if (break_scope.id == .Switch) blk: { | ||
| 3901 | const swtch = @fieldParentPtr(Scope.Switch, "base", break_scope); | ||
| 3902 | const block_scope = try scope.findBlockScope(rp.c); | ||
| 3903 | swtch.switch_label = try block_scope.makeMangledName(rp.c, "switch"); | ||
| 3904 | break :blk swtch.switch_label; | ||
| 3905 | } else | ||
| 3906 | null; | ||
| 3907 | |||
| 3908 | var cf = try CtrlFlow.init(rp.c, .Break, label_text); | ||
| 3909 | const br = try cf.finish(null); | ||
| 3910 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 3911 | return &br.base; | ||
| 3912 | } | ||
| 3913 | |||
| 3914 | fn transFloatingLiteral(rp: RestorePoint, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!*ast.Node { | ||
| 3915 | // TODO use something more accurate | 3050 | // TODO use something more accurate |
| 3916 | const dbl = stmt.getValueAsApproximateDouble(); | 3051 | var dbl = stmt.getValueAsApproximateDouble(); |
| 3917 | const node = try rp.c.arena.create(ast.Node.OneToken); | 3052 | const is_negative = dbl < 0; |
| 3918 | node.* = .{ | 3053 | if (is_negative) dbl = -dbl; |
| 3919 | .base = .{ .tag = .FloatLiteral }, | 3054 | const str = try std.fmt.allocPrint(c.arena, "{d}", .{dbl}); |
| 3920 | .token = try appendTokenFmt(rp.c, .FloatLiteral, "{d}", .{dbl}), | 3055 | var node = if (dbl == std.math.floor(dbl)) |
| 3921 | }; | 3056 | try Tag.integer_literal.create(c.arena, str) |
| 3922 | return maybeSuppressResult(rp, scope, used, &node.base); | 3057 | else |
| 3058 | try Tag.float_literal.create(c.arena, str); | ||
| 3059 | if (is_negative) node = try Tag.negate.create(c.arena, node); | ||
| 3060 | return maybeSuppressResult(c, scope, used, node); | ||
| 3923 | } | 3061 | } |
| 3924 | 3062 | ||
| 3925 | fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.BinaryConditionalOperator, used: ResultUsed) TransError!*ast.Node { | 3063 | fn transBinaryConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.BinaryConditionalOperator, used: ResultUsed) TransError!Node { |
| 3926 | // GNU extension of the ternary operator where the middle expression is | 3064 | // GNU extension of the ternary operator where the middle expression is |
| 3927 | // omitted, the conditition itself is returned if it evaluates to true | 3065 | // omitted, the conditition itself is returned if it evaluates to true |
| 3066 | const qt = @ptrCast(*const clang.Expr, stmt).getType(); | ||
| 3067 | const res_is_bool = qualTypeIsBoolean(qt); | ||
| 3928 | const casted_stmt = @ptrCast(*const clang.AbstractConditionalOperator, stmt); | 3068 | const casted_stmt = @ptrCast(*const clang.AbstractConditionalOperator, stmt); |
| 3929 | const cond_expr = casted_stmt.getCond(); | 3069 | const cond_expr = casted_stmt.getCond(); |
| 3930 | const true_expr = casted_stmt.getTrueExpr(); | 3070 | const true_expr = casted_stmt.getTrueExpr(); |
| ... | @@ -3935,184 +3075,149 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const | ... | @@ -3935,184 +3075,149 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const |
| 3935 | // const _cond_temp = (cond_expr); | 3075 | // const _cond_temp = (cond_expr); |
| 3936 | // break :blk if (_cond_temp) _cond_temp else (false_expr); | 3076 | // break :blk if (_cond_temp) _cond_temp else (false_expr); |
| 3937 | // }) | 3077 | // }) |
| 3938 | const lparen = try appendToken(rp.c, .LParen, "("); | 3078 | var block_scope = try Scope.Block.init(c, scope, true); |
| 3939 | |||
| 3940 | var block_scope = try Scope.Block.init(rp.c, scope, true); | ||
| 3941 | defer block_scope.deinit(); | 3079 | defer block_scope.deinit(); |
| 3942 | 3080 | ||
| 3943 | const mangled_name = try block_scope.makeMangledName(rp.c, "cond_temp"); | 3081 | const mangled_name = try block_scope.makeMangledName(c, "cond_temp"); |
| 3944 | const mut_tok = try appendToken(rp.c, .Keyword_const, "const"); | 3082 | const init_node = try transExpr(c, &block_scope.base, cond_expr, .used); |
| 3945 | const name_tok = try appendIdentifier(rp.c, mangled_name); | 3083 | const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = mangled_name, .init = init_node }); |
| 3946 | const eq_token = try appendToken(rp.c, .Equal, "="); | 3084 | try block_scope.statements.append(ref_decl); |
| 3947 | const init_node = try transExpr(rp, &block_scope.base, cond_expr, .used, .r_value); | ||
| 3948 | const semicolon_token = try appendToken(rp.c, .Semicolon, ";"); | ||
| 3949 | const tmp_var = try ast.Node.VarDecl.create(rp.c.arena, .{ | ||
| 3950 | .name_token = name_tok, | ||
| 3951 | .mut_token = mut_tok, | ||
| 3952 | .semicolon_token = semicolon_token, | ||
| 3953 | }, .{ | ||
| 3954 | .eq_token = eq_token, | ||
| 3955 | .init_node = init_node, | ||
| 3956 | }); | ||
| 3957 | try block_scope.statements.append(&tmp_var.base); | ||
| 3958 | |||
| 3959 | var break_node_tmp = try CtrlFlow.initToken(rp.c, .Break, block_scope.label); | ||
| 3960 | 3085 | ||
| 3961 | const if_node = try transCreateNodeIf(rp.c); | ||
| 3962 | var cond_scope = Scope.Condition{ | 3086 | var cond_scope = Scope.Condition{ |
| 3963 | .base = .{ | 3087 | .base = .{ |
| 3964 | .parent = &block_scope.base, | 3088 | .parent = &block_scope.base, |
| 3965 | .id = .Condition, | 3089 | .id = .condition, |
| 3966 | }, | 3090 | }, |
| 3967 | }; | 3091 | }; |
| 3968 | defer cond_scope.deinit(); | 3092 | defer cond_scope.deinit(); |
| 3969 | const tmp_var_node = try transCreateNodeIdentifier(rp.c, mangled_name); | 3093 | |
| 3970 | 3094 | const cond_ident = try Tag.identifier.create(c.arena, mangled_name); | |
| 3971 | const ty = getExprQualType(rp.c, cond_expr).getTypePtr(); | 3095 | const ty = getExprQualType(c, cond_expr).getTypePtr(); |
| 3972 | const cond_node = try finishBoolExpr(rp, &cond_scope.base, cond_expr.getBeginLoc(), ty, tmp_var_node, used); | 3096 | const cond_node = try finishBoolExpr(c, &cond_scope.base, cond_expr.getBeginLoc(), ty, cond_ident, .used); |
| 3973 | if_node.condition = cond_node; | 3097 | var then_body = cond_ident; |
| 3974 | _ = try appendToken(rp.c, .RParen, ")"); | 3098 | if (!res_is_bool and isBoolRes(init_node)) { |
| 3975 | 3099 | then_body = try Tag.bool_to_int.create(c.arena, then_body); | |
| 3976 | if_node.body = try transCreateNodeIdentifier(rp.c, mangled_name); | 3100 | } |
| 3977 | if_node.@"else" = try transCreateNodeElse(rp.c); | 3101 | |
| 3978 | if_node.@"else".?.body = try transExpr(rp, &block_scope.base, false_expr, .used, .r_value); | 3102 | var else_body = try transExpr(c, &block_scope.base, false_expr, .used); |
| 3979 | _ = try appendToken(rp.c, .Semicolon, ";"); | 3103 | if (!res_is_bool and isBoolRes(else_body)) { |
| 3980 | 3104 | else_body = try Tag.bool_to_int.create(c.arena, else_body); | |
| 3981 | const break_node = try break_node_tmp.finish(&if_node.base); | 3105 | } |
| 3982 | _ = try appendToken(rp.c, .Semicolon, ";"); | 3106 | const if_node = try Tag.@"if".create(c.arena, .{ |
| 3983 | try block_scope.statements.append(&break_node.base); | 3107 | .cond = cond_node, |
| 3984 | const block_node = try block_scope.complete(rp.c); | 3108 | .then = then_body, |
| 3985 | 3109 | .@"else" = else_body, | |
| 3986 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); | 3110 | }); |
| 3987 | grouped_expr.* = .{ | 3111 | const break_node = try Tag.break_val.create(c.arena, .{ |
| 3988 | .lparen = lparen, | 3112 | .label = block_scope.label, |
| 3989 | .expr = block_node, | 3113 | .val = if_node, |
| 3990 | .rparen = try appendToken(rp.c, .RParen, ")"), | 3114 | }); |
| 3991 | }; | 3115 | try block_scope.statements.append(break_node); |
| 3992 | return maybeSuppressResult(rp, scope, used, &grouped_expr.base); | 3116 | const res = try block_scope.complete(c); |
| 3117 | return maybeSuppressResult(c, scope, used, res); | ||
| 3993 | } | 3118 | } |
| 3994 | 3119 | ||
| 3995 | fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.ConditionalOperator, used: ResultUsed) TransError!*ast.Node { | 3120 | fn transConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.ConditionalOperator, used: ResultUsed) TransError!Node { |
| 3996 | const grouped = scope.id == .Condition; | ||
| 3997 | const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined; | ||
| 3998 | const if_node = try transCreateNodeIf(rp.c); | ||
| 3999 | var cond_scope = Scope.Condition{ | 3121 | var cond_scope = Scope.Condition{ |
| 4000 | .base = .{ | 3122 | .base = .{ |
| 4001 | .parent = scope, | 3123 | .parent = scope, |
| 4002 | .id = .Condition, | 3124 | .id = .condition, |
| 4003 | }, | 3125 | }, |
| 4004 | }; | 3126 | }; |
| 4005 | defer cond_scope.deinit(); | 3127 | defer cond_scope.deinit(); |
| 4006 | 3128 | ||
| 3129 | const qt = @ptrCast(*const clang.Expr, stmt).getType(); | ||
| 3130 | const res_is_bool = qualTypeIsBoolean(qt); | ||
| 4007 | const casted_stmt = @ptrCast(*const clang.AbstractConditionalOperator, stmt); | 3131 | const casted_stmt = @ptrCast(*const clang.AbstractConditionalOperator, stmt); |
| 4008 | const cond_expr = casted_stmt.getCond(); | 3132 | const cond_expr = casted_stmt.getCond(); |
| 4009 | const true_expr = casted_stmt.getTrueExpr(); | 3133 | const true_expr = casted_stmt.getTrueExpr(); |
| 4010 | const false_expr = casted_stmt.getFalseExpr(); | 3134 | const false_expr = casted_stmt.getFalseExpr(); |
| 4011 | 3135 | ||
| 4012 | if_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false); | 3136 | const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); |
| 4013 | _ = try appendToken(rp.c, .RParen, ")"); | ||
| 4014 | |||
| 4015 | if_node.body = try transExpr(rp, scope, true_expr, .used, .r_value); | ||
| 4016 | 3137 | ||
| 4017 | if_node.@"else" = try transCreateNodeElse(rp.c); | 3138 | var then_body = try transExpr(c, scope, true_expr, used); |
| 4018 | if_node.@"else".?.body = try transExpr(rp, scope, false_expr, .used, .r_value); | 3139 | if (!res_is_bool and isBoolRes(then_body)) { |
| 3140 | then_body = try Tag.bool_to_int.create(c.arena, then_body); | ||
| 3141 | } | ||
| 4019 | 3142 | ||
| 4020 | if (grouped) { | 3143 | var else_body = try transExpr(c, scope, false_expr, used); |
| 4021 | const rparen = try appendToken(rp.c, .RParen, ")"); | 3144 | if (!res_is_bool and isBoolRes(else_body)) { |
| 4022 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); | 3145 | else_body = try Tag.bool_to_int.create(c.arena, else_body); |
| 4023 | grouped_expr.* = .{ | ||
| 4024 | .lparen = lparen, | ||
| 4025 | .expr = &if_node.base, | ||
| 4026 | .rparen = rparen, | ||
| 4027 | }; | ||
| 4028 | return maybeSuppressResult(rp, scope, used, &grouped_expr.base); | ||
| 4029 | } else { | ||
| 4030 | return maybeSuppressResult(rp, scope, used, &if_node.base); | ||
| 4031 | } | 3146 | } |
| 3147 | |||
| 3148 | const if_node = try Tag.@"if".create(c.arena, .{ | ||
| 3149 | .cond = cond, | ||
| 3150 | .then = then_body, | ||
| 3151 | .@"else" = else_body, | ||
| 3152 | }); | ||
| 3153 | // Clang inserts ImplicitCast(ToVoid)'s to both rhs and lhs so we don't need to supress the result here. | ||
| 3154 | return if_node; | ||
| 4032 | } | 3155 | } |
| 4033 | 3156 | ||
| 4034 | fn maybeSuppressResult( | 3157 | fn maybeSuppressResult( |
| 4035 | rp: RestorePoint, | 3158 | c: *Context, |
| 4036 | scope: *Scope, | 3159 | scope: *Scope, |
| 4037 | used: ResultUsed, | 3160 | used: ResultUsed, |
| 4038 | result: *ast.Node, | 3161 | result: Node, |
| 4039 | ) TransError!*ast.Node { | 3162 | ) TransError!Node { |
| 4040 | if (used == .used) return result; | 3163 | if (used == .used) return result; |
| 4041 | if (scope.id != .Condition) { | 3164 | return Tag.discard.create(c.arena, result); |
| 4042 | // NOTE: This is backwards, but the semicolon must immediately follow the node. | ||
| 4043 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 4044 | } else { // TODO is there a way to avoid this hack? | ||
| 4045 | // this parenthesis must come immediately following the node | ||
| 4046 | _ = try appendToken(rp.c, .RParen, ")"); | ||
| 4047 | // these need to come before _ | ||
| 4048 | _ = try appendToken(rp.c, .Colon, ":"); | ||
| 4049 | _ = try appendToken(rp.c, .LParen, "("); | ||
| 4050 | } | ||
| 4051 | const lhs = try transCreateNodeIdentifier(rp.c, "_"); | ||
| 4052 | const op_token = try appendToken(rp.c, .Equal, "="); | ||
| 4053 | const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp); | ||
| 4054 | op_node.* = .{ | ||
| 4055 | .base = .{ .tag = .Assign }, | ||
| 4056 | .op_token = op_token, | ||
| 4057 | .lhs = lhs, | ||
| 4058 | .rhs = result, | ||
| 4059 | }; | ||
| 4060 | return &op_node.base; | ||
| 4061 | } | 3165 | } |
| 4062 | 3166 | ||
| 4063 | fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void { | 3167 | fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void { |
| 4064 | try c.root_decls.append(c.gpa, decl_node); | ||
| 4065 | _ = try c.global_scope.sym_table.put(name, decl_node); | 3168 | _ = try c.global_scope.sym_table.put(name, decl_node); |
| 3169 | try c.global_scope.nodes.append(decl_node); | ||
| 4066 | } | 3170 | } |
| 4067 | 3171 | ||
| 4068 | /// Translate a qual type for a variable with an initializer. The initializer | 3172 | /// Translate a qual type for a variable with an initializer. The initializer |
| 4069 | /// only matters for incomplete arrays, since the size of the array is determined | 3173 | /// only matters for incomplete arrays, since the size of the array is determined |
| 4070 | /// by the size of the initializer | 3174 | /// by the size of the initializer |
| 4071 | fn transQualTypeInitialized( | 3175 | fn transQualTypeInitialized( |
| 4072 | rp: RestorePoint, | 3176 | c: *Context, |
| 3177 | scope: *Scope, | ||
| 4073 | qt: clang.QualType, | 3178 | qt: clang.QualType, |
| 4074 | decl_init: *const clang.Expr, | 3179 | decl_init: *const clang.Expr, |
| 4075 | source_loc: clang.SourceLocation, | 3180 | source_loc: clang.SourceLocation, |
| 4076 | ) TypeError!*ast.Node { | 3181 | ) TypeError!Node { |
| 4077 | const ty = qt.getTypePtr(); | 3182 | const ty = qt.getTypePtr(); |
| 4078 | if (ty.getTypeClass() == .IncompleteArray) { | 3183 | if (ty.getTypeClass() == .IncompleteArray) { |
| 4079 | const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty); | 3184 | const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty); |
| 4080 | const elem_ty = incomplete_array_ty.getElementType().getTypePtr(); | 3185 | const elem_ty = try transType(c, scope, incomplete_array_ty.getElementType().getTypePtr(), source_loc); |
| 4081 | 3186 | ||
| 4082 | switch (decl_init.getStmtClass()) { | 3187 | switch (decl_init.getStmtClass()) { |
| 4083 | .StringLiteralClass => { | 3188 | .StringLiteralClass => { |
| 4084 | const string_lit = @ptrCast(*const clang.StringLiteral, decl_init); | 3189 | const string_lit = @ptrCast(*const clang.StringLiteral, decl_init); |
| 4085 | const string_lit_size = string_lit.getLength() + 1; // +1 for null terminator | 3190 | const string_lit_size = string_lit.getLength() + 1; // +1 for null terminator |
| 4086 | const array_size = @intCast(usize, string_lit_size); | 3191 | const array_size = @intCast(usize, string_lit_size); |
| 4087 | return transCreateNodeArrayType(rp, source_loc, elem_ty, array_size); | 3192 | return Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty }); |
| 4088 | }, | 3193 | }, |
| 4089 | .InitListExprClass => { | 3194 | .InitListExprClass => { |
| 4090 | const init_expr = @ptrCast(*const clang.InitListExpr, decl_init); | 3195 | const init_expr = @ptrCast(*const clang.InitListExpr, decl_init); |
| 4091 | const size = init_expr.getNumInits(); | 3196 | const size = init_expr.getNumInits(); |
| 4092 | return transCreateNodeArrayType(rp, source_loc, elem_ty, size); | 3197 | return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_ty }); |
| 4093 | }, | 3198 | }, |
| 4094 | else => {}, | 3199 | else => {}, |
| 4095 | } | 3200 | } |
| 4096 | } | 3201 | } |
| 4097 | return transQualType(rp, qt, source_loc); | 3202 | return transQualType(c, scope, qt, source_loc); |
| 4098 | } | 3203 | } |
| 4099 | 3204 | ||
| 4100 | fn transQualType(rp: RestorePoint, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!*ast.Node { | 3205 | fn transQualType(c: *Context, scope: *Scope, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!Node { |
| 4101 | return transType(rp, qt.getTypePtr(), source_loc); | 3206 | return transType(c, scope, qt.getTypePtr(), source_loc); |
| 4102 | } | 3207 | } |
| 4103 | 3208 | ||
| 4104 | /// Produces a Zig AST node by translating a Clang QualType, respecting the width, but modifying the signed-ness. | 3209 | /// Produces a Zig AST node by translating a Clang QualType, respecting the width, but modifying the signed-ness. |
| 4105 | /// Asserts the type is an integer. | 3210 | /// Asserts the type is an integer. |
| 4106 | fn transQualTypeIntWidthOf(c: *Context, ty: clang.QualType, is_signed: bool) TypeError!*ast.Node { | 3211 | fn transQualTypeIntWidthOf(c: *Context, ty: clang.QualType, is_signed: bool) TypeError!Node { |
| 4107 | return transTypeIntWidthOf(c, qualTypeCanon(ty), is_signed); | 3212 | return transTypeIntWidthOf(c, qualTypeCanon(ty), is_signed); |
| 4108 | } | 3213 | } |
| 4109 | 3214 | ||
| 4110 | /// Produces a Zig AST node by translating a Clang Type, respecting the width, but modifying the signed-ness. | 3215 | /// Produces a Zig AST node by translating a Clang Type, respecting the width, but modifying the signed-ness. |
| 4111 | /// Asserts the type is an integer. | 3216 | /// Asserts the type is an integer. |
| 4112 | fn transTypeIntWidthOf(c: *Context, ty: *const clang.Type, is_signed: bool) TypeError!*ast.Node { | 3217 | fn transTypeIntWidthOf(c: *Context, ty: *const clang.Type, is_signed: bool) TypeError!Node { |
| 4113 | assert(ty.getTypeClass() == .Builtin); | 3218 | assert(ty.getTypeClass() == .Builtin); |
| 4114 | const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); | 3219 | const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); |
| 4115 | return transCreateNodeIdentifier(c, switch (builtin_ty.getKind()) { | 3220 | return Tag.type.create(c.arena, switch (builtin_ty.getKind()) { |
| 4116 | .Char_U, .Char_S, .UChar, .SChar, .Char8 => if (is_signed) "i8" else "u8", | 3221 | .Char_U, .Char_S, .UChar, .SChar, .Char8 => if (is_signed) "i8" else "u8", |
| 4117 | .UShort, .Short => if (is_signed) "c_short" else "c_ushort", | 3222 | .UShort, .Short => if (is_signed) "c_short" else "c_ushort", |
| 4118 | .UInt, .Int => if (is_signed) "c_int" else "c_uint", | 3223 | .UInt, .Int => if (is_signed) "c_int" else "c_uint", |
| ... | @@ -4141,7 +3246,7 @@ fn qualTypeIsBoolean(qt: clang.QualType) bool { | ... | @@ -4141,7 +3246,7 @@ fn qualTypeIsBoolean(qt: clang.QualType) bool { |
| 4141 | return qualTypeCanon(qt).isBooleanType(); | 3246 | return qualTypeCanon(qt).isBooleanType(); |
| 4142 | } | 3247 | } |
| 4143 | 3248 | ||
| 4144 | fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.SourceLocation) !u32 { | 3249 | fn qualTypeIntBitWidth(c: *Context, qt: clang.QualType) !u32 { |
| 4145 | const ty = qt.getTypePtr(); | 3250 | const ty = qt.getTypePtr(); |
| 4146 | 3251 | ||
| 4147 | switch (ty.getTypeClass()) { | 3252 | switch (ty.getTypeClass()) { |
| ... | @@ -4165,7 +3270,7 @@ fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.S | ... | @@ -4165,7 +3270,7 @@ fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.S |
| 4165 | .Typedef => { | 3270 | .Typedef => { |
| 4166 | const typedef_ty = @ptrCast(*const clang.TypedefType, ty); | 3271 | const typedef_ty = @ptrCast(*const clang.TypedefType, ty); |
| 4167 | const typedef_decl = typedef_ty.getDecl(); | 3272 | const typedef_decl = typedef_ty.getDecl(); |
| 4168 | const type_name = try rp.c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin()); | 3273 | const type_name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin()); |
| 4169 | 3274 | ||
| 4170 | if (mem.eql(u8, type_name, "uint8_t") or mem.eql(u8, type_name, "int8_t")) { | 3275 | if (mem.eql(u8, type_name, "uint8_t") or mem.eql(u8, type_name, "int8_t")) { |
| 4171 | return 8; | 3276 | return 8; |
| ... | @@ -4181,55 +3286,19 @@ fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.S | ... | @@ -4181,55 +3286,19 @@ fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.S |
| 4181 | }, | 3286 | }, |
| 4182 | else => return 0, | 3287 | else => return 0, |
| 4183 | } | 3288 | } |
| 4184 | |||
| 4185 | unreachable; | ||
| 4186 | } | 3289 | } |
| 4187 | 3290 | ||
| 4188 | fn qualTypeToLog2IntRef(rp: RestorePoint, qt: clang.QualType, source_loc: clang.SourceLocation) !*ast.Node { | 3291 | fn qualTypeToLog2IntRef(c: *Context, scope: *Scope, qt: clang.QualType, source_loc: clang.SourceLocation) !Node { |
| 4189 | const int_bit_width = try qualTypeIntBitWidth(rp, qt, source_loc); | 3292 | const int_bit_width = try qualTypeIntBitWidth(c, qt); |
| 4190 | 3293 | ||
| 4191 | if (int_bit_width != 0) { | 3294 | if (int_bit_width != 0) { |
| 4192 | // we can perform the log2 now. | 3295 | // we can perform the log2 now. |
| 4193 | const cast_bit_width = math.log2_int(u64, int_bit_width); | 3296 | const cast_bit_width = math.log2_int(u64, int_bit_width); |
| 4194 | const node = try rp.c.arena.create(ast.Node.OneToken); | 3297 | return Tag.log2_int_type.create(c.arena, cast_bit_width); |
| 4195 | node.* = .{ | 3298 | } |
| 4196 | .base = .{ .tag = .IntegerLiteral }, | ||
| 4197 | .token = try appendTokenFmt(rp.c, .Identifier, "u{d}", .{cast_bit_width}), | ||
| 4198 | }; | ||
| 4199 | return &node.base; | ||
| 4200 | } | ||
| 4201 | |||
| 4202 | const zig_type_node = try transQualType(rp, qt, source_loc); | ||
| 4203 | |||
| 4204 | // @import("std").math.Log2Int(c_long); | ||
| 4205 | // | ||
| 4206 | // FnCall | ||
| 4207 | // FieldAccess | ||
| 4208 | // FieldAccess | ||
| 4209 | // FnCall (.builtin = true) | ||
| 4210 | // Symbol "import" | ||
| 4211 | // StringLiteral "std" | ||
| 4212 | // Symbol "math" | ||
| 4213 | // Symbol "Log2Int" | ||
| 4214 | // Symbol <zig_type_node> (var from above) | ||
| 4215 | |||
| 4216 | const import_fn_call = try rp.c.createBuiltinCall("@import", 1); | ||
| 4217 | const std_token = try appendToken(rp.c, .StringLiteral, "\"std\""); | ||
| 4218 | const std_node = try rp.c.arena.create(ast.Node.OneToken); | ||
| 4219 | std_node.* = .{ | ||
| 4220 | .base = .{ .tag = .StringLiteral }, | ||
| 4221 | .token = std_token, | ||
| 4222 | }; | ||
| 4223 | import_fn_call.params()[0] = &std_node.base; | ||
| 4224 | import_fn_call.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 4225 | |||
| 4226 | const inner_field_access = try transCreateNodeFieldAccess(rp.c, &import_fn_call.base, "math"); | ||
| 4227 | const outer_field_access = try transCreateNodeFieldAccess(rp.c, inner_field_access, "Log2Int"); | ||
| 4228 | const log2int_fn_call = try rp.c.createCall(outer_field_access, 1); | ||
| 4229 | log2int_fn_call.params()[0] = zig_type_node; | ||
| 4230 | log2int_fn_call.rtoken = try appendToken(rp.c, .RParen, ")"); | ||
| 4231 | 3299 | ||
| 4232 | return &log2int_fn_call.base; | 3300 | const zig_type = try transQualType(c, scope, qt, source_loc); |
| 3301 | return Tag.std_math_Log2Int.create(c.arena, zig_type); | ||
| 4233 | } | 3302 | } |
| 4234 | 3303 | ||
| 4235 | fn qualTypeChildIsFnProto(qt: clang.QualType) bool { | 3304 | fn qualTypeChildIsFnProto(qt: clang.QualType) bool { |
| ... | @@ -4393,28 +3462,22 @@ fn cIsLongLongInteger(qt: clang.QualType) bool { | ... | @@ -4393,28 +3462,22 @@ fn cIsLongLongInteger(qt: clang.QualType) bool { |
| 4393 | }; | 3462 | }; |
| 4394 | } | 3463 | } |
| 4395 | fn transCreateNodeAssign( | 3464 | fn transCreateNodeAssign( |
| 4396 | rp: RestorePoint, | 3465 | c: *Context, |
| 4397 | scope: *Scope, | 3466 | scope: *Scope, |
| 4398 | result_used: ResultUsed, | 3467 | result_used: ResultUsed, |
| 4399 | lhs: *const clang.Expr, | 3468 | lhs: *const clang.Expr, |
| 4400 | rhs: *const clang.Expr, | 3469 | rhs: *const clang.Expr, |
| 4401 | ) !*ast.Node { | 3470 | ) !Node { |
| 4402 | // common case | 3471 | // common case |
| 4403 | // c: lhs = rhs | 3472 | // c: lhs = rhs |
| 4404 | // zig: lhs = rhs | 3473 | // zig: lhs = rhs |
| 4405 | if (result_used == .unused) { | 3474 | if (result_used == .unused) { |
| 4406 | const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value); | 3475 | const lhs_node = try transExpr(c, scope, lhs, .used); |
| 4407 | const eq_token = try appendToken(rp.c, .Equal, "="); | 3476 | var rhs_node = try transExprCoercing(c, scope, rhs, .used); |
| 4408 | var rhs_node = try transExprCoercing(rp, scope, rhs, .used, .r_value); | ||
| 4409 | if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) { | 3477 | if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) { |
| 4410 | const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1); | 3478 | rhs_node = try Tag.bool_to_int.create(c.arena, rhs_node); |
| 4411 | builtin_node.params()[0] = rhs_node; | ||
| 4412 | builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 4413 | rhs_node = &builtin_node.base; | ||
| 4414 | } | 3479 | } |
| 4415 | if (scope.id != .Condition) | 3480 | return transCreateNodeInfixOp(c, scope, .assign, lhs_node, rhs_node, .used); |
| 4416 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 4417 | return transCreateNodeInfixOp(rp, scope, lhs_node, .Assign, eq_token, rhs_node, .used, false); | ||
| 4418 | } | 3481 | } |
| 4419 | 3482 | ||
| 4420 | // worst case | 3483 | // worst case |
| ... | @@ -4424,176 +3487,62 @@ fn transCreateNodeAssign( | ... | @@ -4424,176 +3487,62 @@ fn transCreateNodeAssign( |
| 4424 | // zig: lhs = _tmp; | 3487 | // zig: lhs = _tmp; |
| 4425 | // zig: break :blk _tmp | 3488 | // zig: break :blk _tmp |
| 4426 | // zig: }) | 3489 | // zig: }) |
| 4427 | var block_scope = try Scope.Block.init(rp.c, scope, true); | 3490 | var block_scope = try Scope.Block.init(c, scope, true); |
| 4428 | defer block_scope.deinit(); | 3491 | defer block_scope.deinit(); |
| 4429 | 3492 | ||
| 4430 | const tmp = try block_scope.makeMangledName(rp.c, "tmp"); | 3493 | const tmp = try block_scope.makeMangledName(c, "tmp"); |
| 4431 | const mut_tok = try appendToken(rp.c, .Keyword_const, "const"); | 3494 | const rhs_node = try transExpr(c, &block_scope.base, rhs, .used); |
| 4432 | const name_tok = try appendIdentifier(rp.c, tmp); | 3495 | const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = rhs_node }); |
| 4433 | const eq_token = try appendToken(rp.c, .Equal, "="); | 3496 | try block_scope.statements.append(tmp_decl); |
| 4434 | var rhs_node = try transExpr(rp, &block_scope.base, rhs, .used, .r_value); | ||
| 4435 | if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) { | ||
| 4436 | const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1); | ||
| 4437 | builtin_node.params()[0] = rhs_node; | ||
| 4438 | builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 4439 | rhs_node = &builtin_node.base; | ||
| 4440 | } | ||
| 4441 | const init_node = rhs_node; | ||
| 4442 | const semicolon_token = try appendToken(rp.c, .Semicolon, ";"); | ||
| 4443 | const node = try ast.Node.VarDecl.create(rp.c.arena, .{ | ||
| 4444 | .name_token = name_tok, | ||
| 4445 | .mut_token = mut_tok, | ||
| 4446 | .semicolon_token = semicolon_token, | ||
| 4447 | }, .{ | ||
| 4448 | .eq_token = eq_token, | ||
| 4449 | .init_node = init_node, | ||
| 4450 | }); | ||
| 4451 | try block_scope.statements.append(&node.base); | ||
| 4452 | |||
| 4453 | const lhs_node = try transExpr(rp, &block_scope.base, lhs, .used, .l_value); | ||
| 4454 | const lhs_eq_token = try appendToken(rp.c, .Equal, "="); | ||
| 4455 | const ident = try transCreateNodeIdentifier(rp.c, tmp); | ||
| 4456 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 4457 | 3497 | ||
| 4458 | const assign = try transCreateNodeInfixOp(rp, &block_scope.base, lhs_node, .Assign, lhs_eq_token, ident, .used, false); | 3498 | const lhs_node = try transExpr(c, &block_scope.base, lhs, .used); |
| 3499 | const tmp_ident = try Tag.identifier.create(c.arena, tmp); | ||
| 3500 | const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, lhs_node, tmp_ident, .used); | ||
| 4459 | try block_scope.statements.append(assign); | 3501 | try block_scope.statements.append(assign); |
| 4460 | 3502 | ||
| 4461 | const break_node = blk: { | 3503 | const break_node = try Tag.break_val.create(c.arena, .{ |
| 4462 | var tmp_ctrl_flow = try CtrlFlow.init(rp.c, .Break, tokenSlice(rp.c, block_scope.label.?)); | 3504 | .label = block_scope.label, |
| 4463 | const rhs_expr = try transCreateNodeIdentifier(rp.c, tmp); | 3505 | .val = tmp_ident, |
| 4464 | break :blk try tmp_ctrl_flow.finish(rhs_expr); | 3506 | }); |
| 4465 | }; | 3507 | try block_scope.statements.append(break_node); |
| 4466 | _ = try appendToken(rp.c, .Semicolon, ";"); | 3508 | return block_scope.complete(c); |
| 4467 | try block_scope.statements.append(&break_node.base); | ||
| 4468 | const block_node = try block_scope.complete(rp.c); | ||
| 4469 | // semicolon must immediately follow rbrace because it is the last token in a block | ||
| 4470 | _ = try appendToken(rp.c, .Semicolon, ";"); | ||
| 4471 | return block_node; | ||
| 4472 | } | ||
| 4473 | |||
| 4474 | fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node { | ||
| 4475 | const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 4476 | field_access_node.* = .{ | ||
| 4477 | .base = .{ .tag = .Period }, | ||
| 4478 | .op_token = try appendToken(c, .Period, "."), | ||
| 4479 | .lhs = container, | ||
| 4480 | .rhs = try transCreateNodeIdentifier(c, field_name), | ||
| 4481 | }; | ||
| 4482 | return &field_access_node.base; | ||
| 4483 | } | ||
| 4484 | |||
| 4485 | fn transCreateNodeSimplePrefixOp( | ||
| 4486 | c: *Context, | ||
| 4487 | comptime tag: ast.Node.Tag, | ||
| 4488 | op_tok_id: std.zig.Token.Id, | ||
| 4489 | bytes: []const u8, | ||
| 4490 | ) !*ast.Node.SimplePrefixOp { | ||
| 4491 | const node = try c.arena.create(ast.Node.SimplePrefixOp); | ||
| 4492 | node.* = .{ | ||
| 4493 | .base = .{ .tag = tag }, | ||
| 4494 | .op_token = try appendToken(c, op_tok_id, bytes), | ||
| 4495 | .rhs = undefined, // translate and set afterward | ||
| 4496 | }; | ||
| 4497 | return node; | ||
| 4498 | } | 3509 | } |
| 4499 | 3510 | ||
| 4500 | fn transCreateNodeInfixOp( | 3511 | fn transCreateNodeInfixOp( |
| 4501 | rp: RestorePoint, | 3512 | c: *Context, |
| 4502 | scope: *Scope, | 3513 | scope: *Scope, |
| 4503 | lhs_node: *ast.Node, | 3514 | op: Tag, |
| 4504 | op: ast.Node.Tag, | 3515 | lhs: Node, |
| 4505 | op_token: ast.TokenIndex, | 3516 | rhs: Node, |
| 4506 | rhs_node: *ast.Node, | ||
| 4507 | used: ResultUsed, | 3517 | used: ResultUsed, |
| 4508 | grouped: bool, | 3518 | ) !Node { |
| 4509 | ) !*ast.Node { | 3519 | const payload = try c.arena.create(ast.Payload.BinOp); |
| 4510 | var lparen = if (grouped) | 3520 | payload.* = .{ |
| 4511 | try appendToken(rp.c, .LParen, "(") | ||
| 4512 | else | ||
| 4513 | null; | ||
| 4514 | const node = try rp.c.arena.create(ast.Node.SimpleInfixOp); | ||
| 4515 | node.* = .{ | ||
| 4516 | .base = .{ .tag = op }, | 3521 | .base = .{ .tag = op }, |
| 4517 | .op_token = op_token, | 3522 | .data = .{ |
| 4518 | .lhs = lhs_node, | 3523 | .lhs = lhs, |
| 4519 | .rhs = rhs_node, | 3524 | .rhs = rhs, |
| 4520 | }; | 3525 | }, |
| 4521 | if (!grouped) return maybeSuppressResult(rp, scope, used, &node.base); | ||
| 4522 | const rparen = try appendToken(rp.c, .RParen, ")"); | ||
| 4523 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); | ||
| 4524 | grouped_expr.* = .{ | ||
| 4525 | .lparen = lparen.?, | ||
| 4526 | .expr = &node.base, | ||
| 4527 | .rparen = rparen, | ||
| 4528 | }; | 3526 | }; |
| 4529 | return maybeSuppressResult(rp, scope, used, &grouped_expr.base); | 3527 | return maybeSuppressResult(c, scope, used, Node.initPayload(&payload.base)); |
| 4530 | } | 3528 | } |
| 4531 | 3529 | ||
| 4532 | fn transCreateNodeBoolInfixOp( | 3530 | fn transCreateNodeBoolInfixOp( |
| 4533 | rp: RestorePoint, | 3531 | c: *Context, |
| 4534 | scope: *Scope, | 3532 | scope: *Scope, |
| 4535 | stmt: *const clang.BinaryOperator, | 3533 | stmt: *const clang.BinaryOperator, |
| 4536 | op: ast.Node.Tag, | 3534 | op: Tag, |
| 4537 | used: ResultUsed, | 3535 | used: ResultUsed, |
| 4538 | grouped: bool, | 3536 | ) !Node { |
| 4539 | ) !*ast.Node { | 3537 | std.debug.assert(op == .@"and" or op == .@"or"); |
| 4540 | std.debug.assert(op == .BoolAnd or op == .BoolOr); | ||
| 4541 | |||
| 4542 | const lhs_hode = try transBoolExpr(rp, scope, stmt.getLHS(), .used, .l_value, true); | ||
| 4543 | const op_token = if (op == .BoolAnd) | ||
| 4544 | try appendToken(rp.c, .Keyword_and, "and") | ||
| 4545 | else | ||
| 4546 | try appendToken(rp.c, .Keyword_or, "or"); | ||
| 4547 | const rhs = try transBoolExpr(rp, scope, stmt.getRHS(), .used, .r_value, true); | ||
| 4548 | 3538 | ||
| 4549 | return transCreateNodeInfixOp( | 3539 | const lhs = try transBoolExpr(c, scope, stmt.getLHS(), .used); |
| 4550 | rp, | 3540 | const rhs = try transBoolExpr(c, scope, stmt.getRHS(), .used); |
| 4551 | scope, | ||
| 4552 | lhs_hode, | ||
| 4553 | op, | ||
| 4554 | op_token, | ||
| 4555 | rhs, | ||
| 4556 | used, | ||
| 4557 | grouped, | ||
| 4558 | ); | ||
| 4559 | } | ||
| 4560 | 3541 | ||
| 4561 | fn transCreateNodePtrType( | 3542 | return transCreateNodeInfixOp(c, scope, op, lhs, rhs, used); |
| 4562 | c: *Context, | ||
| 4563 | is_const: bool, | ||
| 4564 | is_volatile: bool, | ||
| 4565 | op_tok_id: std.zig.Token.Id, | ||
| 4566 | ) !*ast.Node.PtrType { | ||
| 4567 | const node = try c.arena.create(ast.Node.PtrType); | ||
| 4568 | const op_token = switch (op_tok_id) { | ||
| 4569 | .LBracket => blk: { | ||
| 4570 | const lbracket = try appendToken(c, .LBracket, "["); | ||
| 4571 | _ = try appendToken(c, .Asterisk, "*"); | ||
| 4572 | _ = try appendToken(c, .RBracket, "]"); | ||
| 4573 | break :blk lbracket; | ||
| 4574 | }, | ||
| 4575 | .Identifier => blk: { | ||
| 4576 | const lbracket = try appendToken(c, .LBracket, "["); // Rendering checks if this token + 2 == .Identifier, so needs to return this token | ||
| 4577 | _ = try appendToken(c, .Asterisk, "*"); | ||
| 4578 | _ = try appendIdentifier(c, "c"); | ||
| 4579 | _ = try appendToken(c, .RBracket, "]"); | ||
| 4580 | break :blk lbracket; | ||
| 4581 | }, | ||
| 4582 | .Asterisk => try appendToken(c, .Asterisk, "*"), | ||
| 4583 | else => unreachable, | ||
| 4584 | }; | ||
| 4585 | node.* = .{ | ||
| 4586 | .op_token = op_token, | ||
| 4587 | .ptr_info = .{ | ||
| 4588 | .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null, | ||
| 4589 | .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null, | ||
| 4590 | }, | ||
| 4591 | .rhs = undefined, // translate and set afterward | ||
| 4592 | }; | ||
| 4593 | return node; | ||
| 4594 | } | 3543 | } |
| 4595 | 3544 | ||
| 4596 | fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !*ast.Node { | 3545 | fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node { |
| 4597 | const num_limbs = math.cast(usize, int.getNumWords()) catch |err| switch (err) { | 3546 | const num_limbs = math.cast(usize, int.getNumWords()) catch |err| switch (err) { |
| 4598 | error.Overflow => return error.OutOfMemory, | 3547 | error.Overflow => return error.OutOfMemory, |
| 4599 | }; | 3548 | }; |
| ... | @@ -4629,418 +3578,96 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !*ast.Node { | ... | @@ -4629,418 +3578,96 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !*ast.Node { |
| 4629 | else => @compileError("unimplemented"), | 3578 | else => @compileError("unimplemented"), |
| 4630 | } | 3579 | } |
| 4631 | 3580 | ||
| 4632 | const big: math.big.int.Const = .{ .limbs = limbs, .positive = !is_negative }; | 3581 | const big: math.big.int.Const = .{ .limbs = limbs, .positive = true }; |
| 4633 | const str = big.toStringAlloc(c.arena, 10, false) catch |err| switch (err) { | 3582 | const str = big.toStringAlloc(c.arena, 10, false) catch |err| switch (err) { |
| 4634 | error.OutOfMemory => return error.OutOfMemory, | 3583 | error.OutOfMemory => return error.OutOfMemory, |
| 4635 | }; | 3584 | }; |
| 4636 | defer c.arena.free(str); | 3585 | const res = try Tag.integer_literal.create(c.arena, str); |
| 4637 | const token = try appendToken(c, .IntegerLiteral, str); | 3586 | if (is_negative) return Tag.negate.create(c.arena, res); |
| 4638 | const node = try c.arena.create(ast.Node.OneToken); | 3587 | return res; |
| 4639 | node.* = .{ | ||
| 4640 | .base = .{ .tag = .IntegerLiteral }, | ||
| 4641 | .token = token, | ||
| 4642 | }; | ||
| 4643 | return &node.base; | ||
| 4644 | } | ||
| 4645 | |||
| 4646 | fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node { | ||
| 4647 | const token = try appendToken(c, .Keyword_undefined, "undefined"); | ||
| 4648 | const node = try c.arena.create(ast.Node.OneToken); | ||
| 4649 | node.* = .{ | ||
| 4650 | .base = .{ .tag = .UndefinedLiteral }, | ||
| 4651 | .token = token, | ||
| 4652 | }; | ||
| 4653 | return &node.base; | ||
| 4654 | } | ||
| 4655 | |||
| 4656 | fn transCreateNodeNullLiteral(c: *Context) !*ast.Node { | ||
| 4657 | const token = try appendToken(c, .Keyword_null, "null"); | ||
| 4658 | const node = try c.arena.create(ast.Node.OneToken); | ||
| 4659 | node.* = .{ | ||
| 4660 | .base = .{ .tag = .NullLiteral }, | ||
| 4661 | .token = token, | ||
| 4662 | }; | ||
| 4663 | return &node.base; | ||
| 4664 | } | 3588 | } |
| 4665 | 3589 | ||
| 4666 | fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node { | 3590 | fn transCreateNodeNumber(c: *Context, num: anytype, num_kind: enum { int, float }) !Node { |
| 4667 | const token = if (value) | 3591 | const fmt_s = if (comptime std.meta.trait.isNumber(@TypeOf(num))) "{d}" else "{s}"; |
| 4668 | try appendToken(c, .Keyword_true, "true") | 3592 | const str = try std.fmt.allocPrint(c.arena, fmt_s, .{num}); |
| 3593 | if (num_kind == .float) | ||
| 3594 | return Tag.float_literal.create(c.arena, str) | ||
| 4669 | else | 3595 | else |
| 4670 | try appendToken(c, .Keyword_false, "false"); | 3596 | return Tag.integer_literal.create(c.arena, str); |
| 4671 | const node = try c.arena.create(ast.Node.OneToken); | ||
| 4672 | node.* = .{ | ||
| 4673 | .base = .{ .tag = .BoolLiteral }, | ||
| 4674 | .token = token, | ||
| 4675 | }; | ||
| 4676 | return &node.base; | ||
| 4677 | } | ||
| 4678 | |||
| 4679 | fn transCreateNodeInt(c: *Context, int: anytype) !*ast.Node { | ||
| 4680 | const fmt_s = if (comptime std.meta.trait.isIntegerNumber(@TypeOf(int))) "{d}" else "{s}"; | ||
| 4681 | const token = try appendTokenFmt(c, .IntegerLiteral, fmt_s, .{int}); | ||
| 4682 | const node = try c.arena.create(ast.Node.OneToken); | ||
| 4683 | node.* = .{ | ||
| 4684 | .base = .{ .tag = .IntegerLiteral }, | ||
| 4685 | .token = token, | ||
| 4686 | }; | ||
| 4687 | return &node.base; | ||
| 4688 | } | 3597 | } |
| 4689 | 3598 | ||
| 4690 | fn transCreateNodeFloat(c: *Context, str: []const u8) !*ast.Node { | 3599 | fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: Node, proto_alias: *ast.Payload.Func) !Node { |
| 4691 | const token = try appendTokenFmt(c, .FloatLiteral, "{s}", .{str}); | ||
| 4692 | const node = try c.arena.create(ast.Node.OneToken); | ||
| 4693 | node.* = .{ | ||
| 4694 | .base = .{ .tag = .FloatLiteral }, | ||
| 4695 | .token = token, | ||
| 4696 | }; | ||
| 4697 | return &node.base; | ||
| 4698 | } | ||
| 4699 | |||
| 4700 | fn transCreateNodeOpaqueType(c: *Context) !*ast.Node { | ||
| 4701 | const container_tok = try appendToken(c, .Keyword_opaque, "opaque"); | ||
| 4702 | const lbrace_token = try appendToken(c, .LBrace, "{"); | ||
| 4703 | const container_node = try ast.Node.ContainerDecl.alloc(c.arena, 0); | ||
| 4704 | container_node.* = .{ | ||
| 4705 | .kind_token = container_tok, | ||
| 4706 | .layout_token = null, | ||
| 4707 | .lbrace_token = lbrace_token, | ||
| 4708 | .rbrace_token = try appendToken(c, .RBrace, "}"), | ||
| 4709 | .fields_and_decls_len = 0, | ||
| 4710 | .init_arg_expr = .None, | ||
| 4711 | }; | ||
| 4712 | return &container_node.base; | ||
| 4713 | } | ||
| 4714 | |||
| 4715 | fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias: *ast.Node.FnProto) !*ast.Node { | ||
| 4716 | const scope = &c.global_scope.base; | 3600 | const scope = &c.global_scope.base; |
| 4717 | 3601 | ||
| 4718 | const pub_tok = try appendToken(c, .Keyword_pub, "pub"); | 3602 | var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa); |
| 4719 | const fn_tok = try appendToken(c, .Keyword_fn, "fn"); | ||
| 4720 | const name_tok = try appendIdentifier(c, name); | ||
| 4721 | _ = try appendToken(c, .LParen, "("); | ||
| 4722 | |||
| 4723 | var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(c.gpa); | ||
| 4724 | defer fn_params.deinit(); | 3603 | defer fn_params.deinit(); |
| 4725 | 3604 | ||
| 4726 | for (proto_alias.params()) |param, i| { | 3605 | for (proto_alias.data.params) |param, i| { |
| 4727 | if (i != 0) { | 3606 | const param_name = param.name orelse |
| 4728 | _ = try appendToken(c, .Comma, ","); | 3607 | try std.fmt.allocPrint(c.arena, "arg_{d}", .{c.getMangle()}); |
| 4729 | } | ||
| 4730 | const param_name_tok = param.name_token orelse | ||
| 4731 | try appendTokenFmt(c, .Identifier, "arg_{d}", .{c.getMangle()}); | ||
| 4732 | |||
| 4733 | _ = try appendToken(c, .Colon, ":"); | ||
| 4734 | 3608 | ||
| 4735 | (try fn_params.addOne()).* = .{ | 3609 | try fn_params.append(.{ |
| 4736 | .doc_comments = null, | 3610 | .name = param_name, |
| 4737 | .comptime_token = null, | 3611 | .type = param.type, |
| 4738 | .noalias_token = param.noalias_token, | 3612 | .is_noalias = param.is_noalias, |
| 4739 | .name_token = param_name_tok, | 3613 | }); |
| 4740 | .param_type = param.param_type, | ||
| 4741 | }; | ||
| 4742 | } | 3614 | } |
| 4743 | 3615 | ||
| 4744 | _ = try appendToken(c, .RParen, ")"); | 3616 | const init = if (ref.castTag(.var_decl)) |v| |
| 4745 | 3617 | v.data.init.? | |
| 4746 | _ = try appendToken(c, .Keyword_callconv, "callconv"); | 3618 | else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v| |
| 4747 | _ = try appendToken(c, .LParen, "("); | 3619 | v.data.init |
| 4748 | const callconv_expr = try transCreateNodeEnumLiteral(c, "Inline"); | 3620 | else |
| 4749 | _ = try appendToken(c, .RParen, ")"); | 3621 | unreachable; |
| 4750 | |||
| 4751 | const block_lbrace = try appendToken(c, .LBrace, "{"); | ||
| 4752 | |||
| 4753 | const return_kw = try appendToken(c, .Keyword_return, "return"); | ||
| 4754 | const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.getInitNode().?); | ||
| 4755 | |||
| 4756 | const call_expr = try c.createCall(unwrap_expr, fn_params.items.len); | ||
| 4757 | const call_params = call_expr.params(); | ||
| 4758 | 3622 | ||
| 3623 | const unwrap_expr = try Tag.unwrap.create(c.arena, init); | ||
| 3624 | const args = try c.arena.alloc(Node, fn_params.items.len); | ||
| 4759 | for (fn_params.items) |param, i| { | 3625 | for (fn_params.items) |param, i| { |
| 4760 | if (i != 0) { | 3626 | args[i] = try Tag.identifier.create(c.arena, param.name.?); |
| 4761 | _ = try appendToken(c, .Comma, ","); | ||
| 4762 | } | ||
| 4763 | call_params[i] = try transCreateNodeIdentifier(c, tokenSlice(c, param.name_token.?)); | ||
| 4764 | } | 3627 | } |
| 4765 | call_expr.rtoken = try appendToken(c, .RParen, ")"); | 3628 | const call_expr = try Tag.call.create(c.arena, .{ |
| 4766 | 3629 | .lhs = unwrap_expr, | |
| 4767 | const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{ | 3630 | .args = args, |
| 4768 | .ltoken = return_kw, | ||
| 4769 | .tag = .Return, | ||
| 4770 | }, .{ | ||
| 4771 | .rhs = &call_expr.base, | ||
| 4772 | }); | 3631 | }); |
| 4773 | _ = try appendToken(c, .Semicolon, ";"); | 3632 | const return_expr = try Tag.@"return".create(c.arena, call_expr); |
| 4774 | 3633 | const block = try Tag.block_single.create(c.arena, return_expr); | |
| 4775 | const block = try ast.Node.Block.alloc(c.arena, 1); | 3634 | |
| 4776 | block.* = .{ | 3635 | return Tag.pub_inline_fn.create(c.arena, .{ |
| 4777 | .lbrace = block_lbrace, | 3636 | .name = name, |
| 4778 | .statements_len = 1, | 3637 | .params = try c.arena.dupe(ast.Payload.Param, fn_params.items), |
| 4779 | .rbrace = try appendToken(c, .RBrace, "}"), | 3638 | .return_type = proto_alias.data.return_type, |
| 4780 | }; | 3639 | .body = block, |
| 4781 | block.statements()[0] = &return_expr.base; | ||
| 4782 | |||
| 4783 | const fn_proto = try ast.Node.FnProto.create(c.arena, .{ | ||
| 4784 | .params_len = fn_params.items.len, | ||
| 4785 | .fn_token = fn_tok, | ||
| 4786 | .return_type = proto_alias.return_type, | ||
| 4787 | }, .{ | ||
| 4788 | .visib_token = pub_tok, | ||
| 4789 | .name_token = name_tok, | ||
| 4790 | .body_node = &block.base, | ||
| 4791 | .callconv_expr = callconv_expr, | ||
| 4792 | }); | 3640 | }); |
| 4793 | mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items); | ||
| 4794 | return &fn_proto.base; | ||
| 4795 | } | ||
| 4796 | |||
| 4797 | fn transCreateNodeUnwrapNull(c: *Context, wrapped: *ast.Node) !*ast.Node { | ||
| 4798 | _ = try appendToken(c, .Period, "."); | ||
| 4799 | const qm = try appendToken(c, .QuestionMark, "?"); | ||
| 4800 | const node = try c.arena.create(ast.Node.SimpleSuffixOp); | ||
| 4801 | node.* = .{ | ||
| 4802 | .base = .{ .tag = .UnwrapOptional }, | ||
| 4803 | .lhs = wrapped, | ||
| 4804 | .rtoken = qm, | ||
| 4805 | }; | ||
| 4806 | return &node.base; | ||
| 4807 | } | ||
| 4808 | |||
| 4809 | fn transCreateNodeEnumLiteral(c: *Context, name: []const u8) !*ast.Node { | ||
| 4810 | const node = try c.arena.create(ast.Node.EnumLiteral); | ||
| 4811 | node.* = .{ | ||
| 4812 | .dot = try appendToken(c, .Period, "."), | ||
| 4813 | .name = try appendIdentifier(c, name), | ||
| 4814 | }; | ||
| 4815 | return &node.base; | ||
| 4816 | } | ||
| 4817 | |||
| 4818 | fn transCreateNodeStringLiteral(c: *Context, str: []const u8) !*ast.Node { | ||
| 4819 | const node = try c.arena.create(ast.Node.OneToken); | ||
| 4820 | node.* = .{ | ||
| 4821 | .base = .{ .tag = .StringLiteral }, | ||
| 4822 | .token = try appendToken(c, .StringLiteral, str), | ||
| 4823 | }; | ||
| 4824 | return &node.base; | ||
| 4825 | } | ||
| 4826 | |||
| 4827 | fn transCreateNodeIf(c: *Context) !*ast.Node.If { | ||
| 4828 | const if_tok = try appendToken(c, .Keyword_if, "if"); | ||
| 4829 | _ = try appendToken(c, .LParen, "("); | ||
| 4830 | const node = try c.arena.create(ast.Node.If); | ||
| 4831 | node.* = .{ | ||
| 4832 | .if_token = if_tok, | ||
| 4833 | .condition = undefined, | ||
| 4834 | .payload = null, | ||
| 4835 | .body = undefined, | ||
| 4836 | .@"else" = null, | ||
| 4837 | }; | ||
| 4838 | return node; | ||
| 4839 | } | ||
| 4840 | |||
| 4841 | fn transCreateNodeElse(c: *Context) !*ast.Node.Else { | ||
| 4842 | const node = try c.arena.create(ast.Node.Else); | ||
| 4843 | node.* = .{ | ||
| 4844 | .else_token = try appendToken(c, .Keyword_else, "else"), | ||
| 4845 | .payload = null, | ||
| 4846 | .body = undefined, | ||
| 4847 | }; | ||
| 4848 | return node; | ||
| 4849 | } | ||
| 4850 | |||
| 4851 | fn transCreateNodeBreak( | ||
| 4852 | c: *Context, | ||
| 4853 | label: ?ast.TokenIndex, | ||
| 4854 | rhs: ?*ast.Node, | ||
| 4855 | ) !*ast.Node.ControlFlowExpression { | ||
| 4856 | var ctrl_flow = try CtrlFlow.init(c, .Break, if (label) |l| tokenSlice(c, l) else null); | ||
| 4857 | return ctrl_flow.finish(rhs); | ||
| 4858 | } | ||
| 4859 | |||
| 4860 | const CtrlFlow = struct { | ||
| 4861 | c: *Context, | ||
| 4862 | ltoken: ast.TokenIndex, | ||
| 4863 | label_token: ?ast.TokenIndex, | ||
| 4864 | tag: ast.Node.Tag, | ||
| 4865 | |||
| 4866 | /// Does everything except the RHS. | ||
| 4867 | fn init(c: *Context, tag: ast.Node.Tag, label: ?[]const u8) !CtrlFlow { | ||
| 4868 | const kw: Token.Id = switch (tag) { | ||
| 4869 | .Break => .Keyword_break, | ||
| 4870 | .Continue => .Keyword_continue, | ||
| 4871 | .Return => .Keyword_return, | ||
| 4872 | else => unreachable, | ||
| 4873 | }; | ||
| 4874 | const kw_text = switch (tag) { | ||
| 4875 | .Break => "break", | ||
| 4876 | .Continue => "continue", | ||
| 4877 | .Return => "return", | ||
| 4878 | else => unreachable, | ||
| 4879 | }; | ||
| 4880 | const ltoken = try appendToken(c, kw, kw_text); | ||
| 4881 | const label_token = if (label) |l| blk: { | ||
| 4882 | _ = try appendToken(c, .Colon, ":"); | ||
| 4883 | break :blk try appendIdentifier(c, l); | ||
| 4884 | } else null; | ||
| 4885 | return CtrlFlow{ | ||
| 4886 | .c = c, | ||
| 4887 | .ltoken = ltoken, | ||
| 4888 | .label_token = label_token, | ||
| 4889 | .tag = tag, | ||
| 4890 | }; | ||
| 4891 | } | ||
| 4892 | |||
| 4893 | fn initToken(c: *Context, tag: ast.Node.Tag, label: ?ast.TokenIndex) !CtrlFlow { | ||
| 4894 | const other_token = label orelse return init(c, tag, null); | ||
| 4895 | const loc = c.token_locs.items[other_token]; | ||
| 4896 | const label_name = c.source_buffer.items[loc.start..loc.end]; | ||
| 4897 | return init(c, tag, label_name); | ||
| 4898 | } | ||
| 4899 | |||
| 4900 | fn finish(self: *CtrlFlow, rhs: ?*ast.Node) !*ast.Node.ControlFlowExpression { | ||
| 4901 | return ast.Node.ControlFlowExpression.create(self.c.arena, .{ | ||
| 4902 | .ltoken = self.ltoken, | ||
| 4903 | .tag = self.tag, | ||
| 4904 | }, .{ | ||
| 4905 | .label = self.label_token, | ||
| 4906 | .rhs = rhs, | ||
| 4907 | }); | ||
| 4908 | } | ||
| 4909 | }; | ||
| 4910 | |||
| 4911 | fn transCreateNodeWhile(c: *Context) !*ast.Node.While { | ||
| 4912 | const while_tok = try appendToken(c, .Keyword_while, "while"); | ||
| 4913 | _ = try appendToken(c, .LParen, "("); | ||
| 4914 | |||
| 4915 | const node = try c.arena.create(ast.Node.While); | ||
| 4916 | node.* = .{ | ||
| 4917 | .label = null, | ||
| 4918 | .inline_token = null, | ||
| 4919 | .while_token = while_tok, | ||
| 4920 | .condition = undefined, | ||
| 4921 | .payload = null, | ||
| 4922 | .continue_expr = null, | ||
| 4923 | .body = undefined, | ||
| 4924 | .@"else" = null, | ||
| 4925 | }; | ||
| 4926 | return node; | ||
| 4927 | } | ||
| 4928 | |||
| 4929 | fn transCreateNodeContinue(c: *Context) !*ast.Node { | ||
| 4930 | const ltoken = try appendToken(c, .Keyword_continue, "continue"); | ||
| 4931 | const node = try ast.Node.ControlFlowExpression.create(c.arena, .{ | ||
| 4932 | .ltoken = ltoken, | ||
| 4933 | .tag = .Continue, | ||
| 4934 | }, .{}); | ||
| 4935 | _ = try appendToken(c, .Semicolon, ";"); | ||
| 4936 | return &node.base; | ||
| 4937 | } | ||
| 4938 | |||
| 4939 | fn transCreateNodeSwitchCase(c: *Context, lhs: *ast.Node) !*ast.Node.SwitchCase { | ||
| 4940 | const arrow_tok = try appendToken(c, .EqualAngleBracketRight, "=>"); | ||
| 4941 | |||
| 4942 | const node = try ast.Node.SwitchCase.alloc(c.arena, 1); | ||
| 4943 | node.* = .{ | ||
| 4944 | .items_len = 1, | ||
| 4945 | .arrow_token = arrow_tok, | ||
| 4946 | .payload = null, | ||
| 4947 | .expr = undefined, | ||
| 4948 | }; | ||
| 4949 | node.items()[0] = lhs; | ||
| 4950 | return node; | ||
| 4951 | } | ||
| 4952 | |||
| 4953 | fn transCreateNodeSwitchElse(c: *Context) !*ast.Node { | ||
| 4954 | const node = try c.arena.create(ast.Node.SwitchElse); | ||
| 4955 | node.* = .{ | ||
| 4956 | .token = try appendToken(c, .Keyword_else, "else"), | ||
| 4957 | }; | ||
| 4958 | return &node.base; | ||
| 4959 | } | 3641 | } |
| 4960 | 3642 | ||
| 4961 | fn transCreateNodeShiftOp( | 3643 | fn transCreateNodeShiftOp( |
| 4962 | rp: RestorePoint, | 3644 | c: *Context, |
| 4963 | scope: *Scope, | 3645 | scope: *Scope, |
| 4964 | stmt: *const clang.BinaryOperator, | 3646 | stmt: *const clang.BinaryOperator, |
| 4965 | op: ast.Node.Tag, | 3647 | op: Tag, |
| 4966 | op_tok_id: std.zig.Token.Id, | 3648 | used: ResultUsed, |
| 4967 | bytes: []const u8, | 3649 | ) !Node { |
| 4968 | ) !*ast.Node { | 3650 | std.debug.assert(op == .shl or op == .shr); |
| 4969 | std.debug.assert(op == .BitShiftLeft or op == .BitShiftRight); | ||
| 4970 | 3651 | ||
| 4971 | const lhs_expr = stmt.getLHS(); | 3652 | const lhs_expr = stmt.getLHS(); |
| 4972 | const rhs_expr = stmt.getRHS(); | 3653 | const rhs_expr = stmt.getRHS(); |
| 4973 | const rhs_location = rhs_expr.getBeginLoc(); | 3654 | const rhs_location = rhs_expr.getBeginLoc(); |
| 4974 | // lhs >> @as(u5, rh) | 3655 | // lhs >> @as(u5, rh) |
| 4975 | 3656 | ||
| 4976 | const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value); | 3657 | const lhs = try transExpr(c, scope, lhs_expr, .used); |
| 4977 | const op_token = try appendToken(rp.c, op_tok_id, bytes); | ||
| 4978 | |||
| 4979 | const cast_node = try rp.c.createBuiltinCall("@intCast", 2); | ||
| 4980 | const rhs_type = try qualTypeToLog2IntRef(rp, stmt.getType(), rhs_location); | ||
| 4981 | cast_node.params()[0] = rhs_type; | ||
| 4982 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 4983 | const rhs = try transExprCoercing(rp, scope, rhs_expr, .used, .r_value); | ||
| 4984 | cast_node.params()[1] = rhs; | ||
| 4985 | cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); | ||
| 4986 | |||
| 4987 | const node = try rp.c.arena.create(ast.Node.SimpleInfixOp); | ||
| 4988 | node.* = .{ | ||
| 4989 | .base = .{ .tag = op }, | ||
| 4990 | .op_token = op_token, | ||
| 4991 | .lhs = lhs, | ||
| 4992 | .rhs = &cast_node.base, | ||
| 4993 | }; | ||
| 4994 | 3658 | ||
| 4995 | return &node.base; | 3659 | const rhs_type = try qualTypeToLog2IntRef(c, scope, stmt.getType(), rhs_location); |
| 4996 | } | 3660 | const rhs = try transExprCoercing(c, scope, rhs_expr, .used); |
| 3661 | const rhs_casted = try Tag.int_cast.create(c.arena, .{ .lhs = rhs_type, .rhs = rhs }); | ||
| 4997 | 3662 | ||
| 4998 | fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node { | 3663 | return transCreateNodeInfixOp(c, scope, op, lhs, rhs_casted, used); |
| 4999 | const node = try c.arena.create(ast.Node.SimpleSuffixOp); | ||
| 5000 | node.* = .{ | ||
| 5001 | .base = .{ .tag = .Deref }, | ||
| 5002 | .lhs = lhs, | ||
| 5003 | .rtoken = try appendToken(c, .PeriodAsterisk, ".*"), | ||
| 5004 | }; | ||
| 5005 | return &node.base; | ||
| 5006 | } | 3664 | } |
| 5007 | 3665 | ||
| 5008 | fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.ArrayAccess { | 3666 | fn transType(c: *Context, scope: *Scope, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Node { |
| 5009 | _ = try appendToken(c, .LBrace, "["); | ||
| 5010 | const node = try c.arena.create(ast.Node.ArrayAccess); | ||
| 5011 | node.* = .{ | ||
| 5012 | .lhs = lhs, | ||
| 5013 | .index_expr = undefined, | ||
| 5014 | .rtoken = undefined, | ||
| 5015 | }; | ||
| 5016 | return node; | ||
| 5017 | } | ||
| 5018 | |||
| 5019 | const RestorePoint = struct { | ||
| 5020 | c: *Context, | ||
| 5021 | token_index: ast.TokenIndex, | ||
| 5022 | src_buf_index: usize, | ||
| 5023 | |||
| 5024 | fn activate(self: RestorePoint) void { | ||
| 5025 | self.c.token_ids.shrinkAndFree(self.c.gpa, self.token_index); | ||
| 5026 | self.c.token_locs.shrinkAndFree(self.c.gpa, self.token_index); | ||
| 5027 | self.c.source_buffer.shrinkAndFree(self.src_buf_index); | ||
| 5028 | } | ||
| 5029 | }; | ||
| 5030 | |||
| 5031 | fn makeRestorePoint(c: *Context) RestorePoint { | ||
| 5032 | return RestorePoint{ | ||
| 5033 | .c = c, | ||
| 5034 | .token_index = c.token_ids.items.len, | ||
| 5035 | .src_buf_index = c.source_buffer.items.len, | ||
| 5036 | }; | ||
| 5037 | } | ||
| 5038 | |||
| 5039 | fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!*ast.Node { | ||
| 5040 | switch (ty.getTypeClass()) { | 3667 | switch (ty.getTypeClass()) { |
| 5041 | .Builtin => { | 3668 | .Builtin => { |
| 5042 | const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); | 3669 | const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); |
| 5043 | return transCreateNodeIdentifier(rp.c, switch (builtin_ty.getKind()) { | 3670 | return Tag.type.create(c.arena, switch (builtin_ty.getKind()) { |
| 5044 | .Void => "c_void", | 3671 | .Void => "c_void", |
| 5045 | .Bool => "bool", | 3672 | .Bool => "bool", |
| 5046 | .Char_U, .UChar, .Char_S, .Char8 => "u8", | 3673 | .Char_U, .UChar, .Char_S, .Char8 => "u8", |
| ... | @@ -5060,112 +3687,115 @@ fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLo | ... | @@ -5060,112 +3687,115 @@ fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLo |
| 5060 | .Float128 => "f128", | 3687 | .Float128 => "f128", |
| 5061 | .Float16 => "f16", | 3688 | .Float16 => "f16", |
| 5062 | .LongDouble => "c_longdouble", | 3689 | .LongDouble => "c_longdouble", |
| 5063 | else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), | 3690 | else => return fail(c, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), |
| 5064 | }); | 3691 | }); |
| 5065 | }, | 3692 | }, |
| 5066 | .FunctionProto => { | 3693 | .FunctionProto => { |
| 5067 | const fn_proto_ty = @ptrCast(*const clang.FunctionProtoType, ty); | 3694 | const fn_proto_ty = @ptrCast(*const clang.FunctionProtoType, ty); |
| 5068 | const fn_proto = try transFnProto(rp, null, fn_proto_ty, source_loc, null, false); | 3695 | const fn_proto = try transFnProto(c, null, fn_proto_ty, source_loc, null, false); |
| 5069 | return &fn_proto.base; | 3696 | return Node.initPayload(&fn_proto.base); |
| 5070 | }, | 3697 | }, |
| 5071 | .FunctionNoProto => { | 3698 | .FunctionNoProto => { |
| 5072 | const fn_no_proto_ty = @ptrCast(*const clang.FunctionType, ty); | 3699 | const fn_no_proto_ty = @ptrCast(*const clang.FunctionType, ty); |
| 5073 | const fn_proto = try transFnNoProto(rp, fn_no_proto_ty, source_loc, null, false); | 3700 | const fn_proto = try transFnNoProto(c, fn_no_proto_ty, source_loc, null, false); |
| 5074 | return &fn_proto.base; | 3701 | return Node.initPayload(&fn_proto.base); |
| 5075 | }, | 3702 | }, |
| 5076 | .Paren => { | 3703 | .Paren => { |
| 5077 | const paren_ty = @ptrCast(*const clang.ParenType, ty); | 3704 | const paren_ty = @ptrCast(*const clang.ParenType, ty); |
| 5078 | return transQualType(rp, paren_ty.getInnerType(), source_loc); | 3705 | return transQualType(c, scope, paren_ty.getInnerType(), source_loc); |
| 5079 | }, | 3706 | }, |
| 5080 | .Pointer => { | 3707 | .Pointer => { |
| 5081 | const child_qt = ty.getPointeeType(); | 3708 | const child_qt = ty.getPointeeType(); |
| 5082 | if (qualTypeChildIsFnProto(child_qt)) { | 3709 | if (qualTypeChildIsFnProto(child_qt)) { |
| 5083 | const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?"); | 3710 | return Tag.optional_type.create(c.arena, try transQualType(c, scope, child_qt, source_loc)); |
| 5084 | optional_node.rhs = try transQualType(rp, child_qt, source_loc); | ||
| 5085 | return &optional_node.base; | ||
| 5086 | } | 3711 | } |
| 5087 | if (typeIsOpaque(rp.c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(rp.c, child_qt)) { | 3712 | const is_const = child_qt.isConstQualified(); |
| 5088 | const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?"); | 3713 | const is_volatile = child_qt.isVolatileQualified(); |
| 5089 | const pointer_node = try transCreateNodePtrType( | 3714 | const elem_type = try transQualType(c, scope, child_qt, source_loc); |
| 5090 | rp.c, | 3715 | if (typeIsOpaque(c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(c, child_qt)) { |
| 5091 | child_qt.isConstQualified(), | 3716 | const ptr = try Tag.single_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); |
| 5092 | child_qt.isVolatileQualified(), | 3717 | return Tag.optional_type.create(c.arena, ptr); |
| 5093 | .Asterisk, | ||
| 5094 | ); | ||
| 5095 | optional_node.rhs = &pointer_node.base; | ||
| 5096 | pointer_node.rhs = try transQualType(rp, child_qt, source_loc); | ||
| 5097 | return &optional_node.base; | ||
| 5098 | } | 3718 | } |
| 5099 | const pointer_node = try transCreateNodePtrType( | 3719 | |
| 5100 | rp.c, | 3720 | return Tag.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); |
| 5101 | child_qt.isConstQualified(), | ||
| 5102 | child_qt.isVolatileQualified(), | ||
| 5103 | .Identifier, | ||
| 5104 | ); | ||
| 5105 | pointer_node.rhs = try transQualType(rp, child_qt, source_loc); | ||
| 5106 | return &pointer_node.base; | ||
| 5107 | }, | 3721 | }, |
| 5108 | .ConstantArray => { | 3722 | .ConstantArray => { |
| 5109 | const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); | 3723 | const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); |
| 5110 | 3724 | ||
| 5111 | const size_ap_int = const_arr_ty.getSize(); | 3725 | const size_ap_int = const_arr_ty.getSize(); |
| 5112 | const size = size_ap_int.getLimitedValue(math.maxInt(usize)); | 3726 | const size = size_ap_int.getLimitedValue(math.maxInt(usize)); |
| 5113 | const elem_ty = const_arr_ty.getElementType().getTypePtr(); | 3727 | const elem_type = try transType(c, scope, const_arr_ty.getElementType().getTypePtr(), source_loc); |
| 5114 | return try transCreateNodeArrayType(rp, source_loc, elem_ty, size); | 3728 | |
| 3729 | return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_type }); | ||
| 5115 | }, | 3730 | }, |
| 5116 | .IncompleteArray => { | 3731 | .IncompleteArray => { |
| 5117 | const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty); | 3732 | const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty); |
| 5118 | 3733 | ||
| 5119 | const child_qt = incomplete_array_ty.getElementType(); | 3734 | const child_qt = incomplete_array_ty.getElementType(); |
| 5120 | var node = try transCreateNodePtrType( | 3735 | const is_const = child_qt.isConstQualified(); |
| 5121 | rp.c, | 3736 | const is_volatile = child_qt.isVolatileQualified(); |
| 5122 | child_qt.isConstQualified(), | 3737 | const elem_type = try transQualType(c, scope, child_qt, source_loc); |
| 5123 | child_qt.isVolatileQualified(), | 3738 | |
| 5124 | .Identifier, | 3739 | return Tag.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); |
| 5125 | ); | ||
| 5126 | node.rhs = try transQualType(rp, child_qt, source_loc); | ||
| 5127 | return &node.base; | ||
| 5128 | }, | 3740 | }, |
| 5129 | .Typedef => { | 3741 | .Typedef => { |
| 5130 | const typedef_ty = @ptrCast(*const clang.TypedefType, ty); | 3742 | const typedef_ty = @ptrCast(*const clang.TypedefType, ty); |
| 5131 | 3743 | ||
| 5132 | const typedef_decl = typedef_ty.getDecl(); | 3744 | const typedef_decl = typedef_ty.getDecl(); |
| 5133 | return (try transTypeDef(rp.c, typedef_decl, false)) orelse | 3745 | var trans_scope = scope; |
| 5134 | revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate typedef declaration", .{}); | 3746 | if (@ptrCast(*const clang.Decl, typedef_decl).castToNamedDecl()) |named_decl| { |
| 3747 | const decl_name = try c.str(named_decl.getName_bytes_begin()); | ||
| 3748 | if (c.global_names.get(decl_name)) |_| trans_scope = &c.global_scope.base; | ||
| 3749 | } | ||
| 3750 | try transTypeDef(c, trans_scope, typedef_decl); | ||
| 3751 | const name = c.decl_table.get(@ptrToInt(typedef_decl.getCanonicalDecl())).?; | ||
| 3752 | return Tag.identifier.create(c.arena, name); | ||
| 5135 | }, | 3753 | }, |
| 5136 | .Record => { | 3754 | .Record => { |
| 5137 | const record_ty = @ptrCast(*const clang.RecordType, ty); | 3755 | const record_ty = @ptrCast(*const clang.RecordType, ty); |
| 5138 | 3756 | ||
| 5139 | const record_decl = record_ty.getDecl(); | 3757 | const record_decl = record_ty.getDecl(); |
| 5140 | return (try transRecordDecl(rp.c, record_decl)) orelse | 3758 | var trans_scope = scope; |
| 5141 | revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to resolve record declaration", .{}); | 3759 | if (@ptrCast(*const clang.Decl, record_decl).castToNamedDecl()) |named_decl| { |
| 3760 | const decl_name = try c.str(named_decl.getName_bytes_begin()); | ||
| 3761 | if (c.global_names.get(decl_name)) |_| trans_scope = &c.global_scope.base; | ||
| 3762 | } | ||
| 3763 | try transRecordDecl(c, trans_scope, record_decl); | ||
| 3764 | const name = c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl())).?; | ||
| 3765 | return Tag.identifier.create(c.arena, name); | ||
| 5142 | }, | 3766 | }, |
| 5143 | .Enum => { | 3767 | .Enum => { |
| 5144 | const enum_ty = @ptrCast(*const clang.EnumType, ty); | 3768 | const enum_ty = @ptrCast(*const clang.EnumType, ty); |
| 5145 | 3769 | ||
| 5146 | const enum_decl = enum_ty.getDecl(); | 3770 | const enum_decl = enum_ty.getDecl(); |
| 5147 | return (try transEnumDecl(rp.c, enum_decl)) orelse | 3771 | var trans_scope = scope; |
| 5148 | revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate enum declaration", .{}); | 3772 | if (@ptrCast(*const clang.Decl, enum_decl).castToNamedDecl()) |named_decl| { |
| 3773 | const decl_name = try c.str(named_decl.getName_bytes_begin()); | ||
| 3774 | if (c.global_names.get(decl_name)) |_| trans_scope = &c.global_scope.base; | ||
| 3775 | } | ||
| 3776 | try transEnumDecl(c, trans_scope, enum_decl); | ||
| 3777 | const name = c.decl_table.get(@ptrToInt(enum_decl.getCanonicalDecl())).?; | ||
| 3778 | return Tag.identifier.create(c.arena, name); | ||
| 5149 | }, | 3779 | }, |
| 5150 | .Elaborated => { | 3780 | .Elaborated => { |
| 5151 | const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty); | 3781 | const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty); |
| 5152 | return transQualType(rp, elaborated_ty.getNamedType(), source_loc); | 3782 | return transQualType(c, scope, elaborated_ty.getNamedType(), source_loc); |
| 5153 | }, | 3783 | }, |
| 5154 | .Decayed => { | 3784 | .Decayed => { |
| 5155 | const decayed_ty = @ptrCast(*const clang.DecayedType, ty); | 3785 | const decayed_ty = @ptrCast(*const clang.DecayedType, ty); |
| 5156 | return transQualType(rp, decayed_ty.getDecayedType(), source_loc); | 3786 | return transQualType(c, scope, decayed_ty.getDecayedType(), source_loc); |
| 5157 | }, | 3787 | }, |
| 5158 | .Attributed => { | 3788 | .Attributed => { |
| 5159 | const attributed_ty = @ptrCast(*const clang.AttributedType, ty); | 3789 | const attributed_ty = @ptrCast(*const clang.AttributedType, ty); |
| 5160 | return transQualType(rp, attributed_ty.getEquivalentType(), source_loc); | 3790 | return transQualType(c, scope, attributed_ty.getEquivalentType(), source_loc); |
| 5161 | }, | 3791 | }, |
| 5162 | .MacroQualified => { | 3792 | .MacroQualified => { |
| 5163 | const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, ty); | 3793 | const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, ty); |
| 5164 | return transQualType(rp, macroqualified_ty.getModifiedType(), source_loc); | 3794 | return transQualType(c, scope, macroqualified_ty.getModifiedType(), source_loc); |
| 5165 | }, | 3795 | }, |
| 5166 | else => { | 3796 | else => { |
| 5167 | const type_name = rp.c.str(ty.getTypeClassName()); | 3797 | const type_name = c.str(ty.getTypeClassName()); |
| 5168 | return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name}); | 3798 | return fail(c, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name}); |
| 5169 | }, | 3799 | }, |
| 5170 | } | 3800 | } |
| 5171 | } | 3801 | } |
| ... | @@ -5231,7 +3861,7 @@ const FnDeclContext = struct { | ... | @@ -5231,7 +3861,7 @@ const FnDeclContext = struct { |
| 5231 | }; | 3861 | }; |
| 5232 | 3862 | ||
| 5233 | fn transCC( | 3863 | fn transCC( |
| 5234 | rp: RestorePoint, | 3864 | c: *Context, |
| 5235 | fn_ty: *const clang.FunctionType, | 3865 | fn_ty: *const clang.FunctionType, |
| 5236 | source_loc: clang.SourceLocation, | 3866 | source_loc: clang.SourceLocation, |
| 5237 | ) !CallingConvention { | 3867 | ) !CallingConvention { |
| ... | @@ -5244,8 +3874,8 @@ fn transCC( | ... | @@ -5244,8 +3874,8 @@ fn transCC( |
| 5244 | .X86ThisCall => return CallingConvention.Thiscall, | 3874 | .X86ThisCall => return CallingConvention.Thiscall, |
| 5245 | .AAPCS => return CallingConvention.AAPCS, | 3875 | .AAPCS => return CallingConvention.AAPCS, |
| 5246 | .AAPCS_VFP => return CallingConvention.AAPCSVFP, | 3876 | .AAPCS_VFP => return CallingConvention.AAPCSVFP, |
| 5247 | else => return revertAndWarn( | 3877 | else => return fail( |
| 5248 | rp, | 3878 | c, |
| 5249 | error.UnsupportedType, | 3879 | error.UnsupportedType, |
| 5250 | source_loc, | 3880 | source_loc, |
| 5251 | "unsupported calling convention: {s}", | 3881 | "unsupported calling convention: {s}", |
| ... | @@ -5255,33 +3885,33 @@ fn transCC( | ... | @@ -5255,33 +3885,33 @@ fn transCC( |
| 5255 | } | 3885 | } |
| 5256 | 3886 | ||
| 5257 | fn transFnProto( | 3887 | fn transFnProto( |
| 5258 | rp: RestorePoint, | 3888 | c: *Context, |
| 5259 | fn_decl: ?*const clang.FunctionDecl, | 3889 | fn_decl: ?*const clang.FunctionDecl, |
| 5260 | fn_proto_ty: *const clang.FunctionProtoType, | 3890 | fn_proto_ty: *const clang.FunctionProtoType, |
| 5261 | source_loc: clang.SourceLocation, | 3891 | source_loc: clang.SourceLocation, |
| 5262 | fn_decl_context: ?FnDeclContext, | 3892 | fn_decl_context: ?FnDeclContext, |
| 5263 | is_pub: bool, | 3893 | is_pub: bool, |
| 5264 | ) !*ast.Node.FnProto { | 3894 | ) !*ast.Payload.Func { |
| 5265 | const fn_ty = @ptrCast(*const clang.FunctionType, fn_proto_ty); | 3895 | const fn_ty = @ptrCast(*const clang.FunctionType, fn_proto_ty); |
| 5266 | const cc = try transCC(rp, fn_ty, source_loc); | 3896 | const cc = try transCC(c, fn_ty, source_loc); |
| 5267 | const is_var_args = fn_proto_ty.isVariadic(); | 3897 | const is_var_args = fn_proto_ty.isVariadic(); |
| 5268 | return finishTransFnProto(rp, fn_decl, fn_proto_ty, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); | 3898 | return finishTransFnProto(c, fn_decl, fn_proto_ty, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); |
| 5269 | } | 3899 | } |
| 5270 | 3900 | ||
| 5271 | fn transFnNoProto( | 3901 | fn transFnNoProto( |
| 5272 | rp: RestorePoint, | 3902 | c: *Context, |
| 5273 | fn_ty: *const clang.FunctionType, | 3903 | fn_ty: *const clang.FunctionType, |
| 5274 | source_loc: clang.SourceLocation, | 3904 | source_loc: clang.SourceLocation, |
| 5275 | fn_decl_context: ?FnDeclContext, | 3905 | fn_decl_context: ?FnDeclContext, |
| 5276 | is_pub: bool, | 3906 | is_pub: bool, |
| 5277 | ) !*ast.Node.FnProto { | 3907 | ) !*ast.Payload.Func { |
| 5278 | const cc = try transCC(rp, fn_ty, source_loc); | 3908 | const cc = try transCC(c, fn_ty, source_loc); |
| 5279 | const is_var_args = if (fn_decl_context) |ctx| (!ctx.is_export and ctx.storage_class != .Static) else true; | 3909 | const is_var_args = if (fn_decl_context) |ctx| (!ctx.is_export and ctx.storage_class != .Static) else true; |
| 5280 | return finishTransFnProto(rp, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); | 3910 | return finishTransFnProto(c, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); |
| 5281 | } | 3911 | } |
| 5282 | 3912 | ||
| 5283 | fn finishTransFnProto( | 3913 | fn finishTransFnProto( |
| 5284 | rp: RestorePoint, | 3914 | c: *Context, |
| 5285 | fn_decl: ?*const clang.FunctionDecl, | 3915 | fn_decl: ?*const clang.FunctionDecl, |
| 5286 | fn_proto_ty: ?*const clang.FunctionProtoType, | 3916 | fn_proto_ty: ?*const clang.FunctionProtoType, |
| 5287 | fn_ty: *const clang.FunctionType, | 3917 | fn_ty: *const clang.FunctionType, |
| ... | @@ -5290,128 +3920,78 @@ fn finishTransFnProto( | ... | @@ -5290,128 +3920,78 @@ fn finishTransFnProto( |
| 5290 | is_var_args: bool, | 3920 | is_var_args: bool, |
| 5291 | cc: CallingConvention, | 3921 | cc: CallingConvention, |
| 5292 | is_pub: bool, | 3922 | is_pub: bool, |
| 5293 | ) !*ast.Node.FnProto { | 3923 | ) !*ast.Payload.Func { |
| 5294 | const is_export = if (fn_decl_context) |ctx| ctx.is_export else false; | 3924 | const is_export = if (fn_decl_context) |ctx| ctx.is_export else false; |
| 5295 | const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false; | 3925 | const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false; |
| 3926 | const scope = &c.global_scope.base; | ||
| 5296 | 3927 | ||
| 5297 | // TODO check for always_inline attribute | 3928 | // TODO check for always_inline attribute |
| 5298 | // TODO check for align attribute | 3929 | // TODO check for align attribute |
| 5299 | 3930 | ||
| 5300 | // pub extern fn name(...) T | 3931 | var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa); |
| 5301 | const pub_tok = if (is_pub) try appendToken(rp.c, .Keyword_pub, "pub") else null; | ||
| 5302 | const extern_export_inline_tok = if (is_export) | ||
| 5303 | try appendToken(rp.c, .Keyword_export, "export") | ||
| 5304 | else if (is_extern) | ||
| 5305 | try appendToken(rp.c, .Keyword_extern, "extern") | ||
| 5306 | else | ||
| 5307 | null; | ||
| 5308 | const fn_tok = try appendToken(rp.c, .Keyword_fn, "fn"); | ||
| 5309 | const name_tok = if (fn_decl_context) |ctx| try appendIdentifier(rp.c, ctx.fn_name) else null; | ||
| 5310 | const lparen_tok = try appendToken(rp.c, .LParen, "("); | ||
| 5311 | |||
| 5312 | var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(rp.c.gpa); | ||
| 5313 | defer fn_params.deinit(); | 3932 | defer fn_params.deinit(); |
| 5314 | const param_count: usize = if (fn_proto_ty != null) fn_proto_ty.?.getNumParams() else 0; | 3933 | const param_count: usize = if (fn_proto_ty != null) fn_proto_ty.?.getNumParams() else 0; |
| 5315 | try fn_params.ensureCapacity(param_count + 1); // +1 for possible var args node | 3934 | try fn_params.ensureCapacity(param_count); |
| 5316 | 3935 | ||
| 5317 | var i: usize = 0; | 3936 | var i: usize = 0; |
| 5318 | while (i < param_count) : (i += 1) { | 3937 | while (i < param_count) : (i += 1) { |
| 5319 | const param_qt = fn_proto_ty.?.getParamType(@intCast(c_uint, i)); | 3938 | const param_qt = fn_proto_ty.?.getParamType(@intCast(c_uint, i)); |
| 3939 | const is_noalias = param_qt.isRestrictQualified(); | ||
| 5320 | 3940 | ||
| 5321 | const noalias_tok = if (param_qt.isRestrictQualified()) try appendToken(rp.c, .Keyword_noalias, "noalias") else null; | 3941 | const param_name: ?[]const u8 = |
| 5322 | 3942 | if (fn_decl) |decl| | |
| 5323 | const param_name_tok: ?ast.TokenIndex = blk: { | 3943 | blk: { |
| 5324 | if (fn_decl) |decl| { | 3944 | const param = decl.getParamDecl(@intCast(c_uint, i)); |
| 5325 | const param = decl.getParamDecl(@intCast(c_uint, i)); | 3945 | const param_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, param).getName_bytes_begin()); |
| 5326 | const param_name: []const u8 = try rp.c.str(@ptrCast(*const clang.NamedDecl, param).getName_bytes_begin()); | 3946 | if (param_name.len < 1) |
| 5327 | if (param_name.len < 1) | 3947 | break :blk null; |
| 5328 | break :blk null; | ||
| 5329 | |||
| 5330 | const result = try appendIdentifier(rp.c, param_name); | ||
| 5331 | _ = try appendToken(rp.c, .Colon, ":"); | ||
| 5332 | break :blk result; | ||
| 5333 | } | ||
| 5334 | break :blk null; | ||
| 5335 | }; | ||
| 5336 | 3948 | ||
| 5337 | const type_node = try transQualType(rp, param_qt, source_loc); | 3949 | break :blk param_name; |
| 3950 | } else null; | ||
| 3951 | const type_node = try transQualType(c, scope, param_qt, source_loc); | ||
| 5338 | 3952 | ||
| 5339 | fn_params.addOneAssumeCapacity().* = .{ | 3953 | fn_params.addOneAssumeCapacity().* = .{ |
| 5340 | .doc_comments = null, | 3954 | .is_noalias = is_noalias, |
| 5341 | .comptime_token = null, | 3955 | .name = param_name, |
| 5342 | .noalias_token = noalias_tok, | 3956 | .type = type_node, |
| 5343 | .name_token = param_name_tok, | ||
| 5344 | .param_type = .{ .type_expr = type_node }, | ||
| 5345 | }; | 3957 | }; |
| 5346 | |||
| 5347 | if (i + 1 < param_count) { | ||
| 5348 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 5349 | } | ||
| 5350 | } | 3958 | } |
| 5351 | 3959 | ||
| 5352 | const var_args_token: ?ast.TokenIndex = if (is_var_args) blk: { | 3960 | const linksection_string = blk: { |
| 5353 | if (param_count > 0) { | ||
| 5354 | _ = try appendToken(rp.c, .Comma, ","); | ||
| 5355 | } | ||
| 5356 | break :blk try appendToken(rp.c, .Ellipsis3, "..."); | ||
| 5357 | } else null; | ||
| 5358 | |||
| 5359 | const rparen_tok = try appendToken(rp.c, .RParen, ")"); | ||
| 5360 | |||
| 5361 | const linksection_expr = blk: { | ||
| 5362 | if (fn_decl) |decl| { | 3961 | if (fn_decl) |decl| { |
| 5363 | var str_len: usize = undefined; | 3962 | var str_len: usize = undefined; |
| 5364 | if (decl.getSectionAttribute(&str_len)) |str_ptr| { | 3963 | if (decl.getSectionAttribute(&str_len)) |str_ptr| { |
| 5365 | _ = try appendToken(rp.c, .Keyword_linksection, "linksection"); | 3964 | break :blk str_ptr[0..str_len]; |
| 5366 | _ = try appendToken(rp.c, .LParen, "("); | ||
| 5367 | const expr = try transCreateNodeStringLiteral( | ||
| 5368 | rp.c, | ||
| 5369 | try std.fmt.allocPrint(rp.c.arena, "\"{s}\"", .{str_ptr[0..str_len]}), | ||
| 5370 | ); | ||
| 5371 | _ = try appendToken(rp.c, .RParen, ")"); | ||
| 5372 | |||
| 5373 | break :blk expr; | ||
| 5374 | } | 3965 | } |
| 5375 | } | 3966 | } |
| 5376 | break :blk null; | 3967 | break :blk null; |
| 5377 | }; | 3968 | }; |
| 5378 | 3969 | ||
| 5379 | const align_expr = blk: { | 3970 | const alignment = blk: { |
| 5380 | if (fn_decl) |decl| { | 3971 | if (fn_decl) |decl| { |
| 5381 | const alignment = decl.getAlignedAttribute(rp.c.clang_context); | 3972 | const alignment = decl.getAlignedAttribute(c.clang_context); |
| 5382 | if (alignment != 0) { | 3973 | if (alignment != 0) { |
| 5383 | _ = try appendToken(rp.c, .Keyword_align, "align"); | ||
| 5384 | _ = try appendToken(rp.c, .LParen, "("); | ||
| 5385 | // Clang reports the alignment in bits | 3974 | // Clang reports the alignment in bits |
| 5386 | const expr = try transCreateNodeInt(rp.c, alignment / 8); | 3975 | break :blk alignment / 8; |
| 5387 | _ = try appendToken(rp.c, .RParen, ")"); | ||
| 5388 | |||
| 5389 | break :blk expr; | ||
| 5390 | } | 3976 | } |
| 5391 | } | 3977 | } |
| 5392 | break :blk null; | 3978 | break :blk null; |
| 5393 | }; | 3979 | }; |
| 5394 | 3980 | ||
| 5395 | const callconv_expr = if ((is_export or is_extern) and cc == .C) null else blk: { | 3981 | const explicit_callconv = if ((is_export or is_extern) and cc == .C) null else cc; |
| 5396 | _ = try appendToken(rp.c, .Keyword_callconv, "callconv"); | ||
| 5397 | _ = try appendToken(rp.c, .LParen, "("); | ||
| 5398 | const expr = try transCreateNodeEnumLiteral(rp.c, @tagName(cc)); | ||
| 5399 | _ = try appendToken(rp.c, .RParen, ")"); | ||
| 5400 | break :blk expr; | ||
| 5401 | }; | ||
| 5402 | 3982 | ||
| 5403 | const return_type_node = blk: { | 3983 | const return_type_node = blk: { |
| 5404 | if (fn_ty.getNoReturnAttr()) { | 3984 | if (fn_ty.getNoReturnAttr()) { |
| 5405 | break :blk try transCreateNodeIdentifier(rp.c, "noreturn"); | 3985 | break :blk Tag.noreturn_type.init(); |
| 5406 | } else { | 3986 | } else { |
| 5407 | const return_qt = fn_ty.getReturnType(); | 3987 | const return_qt = fn_ty.getReturnType(); |
| 5408 | if (isCVoid(return_qt)) { | 3988 | if (isCVoid(return_qt)) { |
| 5409 | // convert primitive c_void to actual void (only for return type) | 3989 | // convert primitive c_void to actual void (only for return type) |
| 5410 | break :blk try transCreateNodeIdentifier(rp.c, "void"); | 3990 | break :blk Tag.void_type.init(); |
| 5411 | } else { | 3991 | } else { |
| 5412 | break :blk transQualType(rp, return_qt, source_loc) catch |err| switch (err) { | 3992 | break :blk transQualType(c, scope, return_qt, source_loc) catch |err| switch (err) { |
| 5413 | error.UnsupportedType => { | 3993 | error.UnsupportedType => { |
| 5414 | try emitWarning(rp.c, source_loc, "unsupported function proto return type", .{}); | 3994 | try warn(c, scope, source_loc, "unsupported function proto return type", .{}); |
| 5415 | return err; | 3995 | return err; |
| 5416 | }, | 3996 | }, |
| 5417 | error.OutOfMemory => |e| return e, | 3997 | error.OutOfMemory => |e| return e, |
| ... | @@ -5419,116 +3999,57 @@ fn finishTransFnProto( | ... | @@ -5419,116 +3999,57 @@ fn finishTransFnProto( |
| 5419 | } | 3999 | } |
| 5420 | } | 4000 | } |
| 5421 | }; | 4001 | }; |
| 4002 | const name: ?[]const u8 = if (fn_decl_context) |ctx| ctx.fn_name else null; | ||
| 4003 | const payload = try c.arena.create(ast.Payload.Func); | ||
| 4004 | payload.* = .{ | ||
| 4005 | .base = .{ .tag = .func }, | ||
| 4006 | .data = .{ | ||
| 4007 | .is_pub = is_pub, | ||
| 4008 | .is_extern = is_extern, | ||
| 4009 | .is_export = is_export, | ||
| 4010 | .is_var_args = is_var_args, | ||
| 4011 | .name = name, | ||
| 4012 | .linksection_string = linksection_string, | ||
| 4013 | .explicit_callconv = explicit_callconv, | ||
| 4014 | .params = try c.arena.dupe(ast.Payload.Param, fn_params.items), | ||
| 4015 | .return_type = return_type_node, | ||
| 4016 | .body = null, | ||
| 4017 | .alignment = alignment, | ||
| 4018 | }, | ||
| 4019 | }; | ||
| 4020 | return payload; | ||
| 4021 | } | ||
| 5422 | 4022 | ||
| 5423 | // We need to reserve an undefined (but non-null) body node to set later. | 4023 | fn warn(c: *Context, scope: *Scope, loc: clang.SourceLocation, comptime format: []const u8, args: anytype) !void { |
| 5424 | var body_node: ?*ast.Node = null; | 4024 | const args_prefix = .{c.locStr(loc)}; |
| 5425 | if (fn_decl_context) |ctx| { | 4025 | const value = try std.fmt.allocPrint(c.arena, "// {s}: warning: " ++ format, args_prefix ++ args); |
| 5426 | if (ctx.has_body) { | 4026 | try scope.appendNode(try Tag.warning.create(c.arena, value)); |
| 5427 | // TODO: we should be able to use undefined here but | ||
| 5428 | // it causes a bug. This is undefined without zig language | ||
| 5429 | // being aware of it. | ||
| 5430 | body_node = @intToPtr(*ast.Node, 0x08); | ||
| 5431 | } | ||
| 5432 | } | ||
| 5433 | |||
| 5434 | const fn_proto = try ast.Node.FnProto.create(rp.c.arena, .{ | ||
| 5435 | .params_len = fn_params.items.len, | ||
| 5436 | .return_type = .{ .Explicit = return_type_node }, | ||
| 5437 | .fn_token = fn_tok, | ||
| 5438 | }, .{ | ||
| 5439 | .visib_token = pub_tok, | ||
| 5440 | .name_token = name_tok, | ||
| 5441 | .extern_export_inline_token = extern_export_inline_tok, | ||
| 5442 | .align_expr = align_expr, | ||
| 5443 | .section_expr = linksection_expr, | ||
| 5444 | .callconv_expr = callconv_expr, | ||
| 5445 | .body_node = body_node, | ||
| 5446 | .var_args_token = var_args_token, | ||
| 5447 | }); | ||
| 5448 | mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items); | ||
| 5449 | return fn_proto; | ||
| 5450 | } | 4027 | } |
| 5451 | 4028 | ||
| 5452 | fn revertAndWarn( | 4029 | fn fail( |
| 5453 | rp: RestorePoint, | 4030 | c: *Context, |
| 5454 | err: anytype, | 4031 | err: anytype, |
| 5455 | source_loc: clang.SourceLocation, | 4032 | source_loc: clang.SourceLocation, |
| 5456 | comptime format: []const u8, | 4033 | comptime format: []const u8, |
| 5457 | args: anytype, | 4034 | args: anytype, |
| 5458 | ) (@TypeOf(err) || error{OutOfMemory}) { | 4035 | ) (@TypeOf(err) || error{OutOfMemory}) { |
| 5459 | rp.activate(); | 4036 | try warn(c, &c.global_scope.base, source_loc, format, args); |
| 5460 | try emitWarning(rp.c, source_loc, format, args); | ||
| 5461 | return err; | 4037 | return err; |
| 5462 | } | 4038 | } |
| 5463 | 4039 | ||
| 5464 | fn emitWarning(c: *Context, loc: clang.SourceLocation, comptime format: []const u8, args: anytype) !void { | 4040 | pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, comptime format: []const u8, args: anytype) Error!void { |
| 5465 | const args_prefix = .{c.locStr(loc)}; | 4041 | // location |
| 5466 | _ = try appendTokenFmt(c, .LineComment, "// {s}: warning: " ++ format, args_prefix ++ args); | ||
| 5467 | } | ||
| 5468 | |||
| 5469 | pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, comptime format: []const u8, args: anytype) !void { | ||
| 5470 | // pub const name = @compileError(msg); | 4042 | // pub const name = @compileError(msg); |
| 5471 | const pub_tok = try appendToken(c, .Keyword_pub, "pub"); | 4043 | const fail_msg = try std.fmt.allocPrint(c.arena, format, args); |
| 5472 | const const_tok = try appendToken(c, .Keyword_const, "const"); | 4044 | try addTopLevelDecl(c, name, try Tag.fail_decl.create(c.arena, .{ .actual = name, .mangled = fail_msg })); |
| 5473 | const name_tok = try appendIdentifier(c, name); | 4045 | const location_comment = try std.fmt.allocPrint(c.arena, "// {s}", .{c.locStr(loc)}); |
| 5474 | const eq_tok = try appendToken(c, .Equal, "="); | 4046 | try c.global_scope.nodes.append(try Tag.warning.create(c.arena, location_comment)); |
| 5475 | const builtin_tok = try appendToken(c, .Builtin, "@compileError"); | ||
| 5476 | const lparen_tok = try appendToken(c, .LParen, "("); | ||
| 5477 | const msg_tok = try appendTokenFmt(c, .StringLiteral, "\"" ++ format ++ "\"", args); | ||
| 5478 | const rparen_tok = try appendToken(c, .RParen, ")"); | ||
| 5479 | const semi_tok = try appendToken(c, .Semicolon, ";"); | ||
| 5480 | _ = try appendTokenFmt(c, .LineComment, "// {s}", .{c.locStr(loc)}); | ||
| 5481 | |||
| 5482 | const msg_node = try c.arena.create(ast.Node.OneToken); | ||
| 5483 | msg_node.* = .{ | ||
| 5484 | .base = .{ .tag = .StringLiteral }, | ||
| 5485 | .token = msg_tok, | ||
| 5486 | }; | ||
| 5487 | |||
| 5488 | const call_node = try ast.Node.BuiltinCall.alloc(c.arena, 1); | ||
| 5489 | call_node.* = .{ | ||
| 5490 | .builtin_token = builtin_tok, | ||
| 5491 | .params_len = 1, | ||
| 5492 | .rparen_token = rparen_tok, | ||
| 5493 | }; | ||
| 5494 | call_node.params()[0] = &msg_node.base; | ||
| 5495 | |||
| 5496 | const var_decl_node = try ast.Node.VarDecl.create(c.arena, .{ | ||
| 5497 | .name_token = name_tok, | ||
| 5498 | .mut_token = const_tok, | ||
| 5499 | .semicolon_token = semi_tok, | ||
| 5500 | }, .{ | ||
| 5501 | .visib_token = pub_tok, | ||
| 5502 | .eq_token = eq_tok, | ||
| 5503 | .init_node = &call_node.base, | ||
| 5504 | }); | ||
| 5505 | try addTopLevelDecl(c, name, &var_decl_node.base); | ||
| 5506 | } | ||
| 5507 | |||
| 5508 | fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenIndex { | ||
| 5509 | std.debug.assert(token_id != .Identifier); // use appendIdentifier | ||
| 5510 | return appendTokenFmt(c, token_id, "{s}", .{bytes}); | ||
| 5511 | } | 4047 | } |
| 5512 | 4048 | ||
| 5513 | fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: anytype) !ast.TokenIndex { | 4049 | pub fn freeErrors(errors: []ClangErrMsg) void { |
| 5514 | assert(token_id != .Invalid); | 4050 | errors.ptr.delete(errors.len); |
| 5515 | |||
| 5516 | try c.token_ids.ensureCapacity(c.gpa, c.token_ids.items.len + 1); | ||
| 5517 | try c.token_locs.ensureCapacity(c.gpa, c.token_locs.items.len + 1); | ||
| 5518 | |||
| 5519 | const start_index = c.source_buffer.items.len; | ||
| 5520 | try c.source_buffer.writer().print(format ++ " ", args); | ||
| 5521 | |||
| 5522 | c.token_ids.appendAssumeCapacity(token_id); | ||
| 5523 | c.token_locs.appendAssumeCapacity(.{ | ||
| 5524 | .start = start_index, | ||
| 5525 | .end = c.source_buffer.items.len - 1, // back up before the space | ||
| 5526 | }); | ||
| 5527 | |||
| 5528 | return c.token_ids.items.len - 1; | ||
| 5529 | } | 4051 | } |
| 5530 | 4052 | ||
| 5531 | // TODO hook up with codegen | ||
| 5532 | fn isZigPrimitiveType(name: []const u8) bool { | 4053 | fn isZigPrimitiveType(name: []const u8) bool { |
| 5533 | if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) { | 4054 | if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) { |
| 5534 | for (name[1..]) |c| { | 4055 | for (name[1..]) |c| { |
| ... | @@ -5539,56 +4060,7 @@ fn isZigPrimitiveType(name: []const u8) bool { | ... | @@ -5539,56 +4060,7 @@ fn isZigPrimitiveType(name: []const u8) bool { |
| 5539 | } | 4060 | } |
| 5540 | return true; | 4061 | return true; |
| 5541 | } | 4062 | } |
| 5542 | // void is invalid in c so it doesn't need to be checked. | 4063 | return @import("astgen.zig").simple_types.has(name); |
| 5543 | return mem.eql(u8, name, "comptime_float") or | ||
| 5544 | mem.eql(u8, name, "comptime_int") or | ||
| 5545 | mem.eql(u8, name, "bool") or | ||
| 5546 | mem.eql(u8, name, "isize") or | ||
| 5547 | mem.eql(u8, name, "usize") or | ||
| 5548 | mem.eql(u8, name, "f16") or | ||
| 5549 | mem.eql(u8, name, "f32") or | ||
| 5550 | mem.eql(u8, name, "f64") or | ||
| 5551 | mem.eql(u8, name, "f128") or | ||
| 5552 | mem.eql(u8, name, "c_longdouble") or | ||
| 5553 | mem.eql(u8, name, "noreturn") or | ||
| 5554 | mem.eql(u8, name, "type") or | ||
| 5555 | mem.eql(u8, name, "anyerror") or | ||
| 5556 | mem.eql(u8, name, "c_short") or | ||
| 5557 | mem.eql(u8, name, "c_ushort") or | ||
| 5558 | mem.eql(u8, name, "c_int") or | ||
| 5559 | mem.eql(u8, name, "c_uint") or | ||
| 5560 | mem.eql(u8, name, "c_long") or | ||
| 5561 | mem.eql(u8, name, "c_ulong") or | ||
| 5562 | mem.eql(u8, name, "c_longlong") or | ||
| 5563 | mem.eql(u8, name, "c_ulonglong"); | ||
| 5564 | } | ||
| 5565 | |||
| 5566 | fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex { | ||
| 5567 | return appendTokenFmt(c, .Identifier, "{}", .{std.zig.fmtId(name)}); | ||
| 5568 | } | ||
| 5569 | |||
| 5570 | fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node { | ||
| 5571 | const token_index = try appendIdentifier(c, name); | ||
| 5572 | const identifier = try c.arena.create(ast.Node.OneToken); | ||
| 5573 | identifier.* = .{ | ||
| 5574 | .base = .{ .tag = .Identifier }, | ||
| 5575 | .token = token_index, | ||
| 5576 | }; | ||
| 5577 | return &identifier.base; | ||
| 5578 | } | ||
| 5579 | |||
| 5580 | fn transCreateNodeIdentifierUnchecked(c: *Context, name: []const u8) !*ast.Node { | ||
| 5581 | const token_index = try appendTokenFmt(c, .Identifier, "{s}", .{name}); | ||
| 5582 | const identifier = try c.arena.create(ast.Node.OneToken); | ||
| 5583 | identifier.* = .{ | ||
| 5584 | .base = .{ .tag = .Identifier }, | ||
| 5585 | .token = token_index, | ||
| 5586 | }; | ||
| 5587 | return &identifier.base; | ||
| 5588 | } | ||
| 5589 | |||
| 5590 | pub fn freeErrors(errors: []ClangErrMsg) void { | ||
| 5591 | errors.ptr.delete(errors.len); | ||
| 5592 | } | 4064 | } |
| 5593 | 4065 | ||
| 5594 | const MacroCtx = struct { | 4066 | const MacroCtx = struct { |
| ... | @@ -5709,27 +4181,13 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void { | ... | @@ -5709,27 +4181,13 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void { |
| 5709 | fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void { | 4181 | fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void { |
| 5710 | const scope = &c.global_scope.base; | 4182 | const scope = &c.global_scope.base; |
| 5711 | 4183 | ||
| 5712 | const visib_tok = try appendToken(c, .Keyword_pub, "pub"); | ||
| 5713 | const mut_tok = try appendToken(c, .Keyword_const, "const"); | ||
| 5714 | const name_tok = try appendIdentifier(c, m.name); | ||
| 5715 | const eq_token = try appendToken(c, .Equal, "="); | ||
| 5716 | |||
| 5717 | const init_node = try parseCExpr(c, m, scope); | 4184 | const init_node = try parseCExpr(c, m, scope); |
| 5718 | const last = m.next().?; | 4185 | const last = m.next().?; |
| 5719 | if (last != .Eof and last != .Nl) | 4186 | if (last != .Eof and last != .Nl) |
| 5720 | return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)}); | 4187 | return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)}); |
| 5721 | 4188 | ||
| 5722 | const semicolon_token = try appendToken(c, .Semicolon, ";"); | 4189 | const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node }); |
| 5723 | const node = try ast.Node.VarDecl.create(c.arena, .{ | 4190 | _ = try c.global_scope.macro_table.put(m.name, var_decl); |
| 5724 | .name_token = name_tok, | ||
| 5725 | .mut_token = mut_tok, | ||
| 5726 | .semicolon_token = semicolon_token, | ||
| 5727 | }, .{ | ||
| 5728 | .visib_token = visib_tok, | ||
| 5729 | .eq_token = eq_token, | ||
| 5730 | .init_node = init_node, | ||
| 5731 | }); | ||
| 5732 | _ = try c.global_scope.macro_table.put(m.name, &node.base); | ||
| 5733 | } | 4191 | } |
| 5734 | 4192 | ||
| 5735 | fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { | 4193 | fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { |
| ... | @@ -5737,16 +4195,11 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { | ... | @@ -5737,16 +4195,11 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { |
| 5737 | defer block_scope.deinit(); | 4195 | defer block_scope.deinit(); |
| 5738 | const scope = &block_scope.base; | 4196 | const scope = &block_scope.base; |
| 5739 | 4197 | ||
| 5740 | const pub_tok = try appendToken(c, .Keyword_pub, "pub"); | ||
| 5741 | const fn_tok = try appendToken(c, .Keyword_fn, "fn"); | ||
| 5742 | const name_tok = try appendIdentifier(c, m.name); | ||
| 5743 | _ = try appendToken(c, .LParen, "("); | ||
| 5744 | |||
| 5745 | if (m.next().? != .LParen) { | 4198 | if (m.next().? != .LParen) { |
| 5746 | return m.fail(c, "unable to translate C expr: expected '('", .{}); | 4199 | return m.fail(c, "unable to translate C expr: expected '('", .{}); |
| 5747 | } | 4200 | } |
| 5748 | 4201 | ||
| 5749 | var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(c.gpa); | 4202 | var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa); |
| 5750 | defer fn_params.deinit(); | 4203 | defer fn_params.deinit(); |
| 5751 | 4204 | ||
| 5752 | while (true) { | 4205 | while (true) { |
| ... | @@ -5754,120 +4207,82 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { | ... | @@ -5754,120 +4207,82 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { |
| 5754 | _ = m.next(); | 4207 | _ = m.next(); |
| 5755 | 4208 | ||
| 5756 | const mangled_name = try block_scope.makeMangledName(c, m.slice()); | 4209 | const mangled_name = try block_scope.makeMangledName(c, m.slice()); |
| 5757 | const param_name_tok = try appendIdentifier(c, mangled_name); | 4210 | try fn_params.append(.{ |
| 5758 | _ = try appendToken(c, .Colon, ":"); | 4211 | .is_noalias = false, |
| 5759 | 4212 | .name = mangled_name, | |
| 5760 | const any_type = try c.arena.create(ast.Node.OneToken); | 4213 | .type = Tag.@"anytype".init(), |
| 5761 | any_type.* = .{ | 4214 | }); |
| 5762 | .base = .{ .tag = .AnyType }, | ||
| 5763 | .token = try appendToken(c, .Keyword_anytype, "anytype"), | ||
| 5764 | }; | ||
| 5765 | |||
| 5766 | (try fn_params.addOne()).* = .{ | ||
| 5767 | .doc_comments = null, | ||
| 5768 | .comptime_token = null, | ||
| 5769 | .noalias_token = null, | ||
| 5770 | .name_token = param_name_tok, | ||
| 5771 | .param_type = .{ .any_type = &any_type.base }, | ||
| 5772 | }; | ||
| 5773 | 4215 | ||
| 5774 | if (m.peek().? != .Comma) break; | 4216 | if (m.peek().? != .Comma) break; |
| 5775 | _ = m.next(); | 4217 | _ = m.next(); |
| 5776 | _ = try appendToken(c, .Comma, ","); | ||
| 5777 | } | 4218 | } |
| 5778 | 4219 | ||
| 5779 | if (m.next().? != .RParen) { | 4220 | if (m.next().? != .RParen) { |
| 5780 | return m.fail(c, "unable to translate C expr: expected ')'", .{}); | 4221 | return m.fail(c, "unable to translate C expr: expected ')'", .{}); |
| 5781 | } | 4222 | } |
| 5782 | 4223 | ||
| 5783 | _ = try appendToken(c, .RParen, ")"); | ||
| 5784 | |||
| 5785 | _ = try appendToken(c, .Keyword_callconv, "callconv"); | ||
| 5786 | _ = try appendToken(c, .LParen, "("); | ||
| 5787 | const callconv_expr = try transCreateNodeEnumLiteral(c, "Inline"); | ||
| 5788 | _ = try appendToken(c, .RParen, ")"); | ||
| 5789 | |||
| 5790 | const type_of = try c.createBuiltinCall("@TypeOf", 1); | ||
| 5791 | |||
| 5792 | const return_kw = try appendToken(c, .Keyword_return, "return"); | ||
| 5793 | const expr = try parseCExpr(c, m, scope); | 4224 | const expr = try parseCExpr(c, m, scope); |
| 5794 | const last = m.next().?; | 4225 | const last = m.next().?; |
| 5795 | if (last != .Eof and last != .Nl) | 4226 | if (last != .Eof and last != .Nl) |
| 5796 | return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)}); | 4227 | return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)}); |
| 5797 | _ = try appendToken(c, .Semicolon, ";"); | 4228 | |
| 5798 | const type_of_arg = if (!expr.tag.isBlock()) expr else blk: { | 4229 | const typeof_arg = if (expr.castTag(.block)) |some| blk: { |
| 5799 | const stmts = expr.blockStatements(); | 4230 | const stmts = some.data.stmts; |
| 5800 | const blk_last = stmts[stmts.len - 1]; | 4231 | const blk_last = stmts[stmts.len - 1]; |
| 5801 | const br = blk_last.cast(ast.Node.ControlFlowExpression).?; | 4232 | const br = blk_last.castTag(.break_val).?; |
| 5802 | break :blk br.getRHS().?; | 4233 | break :blk br.data.val; |
| 5803 | }; | 4234 | } else expr; |
| 5804 | type_of.params()[0] = type_of_arg; | 4235 | const return_type = if (typeof_arg.castTag(.std_meta_cast)) |some| |
| 5805 | type_of.rparen_token = try appendToken(c, .RParen, ")"); | 4236 | some.data.lhs |
| 5806 | const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{ | 4237 | else |
| 5807 | .ltoken = return_kw, | 4238 | try Tag.typeof.create(c.arena, typeof_arg); |
| 5808 | .tag = .Return, | ||
| 5809 | }, .{ | ||
| 5810 | .rhs = expr, | ||
| 5811 | }); | ||
| 5812 | 4239 | ||
| 5813 | try block_scope.statements.append(&return_expr.base); | 4240 | const return_expr = try Tag.@"return".create(c.arena, expr); |
| 5814 | const block_node = try block_scope.complete(c); | 4241 | try block_scope.statements.append(return_expr); |
| 5815 | const fn_proto = try ast.Node.FnProto.create(c.arena, .{ | ||
| 5816 | .fn_token = fn_tok, | ||
| 5817 | .params_len = fn_params.items.len, | ||
| 5818 | .return_type = .{ .Explicit = &type_of.base }, | ||
| 5819 | }, .{ | ||
| 5820 | .visib_token = pub_tok, | ||
| 5821 | .name_token = name_tok, | ||
| 5822 | .body_node = block_node, | ||
| 5823 | .callconv_expr = callconv_expr, | ||
| 5824 | }); | ||
| 5825 | mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items); | ||
| 5826 | 4242 | ||
| 5827 | _ = try c.global_scope.macro_table.put(m.name, &fn_proto.base); | 4243 | const fn_decl = try Tag.pub_inline_fn.create(c.arena, .{ |
| 4244 | .name = m.name, | ||
| 4245 | .params = try c.arena.dupe(ast.Payload.Param, fn_params.items), | ||
| 4246 | .return_type = return_type, | ||
| 4247 | .body = try block_scope.complete(c), | ||
| 4248 | }); | ||
| 4249 | _ = try c.global_scope.macro_table.put(m.name, fn_decl); | ||
| 5828 | } | 4250 | } |
| 5829 | 4251 | ||
| 5830 | const ParseError = Error || error{ParseError}; | 4252 | const ParseError = Error || error{ParseError}; |
| 5831 | 4253 | ||
| 5832 | fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 4254 | fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 5833 | // TODO parseCAssignExpr here | 4255 | // TODO parseCAssignExpr here |
| 5834 | const node = try parseCCondExpr(c, m, scope); | 4256 | const node = try parseCCondExpr(c, m, scope); |
| 5835 | if (m.next().? != .Comma) { | 4257 | if (m.next().? != .Comma) { |
| 5836 | m.i -= 1; | 4258 | m.i -= 1; |
| 5837 | return node; | 4259 | return node; |
| 5838 | } | 4260 | } |
| 5839 | _ = try appendToken(c, .Semicolon, ";"); | ||
| 5840 | var block_scope = try Scope.Block.init(c, scope, true); | 4261 | var block_scope = try Scope.Block.init(c, scope, true); |
| 5841 | defer block_scope.deinit(); | 4262 | defer block_scope.deinit(); |
| 5842 | 4263 | ||
| 5843 | var last = node; | 4264 | var last = node; |
| 5844 | while (true) { | 4265 | while (true) { |
| 5845 | // suppress result | 4266 | // suppress result |
| 5846 | const lhs = try transCreateNodeIdentifier(c, "_"); | 4267 | const ignore = try Tag.discard.create(c.arena, last); |
| 5847 | const op_token = try appendToken(c, .Equal, "="); | 4268 | try block_scope.statements.append(ignore); |
| 5848 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 5849 | op_node.* = .{ | ||
| 5850 | .base = .{ .tag = .Assign }, | ||
| 5851 | .op_token = op_token, | ||
| 5852 | .lhs = lhs, | ||
| 5853 | .rhs = last, | ||
| 5854 | }; | ||
| 5855 | try block_scope.statements.append(&op_node.base); | ||
| 5856 | 4269 | ||
| 5857 | last = try parseCCondExpr(c, m, scope); | 4270 | last = try parseCCondExpr(c, m, scope); |
| 5858 | _ = try appendToken(c, .Semicolon, ";"); | ||
| 5859 | if (m.next().? != .Comma) { | 4271 | if (m.next().? != .Comma) { |
| 5860 | m.i -= 1; | 4272 | m.i -= 1; |
| 5861 | break; | 4273 | break; |
| 5862 | } | 4274 | } |
| 5863 | } | 4275 | } |
| 5864 | 4276 | ||
| 5865 | const break_node = try transCreateNodeBreak(c, block_scope.label, last); | 4277 | const break_node = try Tag.break_val.create(c.arena, .{ |
| 5866 | try block_scope.statements.append(&break_node.base); | 4278 | .label = block_scope.label, |
| 4279 | .val = last, | ||
| 4280 | }); | ||
| 4281 | try block_scope.statements.append(break_node); | ||
| 5867 | return try block_scope.complete(c); | 4282 | return try block_scope.complete(c); |
| 5868 | } | 4283 | } |
| 5869 | 4284 | ||
| 5870 | fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node { | 4285 | fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node { |
| 5871 | var lit_bytes = m.slice(); | 4286 | var lit_bytes = m.slice(); |
| 5872 | 4287 | ||
| 5873 | switch (m.list[m.i].id) { | 4288 | switch (m.list[m.i].id) { |
| ... | @@ -5887,11 +4302,10 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node { | ... | @@ -5887,11 +4302,10 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node { |
| 5887 | } | 4302 | } |
| 5888 | 4303 | ||
| 5889 | if (suffix == .none) { | 4304 | if (suffix == .none) { |
| 5890 | return transCreateNodeInt(c, lit_bytes); | 4305 | return transCreateNodeNumber(c, lit_bytes, .int); |
| 5891 | } | 4306 | } |
| 5892 | 4307 | ||
| 5893 | const cast_node = try c.createBuiltinCall("@as", 2); | 4308 | const type_node = try Tag.type.create(c.arena, switch (suffix) { |
| 5894 | cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) { | ||
| 5895 | .u => "c_uint", | 4309 | .u => "c_uint", |
| 5896 | .l => "c_long", | 4310 | .l => "c_long", |
| 5897 | .lu => "c_ulong", | 4311 | .lu => "c_ulong", |
| ... | @@ -5905,27 +4319,22 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node { | ... | @@ -5905,27 +4319,22 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node { |
| 5905 | .llu => 3, | 4319 | .llu => 3, |
| 5906 | else => unreachable, | 4320 | else => unreachable, |
| 5907 | }]; | 4321 | }]; |
| 5908 | _ = try appendToken(c, .Comma, ","); | 4322 | const rhs = try transCreateNodeNumber(c, lit_bytes, .int); |
| 5909 | cast_node.params()[1] = try transCreateNodeInt(c, lit_bytes); | 4323 | return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs }); |
| 5910 | cast_node.rparen_token = try appendToken(c, .RParen, ")"); | ||
| 5911 | return &cast_node.base; | ||
| 5912 | }, | 4324 | }, |
| 5913 | .FloatLiteral => |suffix| { | 4325 | .FloatLiteral => |suffix| { |
| 5914 | if (lit_bytes[0] == '.') | 4326 | if (lit_bytes[0] == '.') |
| 5915 | lit_bytes = try std.fmt.allocPrint(c.arena, "0{s}", .{lit_bytes}); | 4327 | lit_bytes = try std.fmt.allocPrint(c.arena, "0{s}", .{lit_bytes}); |
| 5916 | if (suffix == .none) { | 4328 | if (suffix == .none) { |
| 5917 | return transCreateNodeFloat(c, lit_bytes); | 4329 | return transCreateNodeNumber(c, lit_bytes, .float); |
| 5918 | } | 4330 | } |
| 5919 | const cast_node = try c.createBuiltinCall("@as", 2); | 4331 | const type_node = try Tag.type.create(c.arena, switch (suffix) { |
| 5920 | cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) { | ||
| 5921 | .f => "f32", | 4332 | .f => "f32", |
| 5922 | .l => "c_longdouble", | 4333 | .l => "c_longdouble", |
| 5923 | else => unreachable, | 4334 | else => unreachable, |
| 5924 | }); | 4335 | }); |
| 5925 | _ = try appendToken(c, .Comma, ","); | 4336 | const rhs = try transCreateNodeNumber(c, lit_bytes[0 .. lit_bytes.len - 1], .float); |
| 5926 | cast_node.params()[1] = try transCreateNodeFloat(c, lit_bytes[0 .. lit_bytes.len - 1]); | 4337 | return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs }); |
| 5927 | cast_node.rparen_token = try appendToken(c, .RParen, ")"); | ||
| 5928 | return &cast_node.base; | ||
| 5929 | }, | 4338 | }, |
| 5930 | else => unreachable, | 4339 | else => unreachable, |
| 5931 | } | 4340 | } |
| ... | @@ -6091,79 +4500,62 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { | ... | @@ -6091,79 +4500,62 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { |
| 6091 | return bytes[0..i]; | 4500 | return bytes[0..i]; |
| 6092 | } | 4501 | } |
| 6093 | 4502 | ||
| 6094 | fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 4503 | fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 6095 | const tok = m.next().?; | 4504 | const tok = m.next().?; |
| 6096 | const slice = m.slice(); | 4505 | const slice = m.slice(); |
| 6097 | switch (tok) { | 4506 | switch (tok) { |
| 6098 | .CharLiteral => { | 4507 | .CharLiteral => { |
| 6099 | if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) { | 4508 | if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) { |
| 6100 | const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, m)); | 4509 | return Tag.char_literal.create(c.arena, try zigifyEscapeSequences(c, m)); |
| 6101 | const node = try c.arena.create(ast.Node.OneToken); | ||
| 6102 | node.* = .{ | ||
| 6103 | .base = .{ .tag = .CharLiteral }, | ||
| 6104 | .token = token, | ||
| 6105 | }; | ||
| 6106 | return &node.base; | ||
| 6107 | } else { | 4510 | } else { |
| 6108 | const token = try appendTokenFmt(c, .IntegerLiteral, "0x{x}", .{slice[1 .. slice.len - 1]}); | 4511 | const str = try std.fmt.allocPrint(c.arena, "0x{x}", .{slice[1 .. slice.len - 1]}); |
| 6109 | const node = try c.arena.create(ast.Node.OneToken); | 4512 | return Tag.integer_literal.create(c.arena, str); |
| 6110 | node.* = .{ | ||
| 6111 | .base = .{ .tag = .IntegerLiteral }, | ||
| 6112 | .token = token, | ||
| 6113 | }; | ||
| 6114 | return &node.base; | ||
| 6115 | } | 4513 | } |
| 6116 | }, | 4514 | }, |
| 6117 | .StringLiteral => { | 4515 | .StringLiteral => { |
| 6118 | const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, m)); | 4516 | return Tag.string_literal.create(c.arena, try zigifyEscapeSequences(c, m)); |
| 6119 | const node = try c.arena.create(ast.Node.OneToken); | ||
| 6120 | node.* = .{ | ||
| 6121 | .base = .{ .tag = .StringLiteral }, | ||
| 6122 | .token = token, | ||
| 6123 | }; | ||
| 6124 | return &node.base; | ||
| 6125 | }, | 4517 | }, |
| 6126 | .IntegerLiteral, .FloatLiteral => { | 4518 | .IntegerLiteral, .FloatLiteral => { |
| 6127 | return parseCNumLit(c, m); | 4519 | return parseCNumLit(c, m); |
| 6128 | }, | 4520 | }, |
| 6129 | // eventually this will be replaced by std.c.parse which will handle these correctly | 4521 | // eventually this will be replaced by std.c.parse which will handle these correctly |
| 6130 | .Keyword_void => return transCreateNodeIdentifierUnchecked(c, "c_void"), | 4522 | .Keyword_void => return Tag.type.create(c.arena, "c_void"), |
| 6131 | .Keyword_bool => return transCreateNodeIdentifierUnchecked(c, "bool"), | 4523 | .Keyword_bool => return Tag.type.create(c.arena, "bool"), |
| 6132 | .Keyword_double => return transCreateNodeIdentifierUnchecked(c, "f64"), | 4524 | .Keyword_double => return Tag.type.create(c.arena, "f64"), |
| 6133 | .Keyword_long => return transCreateNodeIdentifierUnchecked(c, "c_long"), | 4525 | .Keyword_long => return Tag.type.create(c.arena, "c_long"), |
| 6134 | .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"), | 4526 | .Keyword_int => return Tag.type.create(c.arena, "c_int"), |
| 6135 | .Keyword_float => return transCreateNodeIdentifierUnchecked(c, "f32"), | 4527 | .Keyword_float => return Tag.type.create(c.arena, "f32"), |
| 6136 | .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"), | 4528 | .Keyword_short => return Tag.type.create(c.arena, "c_short"), |
| 6137 | .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"), | 4529 | .Keyword_char => return Tag.type.create(c.arena, "u8"), |
| 6138 | .Keyword_unsigned => if (m.next()) |t| switch (t) { | 4530 | .Keyword_unsigned => if (m.next()) |t| switch (t) { |
| 6139 | .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"), | 4531 | .Keyword_char => return Tag.type.create(c.arena, "u8"), |
| 6140 | .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_ushort"), | 4532 | .Keyword_short => return Tag.type.create(c.arena, "c_ushort"), |
| 6141 | .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_uint"), | 4533 | .Keyword_int => return Tag.type.create(c.arena, "c_uint"), |
| 6142 | .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) { | 4534 | .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) { |
| 6143 | _ = m.next(); | 4535 | _ = m.next(); |
| 6144 | return transCreateNodeIdentifierUnchecked(c, "c_ulonglong"); | 4536 | return Tag.type.create(c.arena, "c_ulonglong"); |
| 6145 | } else return transCreateNodeIdentifierUnchecked(c, "c_ulong"), | 4537 | } else return Tag.type.create(c.arena, "c_ulong"), |
| 6146 | else => { | 4538 | else => { |
| 6147 | m.i -= 1; | 4539 | m.i -= 1; |
| 6148 | return transCreateNodeIdentifierUnchecked(c, "c_uint"); | 4540 | return Tag.type.create(c.arena, "c_uint"); |
| 6149 | }, | 4541 | }, |
| 6150 | } else { | 4542 | } else { |
| 6151 | return transCreateNodeIdentifierUnchecked(c, "c_uint"); | 4543 | return Tag.type.create(c.arena, "c_uint"); |
| 6152 | }, | 4544 | }, |
| 6153 | .Keyword_signed => if (m.next()) |t| switch (t) { | 4545 | .Keyword_signed => if (m.next()) |t| switch (t) { |
| 6154 | .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "i8"), | 4546 | .Keyword_char => return Tag.type.create(c.arena, "i8"), |
| 6155 | .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"), | 4547 | .Keyword_short => return Tag.type.create(c.arena, "c_short"), |
| 6156 | .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"), | 4548 | .Keyword_int => return Tag.type.create(c.arena, "c_int"), |
| 6157 | .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) { | 4549 | .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) { |
| 6158 | _ = m.next(); | 4550 | _ = m.next(); |
| 6159 | return transCreateNodeIdentifierUnchecked(c, "c_longlong"); | 4551 | return Tag.type.create(c.arena, "c_longlong"); |
| 6160 | } else return transCreateNodeIdentifierUnchecked(c, "c_long"), | 4552 | } else return Tag.type.create(c.arena, "c_long"), |
| 6161 | else => { | 4553 | else => { |
| 6162 | m.i -= 1; | 4554 | m.i -= 1; |
| 6163 | return transCreateNodeIdentifierUnchecked(c, "c_int"); | 4555 | return Tag.type.create(c.arena, "c_int"); |
| 6164 | }, | 4556 | }, |
| 6165 | } else { | 4557 | } else { |
| 6166 | return transCreateNodeIdentifierUnchecked(c, "c_int"); | 4558 | return Tag.type.create(c.arena, "c_int"); |
| 6167 | }, | 4559 | }, |
| 6168 | .Keyword_enum, .Keyword_struct, .Keyword_union => { | 4560 | .Keyword_enum, .Keyword_struct, .Keyword_union => { |
| 6169 | // struct Foo will be declared as struct_Foo by transRecordDecl | 4561 | // struct Foo will be declared as struct_Foo by transRecordDecl |
| ... | @@ -6173,17 +4565,12 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* | ... | @@ -6173,17 +4565,12 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* |
| 6173 | return error.ParseError; | 4565 | return error.ParseError; |
| 6174 | } | 4566 | } |
| 6175 | 4567 | ||
| 6176 | const ident_token = try appendTokenFmt(c, .Identifier, "{s}_{s}", .{ slice, m.slice() }); | 4568 | const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ slice, m.slice() }); |
| 6177 | const identifier = try c.arena.create(ast.Node.OneToken); | 4569 | return Tag.identifier.create(c.arena, name); |
| 6178 | identifier.* = .{ | ||
| 6179 | .base = .{ .tag = .Identifier }, | ||
| 6180 | .token = ident_token, | ||
| 6181 | }; | ||
| 6182 | return &identifier.base; | ||
| 6183 | }, | 4570 | }, |
| 6184 | .Identifier => { | 4571 | .Identifier => { |
| 6185 | const mangled_name = scope.getAlias(slice); | 4572 | const mangled_name = scope.getAlias(slice); |
| 6186 | return transCreateNodeIdentifier(c, checkForBuiltinTypedef(mangled_name) orelse mangled_name); | 4573 | return Tag.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name); |
| 6187 | }, | 4574 | }, |
| 6188 | .LParen => { | 4575 | .LParen => { |
| 6189 | const inner_node = try parseCExpr(c, m, scope); | 4576 | const inner_node = try parseCExpr(c, m, scope); |
| ... | @@ -6213,10 +4600,6 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* | ... | @@ -6213,10 +4600,6 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* |
| 6213 | }, | 4600 | }, |
| 6214 | else => return inner_node, | 4601 | else => return inner_node, |
| 6215 | } | 4602 | } |
| 6216 | |||
| 6217 | // hack to get zig fmt to render a comma in builtin calls | ||
| 6218 | _ = try appendToken(c, .Comma, ","); | ||
| 6219 | |||
| 6220 | const node_to_cast = try parseCExpr(c, m, scope); | 4603 | const node_to_cast = try parseCExpr(c, m, scope); |
| 6221 | 4604 | ||
| 6222 | if (saw_l_paren and m.next().? != .RParen) { | 4605 | if (saw_l_paren and m.next().? != .RParen) { |
| ... | @@ -6224,28 +4607,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* | ... | @@ -6224,28 +4607,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* |
| 6224 | return error.ParseError; | 4607 | return error.ParseError; |
| 6225 | } | 4608 | } |
| 6226 | 4609 | ||
| 6227 | const lparen = try appendToken(c, .LParen, "("); | 4610 | return Tag.std_meta_cast.create(c.arena, .{ .lhs = inner_node, .rhs = node_to_cast }); |
| 6228 | |||
| 6229 | //(@import("std").meta.cast(dest, x)) | ||
| 6230 | const import_fn_call = try c.createBuiltinCall("@import", 1); | ||
| 6231 | const std_node = try transCreateNodeStringLiteral(c, "\"std\""); | ||
| 6232 | import_fn_call.params()[0] = std_node; | ||
| 6233 | import_fn_call.rparen_token = try appendToken(c, .RParen, ")"); | ||
| 6234 | const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta"); | ||
| 6235 | const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "cast"); | ||
| 6236 | |||
| 6237 | const cast_fn_call = try c.createCall(outer_field_access, 2); | ||
| 6238 | cast_fn_call.params()[0] = inner_node; | ||
| 6239 | cast_fn_call.params()[1] = node_to_cast; | ||
| 6240 | cast_fn_call.rtoken = try appendToken(c, .RParen, ")"); | ||
| 6241 | |||
| 6242 | const group_node = try c.arena.create(ast.Node.GroupedExpression); | ||
| 6243 | group_node.* = .{ | ||
| 6244 | .lparen = lparen, | ||
| 6245 | .expr = &cast_fn_call.base, | ||
| 6246 | .rparen = try appendToken(c, .RParen, ")"), | ||
| 6247 | }; | ||
| 6248 | return &group_node.base; | ||
| 6249 | }, | 4611 | }, |
| 6250 | else => { | 4612 | else => { |
| 6251 | try m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(tok)}); | 4613 | try m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(tok)}); |
| ... | @@ -6254,447 +4616,256 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* | ... | @@ -6254,447 +4616,256 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* |
| 6254 | } | 4616 | } |
| 6255 | } | 4617 | } |
| 6256 | 4618 | ||
| 6257 | fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 4619 | fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 6258 | var node = try parseCPrimaryExprInner(c, m, scope); | 4620 | var node = try parseCPrimaryExprInner(c, m, scope); |
| 6259 | // In C the preprocessor would handle concatting strings while expanding macros. | 4621 | // In C the preprocessor would handle concatting strings while expanding macros. |
| 6260 | // This should do approximately the same by concatting any strings and identifiers | 4622 | // This should do approximately the same by concatting any strings and identifiers |
| 6261 | // after a primary expression. | 4623 | // after a primary expression. |
| 6262 | while (true) { | 4624 | while (true) { |
| 6263 | var op_token: ast.TokenIndex = undefined; | ||
| 6264 | var op_id: ast.Node.Tag = undefined; | ||
| 6265 | switch (m.peek().?) { | 4625 | switch (m.peek().?) { |
| 6266 | .StringLiteral, .Identifier => {}, | 4626 | .StringLiteral, .Identifier => {}, |
| 6267 | else => break, | 4627 | else => break, |
| 6268 | } | 4628 | } |
| 6269 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | 4629 | node = try Tag.array_cat.create(c.arena, .{ .lhs = node, .rhs = try parseCPrimaryExprInner(c, m, scope) }); |
| 6270 | op_node.* = .{ | ||
| 6271 | .base = .{ .tag = .ArrayCat }, | ||
| 6272 | .op_token = try appendToken(c, .PlusPlus, "++"), | ||
| 6273 | .lhs = node, | ||
| 6274 | .rhs = try parseCPrimaryExprInner(c, m, scope), | ||
| 6275 | }; | ||
| 6276 | node = &op_node.base; | ||
| 6277 | } | 4630 | } |
| 6278 | return node; | 4631 | return node; |
| 6279 | } | 4632 | } |
| 6280 | 4633 | ||
| 6281 | fn nodeIsInfixOp(tag: ast.Node.Tag) bool { | 4634 | fn macroBoolToInt(c: *Context, node: Node) !Node { |
| 6282 | return switch (tag) { | ||
| 6283 | .Add, | ||
| 6284 | .AddWrap, | ||
| 6285 | .ArrayCat, | ||
| 6286 | .ArrayMult, | ||
| 6287 | .Assign, | ||
| 6288 | .AssignBitAnd, | ||
| 6289 | .AssignBitOr, | ||
| 6290 | .AssignBitShiftLeft, | ||
| 6291 | .AssignBitShiftRight, | ||
| 6292 | .AssignBitXor, | ||
| 6293 | .AssignDiv, | ||
| 6294 | .AssignSub, | ||
| 6295 | .AssignSubWrap, | ||
| 6296 | .AssignMod, | ||
| 6297 | .AssignAdd, | ||
| 6298 | .AssignAddWrap, | ||
| 6299 | .AssignMul, | ||
| 6300 | .AssignMulWrap, | ||
| 6301 | .BangEqual, | ||
| 6302 | .BitAnd, | ||
| 6303 | .BitOr, | ||
| 6304 | .BitShiftLeft, | ||
| 6305 | .BitShiftRight, | ||
| 6306 | .BitXor, | ||
| 6307 | .BoolAnd, | ||
| 6308 | .BoolOr, | ||
| 6309 | .Div, | ||
| 6310 | .EqualEqual, | ||
| 6311 | .ErrorUnion, | ||
| 6312 | .GreaterOrEqual, | ||
| 6313 | .GreaterThan, | ||
| 6314 | .LessOrEqual, | ||
| 6315 | .LessThan, | ||
| 6316 | .MergeErrorSets, | ||
| 6317 | .Mod, | ||
| 6318 | .Mul, | ||
| 6319 | .MulWrap, | ||
| 6320 | .Period, | ||
| 6321 | .Range, | ||
| 6322 | .Sub, | ||
| 6323 | .SubWrap, | ||
| 6324 | .UnwrapOptional, | ||
| 6325 | .Catch, | ||
| 6326 | => true, | ||
| 6327 | |||
| 6328 | else => false, | ||
| 6329 | }; | ||
| 6330 | } | ||
| 6331 | |||
| 6332 | fn macroBoolToInt(c: *Context, node: *ast.Node) !*ast.Node { | ||
| 6333 | if (!isBoolRes(node)) { | 4635 | if (!isBoolRes(node)) { |
| 6334 | if (!nodeIsInfixOp(node.tag)) return node; | 4636 | return node; |
| 6335 | |||
| 6336 | const group_node = try c.arena.create(ast.Node.GroupedExpression); | ||
| 6337 | group_node.* = .{ | ||
| 6338 | .lparen = try appendToken(c, .LParen, "("), | ||
| 6339 | .expr = node, | ||
| 6340 | .rparen = try appendToken(c, .RParen, ")"), | ||
| 6341 | }; | ||
| 6342 | return &group_node.base; | ||
| 6343 | } | 4637 | } |
| 6344 | 4638 | ||
| 6345 | const builtin_node = try c.createBuiltinCall("@boolToInt", 1); | 4639 | return Tag.bool_to_int.create(c.arena, node); |
| 6346 | builtin_node.params()[0] = node; | ||
| 6347 | builtin_node.rparen_token = try appendToken(c, .RParen, ")"); | ||
| 6348 | return &builtin_node.base; | ||
| 6349 | } | 4640 | } |
| 6350 | 4641 | ||
| 6351 | fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node { | 4642 | fn macroIntToBool(c: *Context, node: Node) !Node { |
| 6352 | if (isBoolRes(node)) { | 4643 | if (isBoolRes(node)) { |
| 6353 | if (!nodeIsInfixOp(node.tag)) return node; | 4644 | return node; |
| 6354 | |||
| 6355 | const group_node = try c.arena.create(ast.Node.GroupedExpression); | ||
| 6356 | group_node.* = .{ | ||
| 6357 | .lparen = try appendToken(c, .LParen, "("), | ||
| 6358 | .expr = node, | ||
| 6359 | .rparen = try appendToken(c, .RParen, ")"), | ||
| 6360 | }; | ||
| 6361 | return &group_node.base; | ||
| 6362 | } | 4645 | } |
| 6363 | 4646 | ||
| 6364 | const op_token = try appendToken(c, .BangEqual, "!="); | 4647 | return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() }); |
| 6365 | const zero = try transCreateNodeInt(c, 0); | ||
| 6366 | const res = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6367 | res.* = .{ | ||
| 6368 | .base = .{ .tag = .BangEqual }, | ||
| 6369 | .op_token = op_token, | ||
| 6370 | .lhs = node, | ||
| 6371 | .rhs = zero, | ||
| 6372 | }; | ||
| 6373 | const group_node = try c.arena.create(ast.Node.GroupedExpression); | ||
| 6374 | group_node.* = .{ | ||
| 6375 | .lparen = try appendToken(c, .LParen, "("), | ||
| 6376 | .expr = &res.base, | ||
| 6377 | .rparen = try appendToken(c, .RParen, ")"), | ||
| 6378 | }; | ||
| 6379 | return &group_node.base; | ||
| 6380 | } | 4648 | } |
| 6381 | 4649 | ||
| 6382 | fn macroGroup(c: *Context, node: *ast.Node) !*ast.Node { | 4650 | fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 6383 | if (!nodeIsInfixOp(node.tag)) return node; | ||
| 6384 | |||
| 6385 | const group_node = try c.arena.create(ast.Node.GroupedExpression); | ||
| 6386 | group_node.* = .{ | ||
| 6387 | .lparen = try appendToken(c, .LParen, "("), | ||
| 6388 | .expr = node, | ||
| 6389 | .rparen = try appendToken(c, .RParen, ")"), | ||
| 6390 | }; | ||
| 6391 | return &group_node.base; | ||
| 6392 | } | ||
| 6393 | |||
| 6394 | fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | ||
| 6395 | const node = try parseCOrExpr(c, m, scope); | 4651 | const node = try parseCOrExpr(c, m, scope); |
| 6396 | if (m.peek().? != .QuestionMark) { | 4652 | if (m.peek().? != .QuestionMark) { |
| 6397 | return node; | 4653 | return node; |
| 6398 | } | 4654 | } |
| 6399 | _ = m.next(); | 4655 | _ = m.next(); |
| 6400 | 4656 | ||
| 6401 | // must come immediately after expr | 4657 | const then_body = try parseCOrExpr(c, m, scope); |
| 6402 | _ = try appendToken(c, .RParen, ")"); | ||
| 6403 | const if_node = try transCreateNodeIf(c); | ||
| 6404 | if_node.condition = node; | ||
| 6405 | if_node.body = try parseCOrExpr(c, m, scope); | ||
| 6406 | if (m.next().? != .Colon) { | 4658 | if (m.next().? != .Colon) { |
| 6407 | try m.fail(c, "unable to translate C expr: expected ':'", .{}); | 4659 | try m.fail(c, "unable to translate C expr: expected ':'", .{}); |
| 6408 | return error.ParseError; | 4660 | return error.ParseError; |
| 6409 | } | 4661 | } |
| 6410 | if_node.@"else" = try transCreateNodeElse(c); | 4662 | const else_body = try parseCCondExpr(c, m, scope); |
| 6411 | if_node.@"else".?.body = try parseCCondExpr(c, m, scope); | 4663 | return Tag.@"if".create(c.arena, .{ .cond = node, .then = then_body, .@"else" = else_body }); |
| 6412 | return &if_node.base; | ||
| 6413 | } | 4664 | } |
| 6414 | 4665 | ||
| 6415 | fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 4666 | fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 6416 | var node = try parseCAndExpr(c, m, scope); | 4667 | var node = try parseCAndExpr(c, m, scope); |
| 6417 | while (m.next().? == .PipePipe) { | 4668 | while (m.next().? == .PipePipe) { |
| 6418 | const lhs_node = try macroIntToBool(c, node); | 4669 | const lhs = try macroIntToBool(c, node); |
| 6419 | const op_token = try appendToken(c, .Keyword_or, "or"); | 4670 | const rhs = try macroIntToBool(c, try parseCAndExpr(c, m, scope)); |
| 6420 | const rhs_node = try parseCAndExpr(c, m, scope); | 4671 | node = try Tag.@"or".create(c.arena, .{ .lhs = lhs, .rhs = rhs }); |
| 6421 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6422 | op_node.* = .{ | ||
| 6423 | .base = .{ .tag = .BoolOr }, | ||
| 6424 | .op_token = op_token, | ||
| 6425 | .lhs = lhs_node, | ||
| 6426 | .rhs = try macroIntToBool(c, rhs_node), | ||
| 6427 | }; | ||
| 6428 | node = &op_node.base; | ||
| 6429 | } | 4672 | } |
| 6430 | m.i -= 1; | 4673 | m.i -= 1; |
| 6431 | return node; | 4674 | return node; |
| 6432 | } | 4675 | } |
| 6433 | 4676 | ||
| 6434 | fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 4677 | fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 6435 | var node = try parseCBitOrExpr(c, m, scope); | 4678 | var node = try parseCBitOrExpr(c, m, scope); |
| 6436 | while (m.next().? == .AmpersandAmpersand) { | 4679 | while (m.next().? == .AmpersandAmpersand) { |
| 6437 | const lhs_node = try macroIntToBool(c, node); | 4680 | const lhs = try macroIntToBool(c, node); |
| 6438 | const op_token = try appendToken(c, .Keyword_and, "and"); | 4681 | const rhs = try macroIntToBool(c, try parseCBitOrExpr(c, m, scope)); |
| 6439 | const rhs_node = try parseCBitOrExpr(c, m, scope); | 4682 | node = try Tag.@"and".create(c.arena, .{ .lhs = lhs, .rhs = rhs }); |
| 6440 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6441 | op_node.* = .{ | ||
| 6442 | .base = .{ .tag = .BoolAnd }, | ||
| 6443 | .op_token = op_token, | ||
| 6444 | .lhs = lhs_node, | ||
| 6445 | .rhs = try macroIntToBool(c, rhs_node), | ||
| 6446 | }; | ||
| 6447 | node = &op_node.base; | ||
| 6448 | } | 4683 | } |
| 6449 | m.i -= 1; | 4684 | m.i -= 1; |
| 6450 | return node; | 4685 | return node; |
| 6451 | } | 4686 | } |
| 6452 | 4687 | ||
| 6453 | fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 4688 | fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 6454 | var node = try parseCBitXorExpr(c, m, scope); | 4689 | var node = try parseCBitXorExpr(c, m, scope); |
| 6455 | while (m.next().? == .Pipe) { | 4690 | while (m.next().? == .Pipe) { |
| 6456 | const lhs_node = try macroBoolToInt(c, node); | 4691 | const lhs = try macroBoolToInt(c, node); |
| 6457 | const op_token = try appendToken(c, .Pipe, "|"); | 4692 | const rhs = try macroBoolToInt(c, try parseCBitXorExpr(c, m, scope)); |
| 6458 | const rhs_node = try parseCBitXorExpr(c, m, scope); | 4693 | node = try Tag.bit_or.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); |
| 6459 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6460 | op_node.* = .{ | ||
| 6461 | .base = .{ .tag = .BitOr }, | ||
| 6462 | .op_token = op_token, | ||
| 6463 | .lhs = lhs_node, | ||
| 6464 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6465 | }; | ||
| 6466 | node = &op_node.base; | ||
| 6467 | } | 4694 | } |
| 6468 | m.i -= 1; | 4695 | m.i -= 1; |
| 6469 | return node; | 4696 | return node; |
| 6470 | } | 4697 | } |
| 6471 | 4698 | ||
| 6472 | fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 4699 | fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 6473 | var node = try parseCBitAndExpr(c, m, scope); | 4700 | var node = try parseCBitAndExpr(c, m, scope); |
| 6474 | while (m.next().? == .Caret) { | 4701 | while (m.next().? == .Caret) { |
| 6475 | const lhs_node = try macroBoolToInt(c, node); | 4702 | const lhs = try macroBoolToInt(c, node); |
| 6476 | const op_token = try appendToken(c, .Caret, "^"); | 4703 | const rhs = try macroBoolToInt(c, try parseCBitAndExpr(c, m, scope)); |
| 6477 | const rhs_node = try parseCBitAndExpr(c, m, scope); | 4704 | node = try Tag.bit_xor.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); |
| 6478 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6479 | op_node.* = .{ | ||
| 6480 | .base = .{ .tag = .BitXor }, | ||
| 6481 | .op_token = op_token, | ||
| 6482 | .lhs = lhs_node, | ||
| 6483 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6484 | }; | ||
| 6485 | node = &op_node.base; | ||
| 6486 | } | 4705 | } |
| 6487 | m.i -= 1; | 4706 | m.i -= 1; |
| 6488 | return node; | 4707 | return node; |
| 6489 | } | 4708 | } |
| 6490 | 4709 | ||
| 6491 | fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 4710 | fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 6492 | var node = try parseCEqExpr(c, m, scope); | 4711 | var node = try parseCEqExpr(c, m, scope); |
| 6493 | while (m.next().? == .Ampersand) { | 4712 | while (m.next().? == .Ampersand) { |
| 6494 | const lhs_node = try macroBoolToInt(c, node); | 4713 | const lhs = try macroBoolToInt(c, node); |
| 6495 | const op_token = try appendToken(c, .Ampersand, "&"); | 4714 | const rhs = try macroBoolToInt(c, try parseCEqExpr(c, m, scope)); |
| 6496 | const rhs_node = try parseCEqExpr(c, m, scope); | 4715 | node = try Tag.bit_and.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); |
| 6497 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6498 | op_node.* = .{ | ||
| 6499 | .base = .{ .tag = .BitAnd }, | ||
| 6500 | .op_token = op_token, | ||
| 6501 | .lhs = lhs_node, | ||
| 6502 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6503 | }; | ||
| 6504 | node = &op_node.base; | ||
| 6505 | } | 4716 | } |
| 6506 | m.i -= 1; | 4717 | m.i -= 1; |
| 6507 | return node; | 4718 | return node; |
| 6508 | } | 4719 | } |
| 6509 | 4720 | ||
| 6510 | fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 4721 | fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 6511 | var node = try parseCRelExpr(c, m, scope); | 4722 | var node = try parseCRelExpr(c, m, scope); |
| 6512 | while (true) { | 4723 | while (true) { |
| 6513 | var op_token: ast.TokenIndex = undefined; | ||
| 6514 | var op_id: ast.Node.Tag = undefined; | ||
| 6515 | switch (m.peek().?) { | 4724 | switch (m.peek().?) { |
| 6516 | .BangEqual => { | 4725 | .BangEqual => { |
| 6517 | op_token = try appendToken(c, .BangEqual, "!="); | 4726 | _ = m.next(); |
| 6518 | op_id = .BangEqual; | 4727 | const lhs = try macroBoolToInt(c, node); |
| 4728 | const rhs = try macroBoolToInt(c, try parseCRelExpr(c, m, scope)); | ||
| 4729 | node = try Tag.not_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); | ||
| 6519 | }, | 4730 | }, |
| 6520 | .EqualEqual => { | 4731 | .EqualEqual => { |
| 6521 | op_token = try appendToken(c, .EqualEqual, "=="); | 4732 | _ = m.next(); |
| 6522 | op_id = .EqualEqual; | 4733 | const lhs = try macroBoolToInt(c, node); |
| 4734 | const rhs = try macroBoolToInt(c, try parseCRelExpr(c, m, scope)); | ||
| 4735 | node = try Tag.equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); | ||
| 6523 | }, | 4736 | }, |
| 6524 | else => return node, | 4737 | else => return node, |
| 6525 | } | 4738 | } |
| 6526 | _ = m.next(); | ||
| 6527 | const lhs_node = try macroBoolToInt(c, node); | ||
| 6528 | const rhs_node = try parseCRelExpr(c, m, scope); | ||
| 6529 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6530 | op_node.* = .{ | ||
| 6531 | .base = .{ .tag = op_id }, | ||
| 6532 | .op_token = op_token, | ||
| 6533 | .lhs = lhs_node, | ||
| 6534 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6535 | }; | ||
| 6536 | node = &op_node.base; | ||
| 6537 | } | 4739 | } |
| 6538 | } | 4740 | } |
| 6539 | 4741 | ||
| 6540 | fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 4742 | fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 6541 | var node = try parseCShiftExpr(c, m, scope); | 4743 | var node = try parseCShiftExpr(c, m, scope); |
| 6542 | while (true) { | 4744 | while (true) { |
| 6543 | var op_token: ast.TokenIndex = undefined; | ||
| 6544 | var op_id: ast.Node.Tag = undefined; | ||
| 6545 | switch (m.peek().?) { | 4745 | switch (m.peek().?) { |
| 6546 | .AngleBracketRight => { | 4746 | .AngleBracketRight => { |
| 6547 | op_token = try appendToken(c, .AngleBracketRight, ">"); | 4747 | _ = m.next(); |
| 6548 | op_id = .GreaterThan; | 4748 | const lhs = try macroBoolToInt(c, node); |
| 4749 | const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope)); | ||
| 4750 | node = try Tag.greater_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); | ||
| 6549 | }, | 4751 | }, |
| 6550 | .AngleBracketRightEqual => { | 4752 | .AngleBracketRightEqual => { |
| 6551 | op_token = try appendToken(c, .AngleBracketRightEqual, ">="); | 4753 | _ = m.next(); |
| 6552 | op_id = .GreaterOrEqual; | 4754 | const lhs = try macroBoolToInt(c, node); |
| 4755 | const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope)); | ||
| 4756 | node = try Tag.greater_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); | ||
| 6553 | }, | 4757 | }, |
| 6554 | .AngleBracketLeft => { | 4758 | .AngleBracketLeft => { |
| 6555 | op_token = try appendToken(c, .AngleBracketLeft, "<"); | 4759 | _ = m.next(); |
| 6556 | op_id = .LessThan; | 4760 | const lhs = try macroBoolToInt(c, node); |
| 4761 | const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope)); | ||
| 4762 | node = try Tag.less_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); | ||
| 6557 | }, | 4763 | }, |
| 6558 | .AngleBracketLeftEqual => { | 4764 | .AngleBracketLeftEqual => { |
| 6559 | op_token = try appendToken(c, .AngleBracketLeftEqual, "<="); | 4765 | _ = m.next(); |
| 6560 | op_id = .LessOrEqual; | 4766 | const lhs = try macroBoolToInt(c, node); |
| 4767 | const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope)); | ||
| 4768 | node = try Tag.less_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); | ||
| 6561 | }, | 4769 | }, |
| 6562 | else => return node, | 4770 | else => return node, |
| 6563 | } | 4771 | } |
| 6564 | _ = m.next(); | ||
| 6565 | const lhs_node = try macroBoolToInt(c, node); | ||
| 6566 | const rhs_node = try parseCShiftExpr(c, m, scope); | ||
| 6567 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6568 | op_node.* = .{ | ||
| 6569 | .base = .{ .tag = op_id }, | ||
| 6570 | .op_token = op_token, | ||
| 6571 | .lhs = lhs_node, | ||
| 6572 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6573 | }; | ||
| 6574 | node = &op_node.base; | ||
| 6575 | } | 4772 | } |
| 6576 | } | 4773 | } |
| 6577 | 4774 | ||
| 6578 | fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 4775 | fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 6579 | var node = try parseCAddSubExpr(c, m, scope); | 4776 | var node = try parseCAddSubExpr(c, m, scope); |
| 6580 | while (true) { | 4777 | while (true) { |
| 6581 | var op_token: ast.TokenIndex = undefined; | ||
| 6582 | var op_id: ast.Node.Tag = undefined; | ||
| 6583 | switch (m.peek().?) { | 4778 | switch (m.peek().?) { |
| 6584 | .AngleBracketAngleBracketLeft => { | 4779 | .AngleBracketAngleBracketLeft => { |
| 6585 | op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<"); | 4780 | _ = m.next(); |
| 6586 | op_id = .BitShiftLeft; | 4781 | const lhs = try macroBoolToInt(c, node); |
| 4782 | const rhs = try macroBoolToInt(c, try parseCAddSubExpr(c, m, scope)); | ||
| 4783 | node = try Tag.shl.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); | ||
| 6587 | }, | 4784 | }, |
| 6588 | .AngleBracketAngleBracketRight => { | 4785 | .AngleBracketAngleBracketRight => { |
| 6589 | op_token = try appendToken(c, .AngleBracketAngleBracketRight, ">>"); | 4786 | _ = m.next(); |
| 6590 | op_id = .BitShiftRight; | 4787 | const lhs = try macroBoolToInt(c, node); |
| 4788 | const rhs = try macroBoolToInt(c, try parseCAddSubExpr(c, m, scope)); | ||
| 4789 | node = try Tag.shr.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); | ||
| 6591 | }, | 4790 | }, |
| 6592 | else => return node, | 4791 | else => return node, |
| 6593 | } | 4792 | } |
| 6594 | _ = m.next(); | ||
| 6595 | const lhs_node = try macroBoolToInt(c, node); | ||
| 6596 | const rhs_node = try parseCAddSubExpr(c, m, scope); | ||
| 6597 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6598 | op_node.* = .{ | ||
| 6599 | .base = .{ .tag = op_id }, | ||
| 6600 | .op_token = op_token, | ||
| 6601 | .lhs = lhs_node, | ||
| 6602 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6603 | }; | ||
| 6604 | node = &op_node.base; | ||
| 6605 | } | 4793 | } |
| 6606 | } | 4794 | } |
| 6607 | 4795 | ||
| 6608 | fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 4796 | fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 6609 | var node = try parseCMulExpr(c, m, scope); | 4797 | var node = try parseCMulExpr(c, m, scope); |
| 6610 | while (true) { | 4798 | while (true) { |
| 6611 | var op_token: ast.TokenIndex = undefined; | ||
| 6612 | var op_id: ast.Node.Tag = undefined; | ||
| 6613 | switch (m.peek().?) { | 4799 | switch (m.peek().?) { |
| 6614 | .Plus => { | 4800 | .Plus => { |
| 6615 | op_token = try appendToken(c, .Plus, "+"); | 4801 | _ = m.next(); |
| 6616 | op_id = .Add; | 4802 | const lhs = try macroBoolToInt(c, node); |
| 4803 | const rhs = try macroBoolToInt(c, try parseCMulExpr(c, m, scope)); | ||
| 4804 | node = try Tag.add.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); | ||
| 6617 | }, | 4805 | }, |
| 6618 | .Minus => { | 4806 | .Minus => { |
| 6619 | op_token = try appendToken(c, .Minus, "-"); | 4807 | _ = m.next(); |
| 6620 | op_id = .Sub; | 4808 | const lhs = try macroBoolToInt(c, node); |
| 4809 | const rhs = try macroBoolToInt(c, try parseCMulExpr(c, m, scope)); | ||
| 4810 | node = try Tag.sub.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); | ||
| 6621 | }, | 4811 | }, |
| 6622 | else => return node, | 4812 | else => return node, |
| 6623 | } | 4813 | } |
| 6624 | _ = m.next(); | ||
| 6625 | const lhs_node = try macroBoolToInt(c, node); | ||
| 6626 | const rhs_node = try parseCMulExpr(c, m, scope); | ||
| 6627 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6628 | op_node.* = .{ | ||
| 6629 | .base = .{ .tag = op_id }, | ||
| 6630 | .op_token = op_token, | ||
| 6631 | .lhs = lhs_node, | ||
| 6632 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6633 | }; | ||
| 6634 | node = &op_node.base; | ||
| 6635 | } | 4814 | } |
| 6636 | } | 4815 | } |
| 6637 | 4816 | ||
| 6638 | fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 4817 | fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 6639 | var node = try parseCUnaryExpr(c, m, scope); | 4818 | var node = try parseCUnaryExpr(c, m, scope); |
| 6640 | while (true) { | 4819 | while (true) { |
| 6641 | var op_token: ast.TokenIndex = undefined; | ||
| 6642 | var op_id: ast.Node.Tag = undefined; | ||
| 6643 | switch (m.next().?) { | 4820 | switch (m.next().?) { |
| 6644 | .Asterisk => { | 4821 | .Asterisk => { |
| 6645 | if (m.peek().? == .RParen) { | 4822 | const next = m.peek().?; |
| 4823 | if (next == .RParen or next == .Nl or next == .Eof) { | ||
| 6646 | // type *) | 4824 | // type *) |
| 6647 | 4825 | ||
| 6648 | // hack to get zig fmt to render a comma in builtin calls | ||
| 6649 | _ = try appendToken(c, .Comma, ","); | ||
| 6650 | |||
| 6651 | // last token of `node` | 4826 | // last token of `node` |
| 6652 | const prev_id = m.list[m.i - 1].id; | 4827 | const prev_id = m.list[m.i - 1].id; |
| 6653 | 4828 | ||
| 6654 | if (prev_id == .Keyword_void) { | 4829 | if (prev_id == .Keyword_void) { |
| 6655 | const ptr = try transCreateNodePtrType(c, false, false, .Asterisk); | 4830 | const ptr = try Tag.single_pointer.create(c.arena, .{ |
| 6656 | ptr.rhs = node; | 4831 | .is_const = false, |
| 6657 | const optional_node = try transCreateNodeSimplePrefixOp(c, .OptionalType, .QuestionMark, "?"); | 4832 | .is_volatile = false, |
| 6658 | optional_node.rhs = &ptr.base; | 4833 | .elem_type = node, |
| 6659 | return &optional_node.base; | 4834 | }); |
| 4835 | return Tag.optional_type.create(c.arena, ptr); | ||
| 6660 | } else { | 4836 | } else { |
| 6661 | const ptr = try transCreateNodePtrType(c, false, false, Token.Id.Identifier); | 4837 | return Tag.c_pointer.create(c.arena, .{ |
| 6662 | ptr.rhs = node; | 4838 | .is_const = false, |
| 6663 | return &ptr.base; | 4839 | .is_volatile = false, |
| 4840 | .elem_type = node, | ||
| 4841 | }); | ||
| 6664 | } | 4842 | } |
| 6665 | } else { | 4843 | } else { |
| 6666 | // expr * expr | 4844 | // expr * expr |
| 6667 | op_token = try appendToken(c, .Asterisk, "*"); | 4845 | const lhs = try macroBoolToInt(c, node); |
| 6668 | op_id = .BitShiftLeft; | 4846 | const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); |
| 4847 | node = try Tag.mul.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); | ||
| 6669 | } | 4848 | } |
| 6670 | }, | 4849 | }, |
| 6671 | .Slash => { | 4850 | .Slash => { |
| 6672 | op_id = .Div; | 4851 | const lhs = try macroBoolToInt(c, node); |
| 6673 | op_token = try appendToken(c, .Slash, "/"); | 4852 | const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); |
| 4853 | node = try Tag.div.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); | ||
| 6674 | }, | 4854 | }, |
| 6675 | .Percent => { | 4855 | .Percent => { |
| 6676 | op_id = .Mod; | 4856 | const lhs = try macroBoolToInt(c, node); |
| 6677 | op_token = try appendToken(c, .Percent, "%"); | 4857 | const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); |
| 4858 | node = try Tag.mod.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); | ||
| 6678 | }, | 4859 | }, |
| 6679 | else => { | 4860 | else => { |
| 6680 | m.i -= 1; | 4861 | m.i -= 1; |
| 6681 | return node; | 4862 | return node; |
| 6682 | }, | 4863 | }, |
| 6683 | } | 4864 | } |
| 6684 | const lhs_node = try macroBoolToInt(c, node); | ||
| 6685 | const rhs_node = try parseCUnaryExpr(c, m, scope); | ||
| 6686 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6687 | op_node.* = .{ | ||
| 6688 | .base = .{ .tag = op_id }, | ||
| 6689 | .op_token = op_token, | ||
| 6690 | .lhs = lhs_node, | ||
| 6691 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6692 | }; | ||
| 6693 | node = &op_node.base; | ||
| 6694 | } | 4865 | } |
| 6695 | } | 4866 | } |
| 6696 | 4867 | ||
| 6697 | fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 4868 | fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 6698 | var node = try parseCPrimaryExpr(c, m, scope); | 4869 | var node = try parseCPrimaryExpr(c, m, scope); |
| 6699 | while (true) { | 4870 | while (true) { |
| 6700 | switch (m.next().?) { | 4871 | switch (m.next().?) { |
| ... | @@ -6704,38 +4875,33 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N | ... | @@ -6704,38 +4875,33 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N |
| 6704 | return error.ParseError; | 4875 | return error.ParseError; |
| 6705 | } | 4876 | } |
| 6706 | 4877 | ||
| 6707 | node = try transCreateNodeFieldAccess(c, node, m.slice()); | 4878 | node = try Tag.field_access.create(c.arena, .{ .lhs = node, .field_name = m.slice() }); |
| 6708 | continue; | ||
| 6709 | }, | 4879 | }, |
| 6710 | .Arrow => { | 4880 | .Arrow => { |
| 6711 | if (m.next().? != .Identifier) { | 4881 | if (m.next().? != .Identifier) { |
| 6712 | try m.fail(c, "unable to translate C expr: expected identifier", .{}); | 4882 | try m.fail(c, "unable to translate C expr: expected identifier", .{}); |
| 6713 | return error.ParseError; | 4883 | return error.ParseError; |
| 6714 | } | 4884 | } |
| 6715 | const deref = try transCreateNodePtrDeref(c, node); | 4885 | |
| 6716 | node = try transCreateNodeFieldAccess(c, deref, m.slice()); | 4886 | const deref = try Tag.deref.create(c.arena, node); |
| 6717 | continue; | 4887 | node = try Tag.field_access.create(c.arena, .{ .lhs = deref, .field_name = m.slice() }); |
| 6718 | }, | 4888 | }, |
| 6719 | .LBracket => { | 4889 | .LBracket => { |
| 6720 | const arr_node = try transCreateNodeArrayAccess(c, node); | 4890 | const index = try macroBoolToInt(c, try parseCExpr(c, m, scope)); |
| 6721 | arr_node.index_expr = try parseCExpr(c, m, scope); | 4891 | node = try Tag.array_access.create(c.arena, .{ .lhs = node, .rhs = index }); |
| 6722 | arr_node.rtoken = try appendToken(c, .RBracket, "]"); | ||
| 6723 | node = &arr_node.base; | ||
| 6724 | if (m.next().? != .RBracket) { | 4892 | if (m.next().? != .RBracket) { |
| 6725 | try m.fail(c, "unable to translate C expr: expected ']'", .{}); | 4893 | try m.fail(c, "unable to translate C expr: expected ']'", .{}); |
| 6726 | return error.ParseError; | 4894 | return error.ParseError; |
| 6727 | } | 4895 | } |
| 6728 | continue; | ||
| 6729 | }, | 4896 | }, |
| 6730 | .LParen => { | 4897 | .LParen => { |
| 6731 | _ = try appendToken(c, .LParen, "("); | 4898 | var args = std.ArrayList(Node).init(c.gpa); |
| 6732 | var call_params = std.ArrayList(*ast.Node).init(c.gpa); | 4899 | defer args.deinit(); |
| 6733 | defer call_params.deinit(); | ||
| 6734 | while (true) { | 4900 | while (true) { |
| 6735 | const arg = try parseCCondExpr(c, m, scope); | 4901 | const arg = try parseCCondExpr(c, m, scope); |
| 6736 | try call_params.append(arg); | 4902 | try args.append(arg); |
| 6737 | switch (m.next().?) { | 4903 | switch (m.next().?) { |
| 6738 | .Comma => _ = try appendToken(c, .Comma, ","), | 4904 | .Comma => {}, |
| 6739 | .RParen => break, | 4905 | .RParen => break, |
| 6740 | else => { | 4906 | else => { |
| 6741 | try m.fail(c, "unable to translate C expr: expected ',' or ')'", .{}); | 4907 | try m.fail(c, "unable to translate C expr: expected ',' or ')'", .{}); |
| ... | @@ -6743,32 +4909,17 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N | ... | @@ -6743,32 +4909,17 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N |
| 6743 | }, | 4909 | }, |
| 6744 | } | 4910 | } |
| 6745 | } | 4911 | } |
| 6746 | const call_node = try ast.Node.Call.alloc(c.arena, call_params.items.len); | 4912 | node = try Tag.call.create(c.arena, .{ .lhs = node, .args = try c.arena.dupe(Node, args.items) }); |
| 6747 | call_node.* = .{ | ||
| 6748 | .lhs = node, | ||
| 6749 | .params_len = call_params.items.len, | ||
| 6750 | .async_token = null, | ||
| 6751 | .rtoken = try appendToken(c, .RParen, ")"), | ||
| 6752 | }; | ||
| 6753 | mem.copy(*ast.Node, call_node.params(), call_params.items); | ||
| 6754 | node = &call_node.base; | ||
| 6755 | continue; | ||
| 6756 | }, | 4913 | }, |
| 6757 | .LBrace => { | 4914 | .LBrace => { |
| 6758 | // must come immediately after `node` | 4915 | var init_vals = std.ArrayList(Node).init(c.gpa); |
| 6759 | _ = try appendToken(c, .Comma, ","); | ||
| 6760 | |||
| 6761 | const dot = try appendToken(c, .Period, "."); | ||
| 6762 | _ = try appendToken(c, .LBrace, "{"); | ||
| 6763 | |||
| 6764 | var init_vals = std.ArrayList(*ast.Node).init(c.gpa); | ||
| 6765 | defer init_vals.deinit(); | 4916 | defer init_vals.deinit(); |
| 6766 | 4917 | ||
| 6767 | while (true) { | 4918 | while (true) { |
| 6768 | const val = try parseCCondExpr(c, m, scope); | 4919 | const val = try parseCCondExpr(c, m, scope); |
| 6769 | try init_vals.append(val); | 4920 | try init_vals.append(val); |
| 6770 | switch (m.next().?) { | 4921 | switch (m.next().?) { |
| 6771 | .Comma => _ = try appendToken(c, .Comma, ","), | 4922 | .Comma => {}, |
| 6772 | .RBrace => break, | 4923 | .RBrace => break, |
| 6773 | else => { | 4924 | else => { |
| 6774 | try m.fail(c, "unable to translate C expr: expected ',' or '}}'", .{}); | 4925 | try m.fail(c, "unable to translate C expr: expected ',' or '}}'", .{}); |
| ... | @@ -6776,29 +4927,8 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N | ... | @@ -6776,29 +4927,8 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N |
| 6776 | }, | 4927 | }, |
| 6777 | } | 4928 | } |
| 6778 | } | 4929 | } |
| 6779 | const tuple_node = try ast.Node.StructInitializerDot.alloc(c.arena, init_vals.items.len); | 4930 | const tuple_node = try Tag.tuple.create(c.arena, try c.arena.dupe(Node, init_vals.items)); |
| 6780 | tuple_node.* = .{ | 4931 | node = try Tag.std_mem_zeroinit.create(c.arena, .{ .lhs = node, .rhs = tuple_node }); |
| 6781 | .dot = dot, | ||
| 6782 | .list_len = init_vals.items.len, | ||
| 6783 | .rtoken = try appendToken(c, .RBrace, "}"), | ||
| 6784 | }; | ||
| 6785 | mem.copy(*ast.Node, tuple_node.list(), init_vals.items); | ||
| 6786 | |||
| 6787 | //(@import("std").mem.zeroInit(T, .{x})) | ||
| 6788 | const import_fn_call = try c.createBuiltinCall("@import", 1); | ||
| 6789 | const std_node = try transCreateNodeStringLiteral(c, "\"std\""); | ||
| 6790 | import_fn_call.params()[0] = std_node; | ||
| 6791 | import_fn_call.rparen_token = try appendToken(c, .RParen, ")"); | ||
| 6792 | const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem"); | ||
| 6793 | const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroInit"); | ||
| 6794 | |||
| 6795 | const zero_init_call = try c.createCall(outer_field_access, 2); | ||
| 6796 | zero_init_call.params()[0] = node; | ||
| 6797 | zero_init_call.params()[1] = &tuple_node.base; | ||
| 6798 | zero_init_call.rtoken = try appendToken(c, .RParen, ")"); | ||
| 6799 | |||
| 6800 | node = &zero_init_call.base; | ||
| 6801 | continue; | ||
| 6802 | }, | 4932 | }, |
| 6803 | .PlusPlus, .MinusMinus => { | 4933 | .PlusPlus, .MinusMinus => { |
| 6804 | try m.fail(c, "TODO postfix inc/dec expr", .{}); | 4934 | try m.fail(c, "TODO postfix inc/dec expr", .{}); |
| ... | @@ -6812,35 +4942,31 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N | ... | @@ -6812,35 +4942,31 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N |
| 6812 | } | 4942 | } |
| 6813 | } | 4943 | } |
| 6814 | 4944 | ||
| 6815 | fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 4945 | fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 6816 | switch (m.next().?) { | 4946 | switch (m.next().?) { |
| 6817 | .Bang => { | 4947 | .Bang => { |
| 6818 | const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!"); | 4948 | const operand = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope)); |
| 6819 | node.rhs = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope)); | 4949 | return Tag.not.create(c.arena, operand); |
| 6820 | return &node.base; | ||
| 6821 | }, | 4950 | }, |
| 6822 | .Minus => { | 4951 | .Minus => { |
| 6823 | const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-"); | 4952 | const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); |
| 6824 | node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); | 4953 | return Tag.negate.create(c.arena, operand); |
| 6825 | return &node.base; | ||
| 6826 | }, | 4954 | }, |
| 6827 | .Plus => return try parseCUnaryExpr(c, m, scope), | 4955 | .Plus => return try parseCUnaryExpr(c, m, scope), |
| 6828 | .Tilde => { | 4956 | .Tilde => { |
| 6829 | const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~"); | 4957 | const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); |
| 6830 | node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); | 4958 | return Tag.bit_not.create(c.arena, operand); |
| 6831 | return &node.base; | ||
| 6832 | }, | 4959 | }, |
| 6833 | .Asterisk => { | 4960 | .Asterisk => { |
| 6834 | const node = try macroGroup(c, try parseCUnaryExpr(c, m, scope)); | 4961 | const operand = try parseCUnaryExpr(c, m, scope); |
| 6835 | return try transCreateNodePtrDeref(c, node); | 4962 | return Tag.deref.create(c.arena, operand); |
| 6836 | }, | 4963 | }, |
| 6837 | .Ampersand => { | 4964 | .Ampersand => { |
| 6838 | const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&"); | 4965 | const operand = try parseCUnaryExpr(c, m, scope); |
| 6839 | node.rhs = try macroGroup(c, try parseCUnaryExpr(c, m, scope)); | 4966 | return Tag.address_of.create(c.arena, operand); |
| 6840 | return &node.base; | ||
| 6841 | }, | 4967 | }, |
| 6842 | .Keyword_sizeof => { | 4968 | .Keyword_sizeof => { |
| 6843 | const inner = if (m.peek().? == .LParen) blk: { | 4969 | const operand = if (m.peek().? == .LParen) blk: { |
| 6844 | _ = m.next(); | 4970 | _ = m.next(); |
| 6845 | // C grammar says this should be 'type-name' but we have to | 4971 | // C grammar says this should be 'type-name' but we have to |
| 6846 | // use parseCMulExpr to correctly handle pointer types. | 4972 | // use parseCMulExpr to correctly handle pointer types. |
| ... | @@ -6852,18 +4978,7 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod | ... | @@ -6852,18 +4978,7 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod |
| 6852 | break :blk inner; | 4978 | break :blk inner; |
| 6853 | } else try parseCUnaryExpr(c, m, scope); | 4979 | } else try parseCUnaryExpr(c, m, scope); |
| 6854 | 4980 | ||
| 6855 | //(@import("std").meta.sizeof(dest, x)) | 4981 | return Tag.std_meta_sizeof.create(c.arena, operand); |
| 6856 | const import_fn_call = try c.createBuiltinCall("@import", 1); | ||
| 6857 | const std_node = try transCreateNodeStringLiteral(c, "\"std\""); | ||
| 6858 | import_fn_call.params()[0] = std_node; | ||
| 6859 | import_fn_call.rparen_token = try appendToken(c, .RParen, ")"); | ||
| 6860 | const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta"); | ||
| 6861 | const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "sizeof"); | ||
| 6862 | |||
| 6863 | const sizeof_call = try c.createCall(outer_field_access, 1); | ||
| 6864 | sizeof_call.params()[0] = inner; | ||
| 6865 | sizeof_call.rtoken = try appendToken(c, .RParen, ")"); | ||
| 6866 | return &sizeof_call.base; | ||
| 6867 | }, | 4982 | }, |
| 6868 | .Keyword_alignof => { | 4983 | .Keyword_alignof => { |
| 6869 | // TODO this won't work if using <stdalign.h>'s | 4984 | // TODO this won't work if using <stdalign.h>'s |
| ... | @@ -6874,16 +4989,13 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod | ... | @@ -6874,16 +4989,13 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod |
| 6874 | } | 4989 | } |
| 6875 | // C grammar says this should be 'type-name' but we have to | 4990 | // C grammar says this should be 'type-name' but we have to |
| 6876 | // use parseCMulExpr to correctly handle pointer types. | 4991 | // use parseCMulExpr to correctly handle pointer types. |
| 6877 | const inner = try parseCMulExpr(c, m, scope); | 4992 | const operand = try parseCMulExpr(c, m, scope); |
| 6878 | if (m.next().? != .RParen) { | 4993 | if (m.next().? != .RParen) { |
| 6879 | try m.fail(c, "unable to translate C expr: expected ')'", .{}); | 4994 | try m.fail(c, "unable to translate C expr: expected ')'", .{}); |
| 6880 | return error.ParseError; | 4995 | return error.ParseError; |
| 6881 | } | 4996 | } |
| 6882 | 4997 | ||
| 6883 | const builtin_call = try c.createBuiltinCall("@alignOf", 1); | 4998 | return Tag.alignof.create(c.arena, operand); |
| 6884 | builtin_call.params()[0] = inner; | ||
| 6885 | builtin_call.rparen_token = try appendToken(c, .RParen, ")"); | ||
| 6886 | return &builtin_call.base; | ||
| 6887 | }, | 4999 | }, |
| 6888 | .PlusPlus, .MinusMinus => { | 5000 | .PlusPlus, .MinusMinus => { |
| 6889 | try m.fail(c, "TODO unary inc/dec expr", .{}); | 5001 | try m.fail(c, "TODO unary inc/dec expr", .{}); |
| ... | @@ -6896,51 +5008,40 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod | ... | @@ -6896,51 +5008,40 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod |
| 6896 | } | 5008 | } |
| 6897 | } | 5009 | } |
| 6898 | 5010 | ||
| 6899 | fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 { | 5011 | fn getContainer(c: *Context, node: Node) ?Node { |
| 6900 | const tok = c.token_locs.items[token]; | 5012 | switch (node.tag()) { |
| 6901 | const slice = c.source_buffer.items[tok.start..tok.end]; | 5013 | .@"union", |
| 6902 | return if (mem.startsWith(u8, slice, "@\"")) | 5014 | .@"struct", |
| 6903 | slice[2 .. slice.len - 1] | 5015 | .@"enum", |
| 6904 | else | 5016 | .address_of, |
| 6905 | slice; | 5017 | .bit_not, |
| 6906 | } | 5018 | .not, |
| 6907 | 5019 | .optional_type, | |
| 6908 | fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { | 5020 | .negate, |
| 6909 | switch (node.tag) { | 5021 | .negate_wrap, |
| 6910 | .ContainerDecl, | 5022 | .array_type, |
| 6911 | .AddressOf, | 5023 | .c_pointer, |
| 6912 | .Await, | 5024 | .single_pointer, |
| 6913 | .BitNot, | ||
| 6914 | .BoolNot, | ||
| 6915 | .OptionalType, | ||
| 6916 | .Negation, | ||
| 6917 | .NegationWrap, | ||
| 6918 | .Resume, | ||
| 6919 | .Try, | ||
| 6920 | .ArrayType, | ||
| 6921 | .ArrayTypeSentinel, | ||
| 6922 | .PtrType, | ||
| 6923 | .SliceType, | ||
| 6924 | => return node, | 5025 | => return node, |
| 6925 | 5026 | ||
| 6926 | .Identifier => { | 5027 | .identifier => { |
| 6927 | const ident = node.castTag(.Identifier).?; | 5028 | const ident = node.castTag(.identifier).?; |
| 6928 | if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| { | 5029 | if (c.global_scope.sym_table.get(ident.data)) |value| { |
| 6929 | if (value.cast(ast.Node.VarDecl)) |var_decl| | 5030 | if (value.castTag(.var_decl)) |var_decl| |
| 6930 | return getContainer(c, var_decl.getInitNode().?); | 5031 | return getContainer(c, var_decl.data.init.?); |
| 5032 | if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |var_decl| | ||
| 5033 | return getContainer(c, var_decl.data.init); | ||
| 6931 | } | 5034 | } |
| 6932 | }, | 5035 | }, |
| 6933 | 5036 | ||
| 6934 | .Period => { | 5037 | .field_access => { |
| 6935 | const infix = node.castTag(.Period).?; | 5038 | const field_access = node.castTag(.field_access).?; |
| 6936 | 5039 | ||
| 6937 | if (getContainerTypeOf(c, infix.lhs)) |ty_node| { | 5040 | if (getContainerTypeOf(c, field_access.data.lhs)) |ty_node| { |
| 6938 | if (ty_node.cast(ast.Node.ContainerDecl)) |container| { | 5041 | if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| { |
| 6939 | for (container.fieldsAndDecls()) |field_ref| { | 5042 | for (container.data.fields) |field| { |
| 6940 | const field = field_ref.cast(ast.Node.ContainerField).?; | 5043 | if (mem.eql(u8, field.name, field_access.data.field_name)) { |
| 6941 | const ident = infix.rhs.castTag(.Identifier).?; | 5044 | return getContainer(c, field.type); |
| 6942 | if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) { | ||
| 6943 | return getContainer(c, field.type_expr.?); | ||
| 6944 | } | 5045 | } |
| 6945 | } | 5046 | } |
| 6946 | } | 5047 | } |
| ... | @@ -6952,22 +5053,19 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { | ... | @@ -6952,22 +5053,19 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { |
| 6952 | return null; | 5053 | return null; |
| 6953 | } | 5054 | } |
| 6954 | 5055 | ||
| 6955 | fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node { | 5056 | fn getContainerTypeOf(c: *Context, ref: Node) ?Node { |
| 6956 | if (ref.castTag(.Identifier)) |ident| { | 5057 | if (ref.castTag(.identifier)) |ident| { |
| 6957 | if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| { | 5058 | if (c.global_scope.sym_table.get(ident.data)) |value| { |
| 6958 | if (value.cast(ast.Node.VarDecl)) |var_decl| { | 5059 | if (value.castTag(.var_decl)) |var_decl| { |
| 6959 | if (var_decl.getTypeNode()) |ty| | 5060 | return getContainer(c, var_decl.data.type); |
| 6960 | return getContainer(c, ty); | ||
| 6961 | } | 5061 | } |
| 6962 | } | 5062 | } |
| 6963 | } else if (ref.castTag(.Period)) |infix| { | 5063 | } else if (ref.castTag(.field_access)) |field_access| { |
| 6964 | if (getContainerTypeOf(c, infix.lhs)) |ty_node| { | 5064 | if (getContainerTypeOf(c, field_access.data.lhs)) |ty_node| { |
| 6965 | if (ty_node.cast(ast.Node.ContainerDecl)) |container| { | 5065 | if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| { |
| 6966 | for (container.fieldsAndDecls()) |field_ref| { | 5066 | for (container.data.fields) |field| { |
| 6967 | const field = field_ref.cast(ast.Node.ContainerField).?; | 5067 | if (mem.eql(u8, field.name, field_access.data.field_name)) { |
| 6968 | const ident = infix.rhs.castTag(.Identifier).?; | 5068 | return getContainer(c, field.type); |
| 6969 | if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) { | ||
| 6970 | return getContainer(c, field.type_expr.?); | ||
| 6971 | } | 5069 | } |
| 6972 | } | 5070 | } |
| 6973 | } else | 5071 | } else |
| ... | @@ -6977,11 +5075,16 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node { | ... | @@ -6977,11 +5075,16 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node { |
| 6977 | return null; | 5075 | return null; |
| 6978 | } | 5076 | } |
| 6979 | 5077 | ||
| 6980 | fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto { | 5078 | fn getFnProto(c: *Context, ref: Node) ?*ast.Payload.Func { |
| 6981 | const init = if (ref.cast(ast.Node.VarDecl)) |v| v.getInitNode().? else return null; | 5079 | const init = if (ref.castTag(.var_decl)) |v| |
| 5080 | v.data.init orelse return null | ||
| 5081 | else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v| | ||
| 5082 | v.data.init | ||
| 5083 | else | ||
| 5084 | return null; | ||
| 6982 | if (getContainerTypeOf(c, init)) |ty_node| { | 5085 | if (getContainerTypeOf(c, init)) |ty_node| { |
| 6983 | if (ty_node.castTag(.OptionalType)) |prefix| { | 5086 | if (ty_node.castTag(.optional_type)) |prefix| { |
| 6984 | if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| { | 5087 | if (prefix.data.castTag(.func)) |fn_proto| { |
| 6985 | return fn_proto; | 5088 | return fn_proto; |
| 6986 | } | 5089 | } |
| 6987 | } | 5090 | } |
src/translate_c/ast.zig created+2529| ... | @@ -0,0 +1,2529 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("std"); | ||
| 7 | const Type = @import("../type.zig").Type; | ||
| 8 | const Allocator = std.mem.Allocator; | ||
| 9 | |||
| 10 | pub const Node = extern union { | ||
| 11 | /// If the tag value is less than Tag.no_payload_count, then no pointer | ||
| 12 | /// dereference is needed. | ||
| 13 | tag_if_small_enough: usize, | ||
| 14 | ptr_otherwise: *Payload, | ||
| 15 | |||
| 16 | pub const Tag = enum { | ||
| 17 | /// Declarations add themselves to the correct scopes and should not be emitted as this tag. | ||
| 18 | declaration, | ||
| 19 | null_literal, | ||
| 20 | undefined_literal, | ||
| 21 | /// opaque {} | ||
| 22 | opaque_literal, | ||
| 23 | true_literal, | ||
| 24 | false_literal, | ||
| 25 | empty_block, | ||
| 26 | return_void, | ||
| 27 | zero_literal, | ||
| 28 | one_literal, | ||
| 29 | void_type, | ||
| 30 | noreturn_type, | ||
| 31 | @"anytype", | ||
| 32 | @"continue", | ||
| 33 | @"break", | ||
| 34 | /// pub usingnamespace @import("std").c.builtins; | ||
| 35 | usingnamespace_builtins, | ||
| 36 | // After this, the tag requires a payload. | ||
| 37 | |||
| 38 | integer_literal, | ||
| 39 | float_literal, | ||
| 40 | string_literal, | ||
| 41 | char_literal, | ||
| 42 | identifier, | ||
| 43 | @"if", | ||
| 44 | /// if (!operand) break; | ||
| 45 | if_not_break, | ||
| 46 | @"while", | ||
| 47 | /// while (true) operand | ||
| 48 | while_true, | ||
| 49 | @"switch", | ||
| 50 | /// else => operand, | ||
| 51 | switch_else, | ||
| 52 | /// items => body, | ||
| 53 | switch_prong, | ||
| 54 | break_val, | ||
| 55 | @"return", | ||
| 56 | field_access, | ||
| 57 | array_access, | ||
| 58 | call, | ||
| 59 | var_decl, | ||
| 60 | func, | ||
| 61 | warning, | ||
| 62 | /// All enums are non-exhaustive | ||
| 63 | @"enum", | ||
| 64 | @"struct", | ||
| 65 | @"union", | ||
| 66 | array_init, | ||
| 67 | tuple, | ||
| 68 | container_init, | ||
| 69 | std_meta_cast, | ||
| 70 | /// _ = operand; | ||
| 71 | discard, | ||
| 72 | |||
| 73 | // a + b | ||
| 74 | add, | ||
| 75 | // a = b | ||
| 76 | add_assign, | ||
| 77 | // c = (a = b) | ||
| 78 | add_wrap, | ||
| 79 | add_wrap_assign, | ||
| 80 | sub, | ||
| 81 | sub_assign, | ||
| 82 | sub_wrap, | ||
| 83 | sub_wrap_assign, | ||
| 84 | mul, | ||
| 85 | mul_assign, | ||
| 86 | mul_wrap, | ||
| 87 | mul_wrap_assign, | ||
| 88 | div, | ||
| 89 | div_assign, | ||
| 90 | shl, | ||
| 91 | shl_assign, | ||
| 92 | shr, | ||
| 93 | shr_assign, | ||
| 94 | mod, | ||
| 95 | mod_assign, | ||
| 96 | @"and", | ||
| 97 | @"or", | ||
| 98 | less_than, | ||
| 99 | less_than_equal, | ||
| 100 | greater_than, | ||
| 101 | greater_than_equal, | ||
| 102 | equal, | ||
| 103 | not_equal, | ||
| 104 | bit_and, | ||
| 105 | bit_and_assign, | ||
| 106 | bit_or, | ||
| 107 | bit_or_assign, | ||
| 108 | bit_xor, | ||
| 109 | bit_xor_assign, | ||
| 110 | array_cat, | ||
| 111 | ellipsis3, | ||
| 112 | assign, | ||
| 113 | |||
| 114 | log2_int_type, | ||
| 115 | /// @import("std").math.Log2Int(operand) | ||
| 116 | std_math_Log2Int, | ||
| 117 | /// @intCast(lhs, rhs) | ||
| 118 | int_cast, | ||
| 119 | /// @rem(lhs, rhs) | ||
| 120 | rem, | ||
| 121 | /// @divTrunc(lhs, rhs) | ||
| 122 | div_trunc, | ||
| 123 | /// @boolToInt(operand) | ||
| 124 | bool_to_int, | ||
| 125 | /// @as(lhs, rhs) | ||
| 126 | as, | ||
| 127 | /// @truncate(lhs, rhs) | ||
| 128 | truncate, | ||
| 129 | /// @bitCast(lhs, rhs) | ||
| 130 | bit_cast, | ||
| 131 | /// @floatCast(lhs, rhs) | ||
| 132 | float_cast, | ||
| 133 | /// @floatToInt(lhs, rhs) | ||
| 134 | float_to_int, | ||
| 135 | /// @intToFloat(lhs, rhs) | ||
| 136 | int_to_float, | ||
| 137 | /// @intToEnum(lhs, rhs) | ||
| 138 | int_to_enum, | ||
| 139 | /// @enumToInt(operand) | ||
| 140 | enum_to_int, | ||
| 141 | /// @intToPtr(lhs, rhs) | ||
| 142 | int_to_ptr, | ||
| 143 | /// @ptrToInt(operand) | ||
| 144 | ptr_to_int, | ||
| 145 | /// @alignCast(lhs, rhs) | ||
| 146 | align_cast, | ||
| 147 | /// @ptrCast(lhs, rhs) | ||
| 148 | ptr_cast, | ||
| 149 | |||
| 150 | negate, | ||
| 151 | negate_wrap, | ||
| 152 | bit_not, | ||
| 153 | not, | ||
| 154 | address_of, | ||
| 155 | /// .? | ||
| 156 | unwrap, | ||
| 157 | /// .* | ||
| 158 | deref, | ||
| 159 | |||
| 160 | block, | ||
| 161 | /// { operand } | ||
| 162 | block_single, | ||
| 163 | |||
| 164 | sizeof, | ||
| 165 | alignof, | ||
| 166 | typeof, | ||
| 167 | type, | ||
| 168 | |||
| 169 | optional_type, | ||
| 170 | c_pointer, | ||
| 171 | single_pointer, | ||
| 172 | array_type, | ||
| 173 | |||
| 174 | /// @import("std").meta.sizeof(operand) | ||
| 175 | std_meta_sizeof, | ||
| 176 | /// @import("std").mem.zeroes(operand) | ||
| 177 | std_mem_zeroes, | ||
| 178 | /// @import("std").mem.zeroInit(lhs, rhs) | ||
| 179 | std_mem_zeroinit, | ||
| 180 | // pub const name = @compileError(msg); | ||
| 181 | fail_decl, | ||
| 182 | // var actual = mangled; | ||
| 183 | arg_redecl, | ||
| 184 | /// pub const alias = actual; | ||
| 185 | alias, | ||
| 186 | /// const name = init; | ||
| 187 | var_simple, | ||
| 188 | /// pub const name = init; | ||
| 189 | pub_var_simple, | ||
| 190 | /// pub const enum_field_name = @enumToInt(enum_name.field_name); | ||
| 191 | pub_enum_redecl, | ||
| 192 | enum_redecl, | ||
| 193 | |||
| 194 | /// pub inline fn name(params) return_type body | ||
| 195 | pub_inline_fn, | ||
| 196 | |||
| 197 | /// [0]type{} | ||
| 198 | empty_array, | ||
| 199 | /// [1]type{val} ** count | ||
| 200 | array_filler, | ||
| 201 | |||
| 202 | pub const last_no_payload_tag = Tag.usingnamespace_builtins; | ||
| 203 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; | ||
| 204 | |||
| 205 | pub fn Type(comptime t: Tag) type { | ||
| 206 | return switch (t) { | ||
| 207 | .declaration, | ||
| 208 | .null_literal, | ||
| 209 | .undefined_literal, | ||
| 210 | .opaque_literal, | ||
| 211 | .true_literal, | ||
| 212 | .false_literal, | ||
| 213 | .empty_block, | ||
| 214 | .usingnamespace_builtins, | ||
| 215 | .return_void, | ||
| 216 | .zero_literal, | ||
| 217 | .one_literal, | ||
| 218 | .void_type, | ||
| 219 | .noreturn_type, | ||
| 220 | .@"anytype", | ||
| 221 | .@"continue", | ||
| 222 | .@"break", | ||
| 223 | => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), | ||
| 224 | |||
| 225 | .std_mem_zeroes, | ||
| 226 | .@"return", | ||
| 227 | .discard, | ||
| 228 | .std_math_Log2Int, | ||
| 229 | .negate, | ||
| 230 | .negate_wrap, | ||
| 231 | .bit_not, | ||
| 232 | .not, | ||
| 233 | .optional_type, | ||
| 234 | .address_of, | ||
| 235 | .unwrap, | ||
| 236 | .deref, | ||
| 237 | .ptr_to_int, | ||
| 238 | .enum_to_int, | ||
| 239 | .empty_array, | ||
| 240 | .while_true, | ||
| 241 | .if_not_break, | ||
| 242 | .switch_else, | ||
| 243 | .block_single, | ||
| 244 | .std_meta_sizeof, | ||
| 245 | .bool_to_int, | ||
| 246 | .sizeof, | ||
| 247 | .alignof, | ||
| 248 | .typeof, | ||
| 249 | => Payload.UnOp, | ||
| 250 | |||
| 251 | .add, | ||
| 252 | .add_assign, | ||
| 253 | .add_wrap, | ||
| 254 | .add_wrap_assign, | ||
| 255 | .sub, | ||
| 256 | .sub_assign, | ||
| 257 | .sub_wrap, | ||
| 258 | .sub_wrap_assign, | ||
| 259 | .mul, | ||
| 260 | .mul_assign, | ||
| 261 | .mul_wrap, | ||
| 262 | .mul_wrap_assign, | ||
| 263 | .div, | ||
| 264 | .div_assign, | ||
| 265 | .shl, | ||
| 266 | .shl_assign, | ||
| 267 | .shr, | ||
| 268 | .shr_assign, | ||
| 269 | .mod, | ||
| 270 | .mod_assign, | ||
| 271 | .@"and", | ||
| 272 | .@"or", | ||
| 273 | .less_than, | ||
| 274 | .less_than_equal, | ||
| 275 | .greater_than, | ||
| 276 | .greater_than_equal, | ||
| 277 | .equal, | ||
| 278 | .not_equal, | ||
| 279 | .bit_and, | ||
| 280 | .bit_and_assign, | ||
| 281 | .bit_or, | ||
| 282 | .bit_or_assign, | ||
| 283 | .bit_xor, | ||
| 284 | .bit_xor_assign, | ||
| 285 | .div_trunc, | ||
| 286 | .rem, | ||
| 287 | .int_cast, | ||
| 288 | .as, | ||
| 289 | .truncate, | ||
| 290 | .bit_cast, | ||
| 291 | .float_cast, | ||
| 292 | .float_to_int, | ||
| 293 | .int_to_float, | ||
| 294 | .int_to_enum, | ||
| 295 | .int_to_ptr, | ||
| 296 | .array_cat, | ||
| 297 | .ellipsis3, | ||
| 298 | .assign, | ||
| 299 | .align_cast, | ||
| 300 | .array_access, | ||
| 301 | .std_mem_zeroinit, | ||
| 302 | .ptr_cast, | ||
| 303 | => Payload.BinOp, | ||
| 304 | |||
| 305 | .integer_literal, | ||
| 306 | .float_literal, | ||
| 307 | .string_literal, | ||
| 308 | .char_literal, | ||
| 309 | .identifier, | ||
| 310 | .warning, | ||
| 311 | .type, | ||
| 312 | => Payload.Value, | ||
| 313 | .@"if" => Payload.If, | ||
| 314 | .@"while" => Payload.While, | ||
| 315 | .@"switch", .array_init,.switch_prong => Payload.Switch, | ||
| 316 | .break_val => Payload.BreakVal, | ||
| 317 | .call => Payload.Call, | ||
| 318 | .var_decl => Payload.VarDecl, | ||
| 319 | .func => Payload.Func, | ||
| 320 | .@"enum" => Payload.Enum, | ||
| 321 | .@"struct", .@"union" => Payload.Record, | ||
| 322 | .tuple => Payload.TupleInit, | ||
| 323 | .container_init => Payload.ContainerInit, | ||
| 324 | .std_meta_cast => Payload.Infix, | ||
| 325 | .block => Payload.Block, | ||
| 326 | .c_pointer, .single_pointer => Payload.Pointer, | ||
| 327 | .array_type => Payload.Array, | ||
| 328 | .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl, | ||
| 329 | .log2_int_type => Payload.Log2IntType, | ||
| 330 | .var_simple, .pub_var_simple => Payload.SimpleVarDecl, | ||
| 331 | .pub_enum_redecl, .enum_redecl => Payload.EnumRedecl, | ||
| 332 | .array_filler => Payload.ArrayFiller, | ||
| 333 | .pub_inline_fn => Payload.PubInlineFn, | ||
| 334 | .field_access => Payload.FieldAccess, | ||
| 335 | }; | ||
| 336 | } | ||
| 337 | |||
| 338 | pub fn init(comptime t: Tag) Node { | ||
| 339 | comptime std.debug.assert(@enumToInt(t) < Tag.no_payload_count); | ||
| 340 | return .{ .tag_if_small_enough = @enumToInt(t) }; | ||
| 341 | } | ||
| 342 | |||
| 343 | pub fn create(comptime t: Tag, ally: *Allocator, data: Data(t)) error{OutOfMemory}!Node { | ||
| 344 | const ptr = try ally.create(t.Type()); | ||
| 345 | ptr.* = .{ | ||
| 346 | .base = .{ .tag = t }, | ||
| 347 | .data = data, | ||
| 348 | }; | ||
| 349 | return Node{ .ptr_otherwise = &ptr.base }; | ||
| 350 | } | ||
| 351 | |||
| 352 | pub fn Data(comptime t: Tag) type { | ||
| 353 | return std.meta.fieldInfo(t.Type(), .data).field_type; | ||
| 354 | } | ||
| 355 | }; | ||
| 356 | |||
| 357 | pub fn tag(self: Node) Tag { | ||
| 358 | if (self.tag_if_small_enough < Tag.no_payload_count) { | ||
| 359 | return @intToEnum(Tag, @intCast(std.meta.Tag(Tag), self.tag_if_small_enough)); | ||
| 360 | } else { | ||
| 361 | return self.ptr_otherwise.tag; | ||
| 362 | } | ||
| 363 | } | ||
| 364 | |||
| 365 | pub fn castTag(self: Node, comptime t: Tag) ?*t.Type() { | ||
| 366 | if (self.tag_if_small_enough < Tag.no_payload_count) | ||
| 367 | return null; | ||
| 368 | |||
| 369 | if (self.ptr_otherwise.tag == t) | ||
| 370 | return @fieldParentPtr(t.Type(), "base", self.ptr_otherwise); | ||
| 371 | |||
| 372 | return null; | ||
| 373 | } | ||
| 374 | |||
| 375 | pub fn initPayload(payload: *Payload) Node { | ||
| 376 | std.debug.assert(@enumToInt(payload.tag) >= Tag.no_payload_count); | ||
| 377 | return .{ .ptr_otherwise = payload }; | ||
| 378 | } | ||
| 379 | |||
| 380 | pub fn isNoreturn(node: Node, break_counts: bool) bool { | ||
| 381 | switch (node.tag()) { | ||
| 382 | .block => { | ||
| 383 | const block_node = node.castTag(.block).?; | ||
| 384 | if (block_node.data.stmts.len == 0) return false; | ||
| 385 | |||
| 386 | const last = block_node.data.stmts[block_node.data.stmts.len - 1]; | ||
| 387 | return last.isNoreturn(break_counts); | ||
| 388 | }, | ||
| 389 | .@"switch" => { | ||
| 390 | const switch_node = node.castTag(.@"switch").?; | ||
| 391 | |||
| 392 | for (switch_node.data.cases) |case| { | ||
| 393 | const body = if (case.castTag(.switch_else)) |some| | ||
| 394 | some.data | ||
| 395 | else if (case.castTag(.switch_prong)) |some| | ||
| 396 | some.data.cond | ||
| 397 | else unreachable; | ||
| 398 | |||
| 399 | if (!body.isNoreturn(break_counts)) return false; | ||
| 400 | } | ||
| 401 | return true; | ||
| 402 | }, | ||
| 403 | .@"return", .return_void => return true, | ||
| 404 | .@"break" => if (break_counts) return true, | ||
| 405 | else => {}, | ||
| 406 | } | ||
| 407 | return false; | ||
| 408 | } | ||
| 409 | |||
| 410 | }; | ||
| 411 | |||
| 412 | pub const Payload = struct { | ||
| 413 | tag: Node.Tag, | ||
| 414 | |||
| 415 | pub const Infix = struct { | ||
| 416 | base: Payload, | ||
| 417 | data: struct { | ||
| 418 | lhs: Node, | ||
| 419 | rhs: Node, | ||
| 420 | }, | ||
| 421 | }; | ||
| 422 | |||
| 423 | pub const Value = struct { | ||
| 424 | base: Payload, | ||
| 425 | data: []const u8, | ||
| 426 | }; | ||
| 427 | |||
| 428 | pub const UnOp = struct { | ||
| 429 | base: Payload, | ||
| 430 | data: Node, | ||
| 431 | }; | ||
| 432 | |||
| 433 | pub const BinOp = struct { | ||
| 434 | base: Payload, | ||
| 435 | data: struct { | ||
| 436 | lhs: Node, | ||
| 437 | rhs: Node, | ||
| 438 | }, | ||
| 439 | }; | ||
| 440 | |||
| 441 | pub const If = struct { | ||
| 442 | base: Payload, | ||
| 443 | data: struct { | ||
| 444 | cond: Node, | ||
| 445 | then: Node, | ||
| 446 | @"else": ?Node, | ||
| 447 | }, | ||
| 448 | }; | ||
| 449 | |||
| 450 | pub const While = struct { | ||
| 451 | base: Payload, | ||
| 452 | data: struct { | ||
| 453 | cond: Node, | ||
| 454 | body: Node, | ||
| 455 | cont_expr: ?Node, | ||
| 456 | }, | ||
| 457 | }; | ||
| 458 | |||
| 459 | pub const Switch = struct { | ||
| 460 | base: Payload, | ||
| 461 | data: struct { | ||
| 462 | cond: Node, | ||
| 463 | cases: []Node, | ||
| 464 | }, | ||
| 465 | }; | ||
| 466 | |||
| 467 | pub const BreakVal = struct { | ||
| 468 | base: Payload, | ||
| 469 | data: struct { | ||
| 470 | label: ?[]const u8, | ||
| 471 | val: Node, | ||
| 472 | }, | ||
| 473 | }; | ||
| 474 | |||
| 475 | pub const Call = struct { | ||
| 476 | base: Payload, | ||
| 477 | data: struct { | ||
| 478 | lhs: Node, | ||
| 479 | args: []Node, | ||
| 480 | }, | ||
| 481 | }; | ||
| 482 | |||
| 483 | pub const VarDecl = struct { | ||
| 484 | base: Payload, | ||
| 485 | data: struct { | ||
| 486 | is_pub: bool, | ||
| 487 | is_const: bool, | ||
| 488 | is_extern: bool, | ||
| 489 | is_export: bool, | ||
| 490 | is_threadlocal: bool, | ||
| 491 | alignment: ?c_uint, | ||
| 492 | linksection_string: ?[]const u8, | ||
| 493 | name: []const u8, | ||
| 494 | type: Node, | ||
| 495 | init: ?Node, | ||
| 496 | }, | ||
| 497 | }; | ||
| 498 | |||
| 499 | pub const Func = struct { | ||
| 500 | base: Payload, | ||
| 501 | data: struct { | ||
| 502 | is_pub: bool, | ||
| 503 | is_extern: bool, | ||
| 504 | is_export: bool, | ||
| 505 | is_var_args: bool, | ||
| 506 | name: ?[]const u8, | ||
| 507 | linksection_string: ?[]const u8, | ||
| 508 | explicit_callconv: ?std.builtin.CallingConvention, | ||
| 509 | params: []Param, | ||
| 510 | return_type: Node, | ||
| 511 | body: ?Node, | ||
| 512 | alignment: ?c_uint, | ||
| 513 | }, | ||
| 514 | }; | ||
| 515 | |||
| 516 | pub const Param = struct { | ||
| 517 | is_noalias: bool, | ||
| 518 | name: ?[]const u8, | ||
| 519 | type: Node, | ||
| 520 | }; | ||
| 521 | |||
| 522 | pub const Enum = struct { | ||
| 523 | base: Payload, | ||
| 524 | data: struct { | ||
| 525 | int_type: Node, | ||
| 526 | fields: []Field, | ||
| 527 | }, | ||
| 528 | |||
| 529 | pub const Field = struct { | ||
| 530 | name: []const u8, | ||
| 531 | value: ?Node, | ||
| 532 | }; | ||
| 533 | }; | ||
| 534 | |||
| 535 | pub const Record = struct { | ||
| 536 | base: Payload, | ||
| 537 | data: struct { | ||
| 538 | is_packed: bool, | ||
| 539 | fields: []Field, | ||
| 540 | }, | ||
| 541 | |||
| 542 | pub const Field = struct { | ||
| 543 | name: []const u8, | ||
| 544 | type: Node, | ||
| 545 | alignment: ?c_uint, | ||
| 546 | }; | ||
| 547 | }; | ||
| 548 | |||
| 549 | pub const TupleInit = struct { | ||
| 550 | base: Payload, | ||
| 551 | data: []Node, | ||
| 552 | }; | ||
| 553 | |||
| 554 | pub const ContainerInit = struct { | ||
| 555 | base: Payload, | ||
| 556 | data: struct { | ||
| 557 | lhs: Node, | ||
| 558 | inits: []Initializer, | ||
| 559 | }, | ||
| 560 | |||
| 561 | pub const Initializer = struct { | ||
| 562 | name: []const u8, | ||
| 563 | value: Node, | ||
| 564 | }; | ||
| 565 | }; | ||
| 566 | |||
| 567 | pub const Block = struct { | ||
| 568 | base: Payload, | ||
| 569 | data: struct { | ||
| 570 | label: ?[]const u8, | ||
| 571 | stmts: []Node, | ||
| 572 | }, | ||
| 573 | }; | ||
| 574 | |||
| 575 | pub const Array = struct { | ||
| 576 | base: Payload, | ||
| 577 | data: struct { | ||
| 578 | elem_type: Node, | ||
| 579 | len: usize, | ||
| 580 | }, | ||
| 581 | }; | ||
| 582 | |||
| 583 | pub const Pointer = struct { | ||
| 584 | base: Payload, | ||
| 585 | data: struct { | ||
| 586 | elem_type: Node, | ||
| 587 | is_const: bool, | ||
| 588 | is_volatile: bool, | ||
| 589 | }, | ||
| 590 | }; | ||
| 591 | |||
| 592 | pub const ArgRedecl = struct { | ||
| 593 | base: Payload, | ||
| 594 | data: struct { | ||
| 595 | actual: []const u8, | ||
| 596 | mangled: []const u8, | ||
| 597 | }, | ||
| 598 | }; | ||
| 599 | |||
| 600 | pub const Log2IntType = struct { | ||
| 601 | base: Payload, | ||
| 602 | data: std.math.Log2Int(u64), | ||
| 603 | }; | ||
| 604 | |||
| 605 | pub const SimpleVarDecl = struct { | ||
| 606 | base: Payload, | ||
| 607 | data: struct { | ||
| 608 | name: []const u8, | ||
| 609 | init: Node, | ||
| 610 | }, | ||
| 611 | }; | ||
| 612 | |||
| 613 | pub const EnumRedecl = struct { | ||
| 614 | base: Payload, | ||
| 615 | data: struct { | ||
| 616 | enum_val_name: []const u8, | ||
| 617 | field_name: []const u8, | ||
| 618 | enum_name: []const u8, | ||
| 619 | }, | ||
| 620 | }; | ||
| 621 | |||
| 622 | pub const ArrayFiller = struct { | ||
| 623 | base: Payload, | ||
| 624 | data: struct { | ||
| 625 | type: Node, | ||
| 626 | filler: Node, | ||
| 627 | count: usize, | ||
| 628 | }, | ||
| 629 | }; | ||
| 630 | |||
| 631 | pub const PubInlineFn = struct { | ||
| 632 | base: Payload, | ||
| 633 | data: struct { | ||
| 634 | name: []const u8, | ||
| 635 | params: []Param, | ||
| 636 | return_type: Node, | ||
| 637 | body: Node, | ||
| 638 | }, | ||
| 639 | }; | ||
| 640 | |||
| 641 | pub const FieldAccess = struct { | ||
| 642 | base: Payload, | ||
| 643 | data: struct { | ||
| 644 | lhs: Node, | ||
| 645 | field_name: []const u8, | ||
| 646 | }, | ||
| 647 | }; | ||
| 648 | }; | ||
| 649 | |||
| 650 | /// Converts the nodes into a Zig ast. | ||
| 651 | /// Caller must free the source slice. | ||
| 652 | pub fn render(gpa: *Allocator, nodes: []const Node) !std.zig.ast.Tree { | ||
| 653 | var ctx = Context{ | ||
| 654 | .gpa = gpa, | ||
| 655 | .buf = std.ArrayList(u8).init(gpa), | ||
| 656 | }; | ||
| 657 | defer ctx.buf.deinit(); | ||
| 658 | defer ctx.nodes.deinit(gpa); | ||
| 659 | defer ctx.extra_data.deinit(gpa); | ||
| 660 | defer ctx.tokens.deinit(gpa); | ||
| 661 | |||
| 662 | // Estimate that each top level node has 10 child nodes. | ||
| 663 | const estimated_node_count = nodes.len * 10; | ||
| 664 | try ctx.nodes.ensureCapacity(gpa, estimated_node_count); | ||
| 665 | // Estimate that each each node has 2 tokens. | ||
| 666 | const estimated_tokens_count = estimated_node_count * 2; | ||
| 667 | try ctx.tokens.ensureCapacity(gpa, estimated_tokens_count); | ||
| 668 | // Estimate that each each token is 3 bytes long. | ||
| 669 | const estimated_buf_len = estimated_tokens_count * 3; | ||
| 670 | try ctx.buf.ensureCapacity(estimated_buf_len); | ||
| 671 | |||
| 672 | ctx.nodes.appendAssumeCapacity(.{ | ||
| 673 | .tag = .root, | ||
| 674 | .main_token = 0, | ||
| 675 | .data = .{ | ||
| 676 | .lhs = undefined, | ||
| 677 | .rhs = undefined, | ||
| 678 | }, | ||
| 679 | }); | ||
| 680 | |||
| 681 | const root_members = blk: { | ||
| 682 | var result = std.ArrayList(NodeIndex).init(gpa); | ||
| 683 | defer result.deinit(); | ||
| 684 | |||
| 685 | for (nodes) |node| { | ||
| 686 | const res = try renderNode(&ctx, node); | ||
| 687 | if (node.tag() == .warning) continue; | ||
| 688 | try result.append(res); | ||
| 689 | } | ||
| 690 | break :blk try ctx.listToSpan(result.items); | ||
| 691 | }; | ||
| 692 | |||
| 693 | ctx.nodes.items(.data)[0] = .{ | ||
| 694 | .lhs = root_members.start, | ||
| 695 | .rhs = root_members.end, | ||
| 696 | }; | ||
| 697 | |||
| 698 | try ctx.tokens.append(gpa, .{ | ||
| 699 | .tag = .eof, | ||
| 700 | .start = @intCast(u32, ctx.buf.items.len), | ||
| 701 | }); | ||
| 702 | |||
| 703 | return std.zig.ast.Tree{ | ||
| 704 | .source = ctx.buf.toOwnedSlice(), | ||
| 705 | .tokens = ctx.tokens.toOwnedSlice(), | ||
| 706 | .nodes = ctx.nodes.toOwnedSlice(), | ||
| 707 | .extra_data = ctx.extra_data.toOwnedSlice(gpa), | ||
| 708 | .errors = &.{}, | ||
| 709 | }; | ||
| 710 | } | ||
| 711 | |||
| 712 | const NodeIndex = std.zig.ast.Node.Index; | ||
| 713 | const NodeSubRange = std.zig.ast.Node.SubRange; | ||
| 714 | const TokenIndex = std.zig.ast.TokenIndex; | ||
| 715 | const TokenTag = std.zig.Token.Tag; | ||
| 716 | |||
| 717 | const Context = struct { | ||
| 718 | gpa: *Allocator, | ||
| 719 | buf: std.ArrayList(u8) = .{}, | ||
| 720 | nodes: std.zig.ast.NodeList = .{}, | ||
| 721 | extra_data: std.ArrayListUnmanaged(std.zig.ast.Node.Index) = .{}, | ||
| 722 | tokens: std.zig.ast.TokenList = .{}, | ||
| 723 | |||
| 724 | fn addTokenFmt(c: *Context, tag: TokenTag, comptime format: []const u8, args: anytype) Allocator.Error!TokenIndex { | ||
| 725 | const start_index = c.buf.items.len; | ||
| 726 | try c.buf.writer().print(format ++ " ", args); | ||
| 727 | |||
| 728 | try c.tokens.append(c.gpa, .{ | ||
| 729 | .tag = tag, | ||
| 730 | .start = @intCast(u32, start_index), | ||
| 731 | }); | ||
| 732 | |||
| 733 | return @intCast(u32, c.tokens.len - 1); | ||
| 734 | } | ||
| 735 | |||
| 736 | fn addToken(c: *Context, tag: TokenTag, bytes: []const u8) Allocator.Error!TokenIndex { | ||
| 737 | return addTokenFmt(c, tag, "{s}", .{bytes}); | ||
| 738 | } | ||
| 739 | |||
| 740 | fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex { | ||
| 741 | return addTokenFmt(c, .identifier, "{s}", .{std.zig.fmtId(bytes)}); | ||
| 742 | } | ||
| 743 | |||
| 744 | fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange { | ||
| 745 | try c.extra_data.appendSlice(c.gpa, list); | ||
| 746 | return NodeSubRange{ | ||
| 747 | .start = @intCast(NodeIndex, c.extra_data.items.len - list.len), | ||
| 748 | .end = @intCast(NodeIndex, c.extra_data.items.len), | ||
| 749 | }; | ||
| 750 | } | ||
| 751 | |||
| 752 | fn addNode(c: *Context, elem: std.zig.ast.NodeList.Elem) Allocator.Error!NodeIndex { | ||
| 753 | const result = @intCast(NodeIndex, c.nodes.len); | ||
| 754 | try c.nodes.append(c.gpa, elem); | ||
| 755 | return result; | ||
| 756 | } | ||
| 757 | |||
| 758 | fn addExtra(c: *Context, extra: anytype) Allocator.Error!NodeIndex { | ||
| 759 | const fields = std.meta.fields(@TypeOf(extra)); | ||
| 760 | try c.extra_data.ensureCapacity(c.gpa, c.extra_data.items.len + fields.len); | ||
| 761 | const result = @intCast(u32, c.extra_data.items.len); | ||
| 762 | inline for (fields) |field| { | ||
| 763 | comptime std.debug.assert(field.field_type == NodeIndex); | ||
| 764 | c.extra_data.appendAssumeCapacity(@field(extra, field.name)); | ||
| 765 | } | ||
| 766 | return result; | ||
| 767 | } | ||
| 768 | }; | ||
| 769 | |||
| 770 | fn renderNodes(c: *Context, nodes: []const Node) Allocator.Error!NodeSubRange { | ||
| 771 | var result = std.ArrayList(NodeIndex).init(c.gpa); | ||
| 772 | defer result.deinit(); | ||
| 773 | |||
| 774 | for (nodes) |node| { | ||
| 775 | const res = try renderNode(c, node); | ||
| 776 | if (node.tag() == .warning) continue; | ||
| 777 | try result.append(res); | ||
| 778 | } | ||
| 779 | |||
| 780 | return try c.listToSpan(result.items); | ||
| 781 | } | ||
| 782 | |||
| 783 | fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { | ||
| 784 | switch (node.tag()) { | ||
| 785 | .declaration => unreachable, | ||
| 786 | .warning => { | ||
| 787 | const payload = node.castTag(.warning).?.data; | ||
| 788 | try c.buf.appendSlice(payload); | ||
| 789 | try c.buf.append('\n'); | ||
| 790 | return @as(NodeIndex, 0); // error: integer value 0 cannot be coerced to type 'std.mem.Allocator.Error!u32' | ||
| 791 | }, | ||
| 792 | .usingnamespace_builtins => { | ||
| 793 | // pub usingnamespace @import("std").c.builtins; | ||
| 794 | _ = try c.addToken(.keyword_pub, "pub"); | ||
| 795 | const usingnamespace_token = try c.addToken(.keyword_usingnamespace, "usingnamespace"); | ||
| 796 | const import_node = try renderStdImport(c, "c", "builtins"); | ||
| 797 | _ = try c.addToken(.semicolon, ";"); | ||
| 798 | |||
| 799 | return c.addNode(.{ | ||
| 800 | .tag = .@"usingnamespace", | ||
| 801 | .main_token = usingnamespace_token, | ||
| 802 | .data = .{ | ||
| 803 | .lhs = import_node, | ||
| 804 | .rhs = undefined, | ||
| 805 | }, | ||
| 806 | }); | ||
| 807 | }, | ||
| 808 | .std_math_Log2Int => { | ||
| 809 | const payload = node.castTag(.std_math_Log2Int).?.data; | ||
| 810 | const import_node = try renderStdImport(c, "math", "Log2Int"); | ||
| 811 | return renderCall(c, import_node, &.{payload}); | ||
| 812 | }, | ||
| 813 | .std_meta_cast => { | ||
| 814 | const payload = node.castTag(.std_meta_cast).?.data; | ||
| 815 | const import_node = try renderStdImport(c, "meta", "cast"); | ||
| 816 | return renderCall(c, import_node, &.{ payload.lhs, payload.rhs }); | ||
| 817 | }, | ||
| 818 | .std_meta_sizeof => { | ||
| 819 | const payload = node.castTag(.std_meta_sizeof).?.data; | ||
| 820 | const import_node = try renderStdImport(c, "meta", "sizeof"); | ||
| 821 | return renderCall(c, import_node, &.{payload}); | ||
| 822 | }, | ||
| 823 | .std_mem_zeroes => { | ||
| 824 | const payload = node.castTag(.std_mem_zeroes).?.data; | ||
| 825 | const import_node = try renderStdImport(c, "mem", "zeroes"); | ||
| 826 | return renderCall(c, import_node, &.{payload}); | ||
| 827 | }, | ||
| 828 | .std_mem_zeroinit => { | ||
| 829 | const payload = node.castTag(.std_mem_zeroinit).?.data; | ||
| 830 | const import_node = try renderStdImport(c, "mem", "zeroInit"); | ||
| 831 | return renderCall(c, import_node, &.{ payload.lhs, payload.rhs }); | ||
| 832 | }, | ||
| 833 | .call => { | ||
| 834 | const payload = node.castTag(.call).?.data; | ||
| 835 | const lhs = try renderNode(c, payload.lhs); | ||
| 836 | return renderCall(c, lhs, payload.args); | ||
| 837 | }, | ||
| 838 | .null_literal => return c.addNode(.{ | ||
| 839 | .tag = .null_literal, | ||
| 840 | .main_token = try c.addToken(.keyword_null, "null"), | ||
| 841 | .data = undefined, | ||
| 842 | }), | ||
| 843 | .undefined_literal => return c.addNode(.{ | ||
| 844 | .tag = .undefined_literal, | ||
| 845 | .main_token = try c.addToken(.keyword_undefined, "undefined"), | ||
| 846 | .data = undefined, | ||
| 847 | }), | ||
| 848 | .true_literal => return c.addNode(.{ | ||
| 849 | .tag = .true_literal, | ||
| 850 | .main_token = try c.addToken(.keyword_true, "true"), | ||
| 851 | .data = undefined, | ||
| 852 | }), | ||
| 853 | .false_literal => return c.addNode(.{ | ||
| 854 | .tag = .false_literal, | ||
| 855 | .main_token = try c.addToken(.keyword_false, "false"), | ||
| 856 | .data = undefined, | ||
| 857 | }), | ||
| 858 | .zero_literal => return c.addNode(.{ | ||
| 859 | .tag = .integer_literal, | ||
| 860 | .main_token = try c.addToken(.integer_literal, "0"), | ||
| 861 | .data = undefined, | ||
| 862 | }), | ||
| 863 | .one_literal => return c.addNode(.{ | ||
| 864 | .tag = .integer_literal, | ||
| 865 | .main_token = try c.addToken(.integer_literal, "1"), | ||
| 866 | .data = undefined, | ||
| 867 | }), | ||
| 868 | .void_type => return c.addNode(.{ | ||
| 869 | .tag = .identifier, | ||
| 870 | .main_token = try c.addToken(.identifier, "void"), | ||
| 871 | .data = undefined, | ||
| 872 | }), | ||
| 873 | .noreturn_type => return c.addNode(.{ | ||
| 874 | .tag = .identifier, | ||
| 875 | .main_token = try c.addToken(.identifier, "noreturn"), | ||
| 876 | .data = undefined, | ||
| 877 | }), | ||
| 878 | .@"continue" => return c.addNode(.{ | ||
| 879 | .tag = .@"continue", | ||
| 880 | .main_token = try c.addToken(.keyword_continue, "continue"), | ||
| 881 | .data = .{ | ||
| 882 | .lhs = 0, | ||
| 883 | .rhs = undefined, | ||
| 884 | }, | ||
| 885 | }), | ||
| 886 | .return_void => return c.addNode(.{ | ||
| 887 | .tag = .@"return", | ||
| 888 | .main_token = try c.addToken(.keyword_return, "return"), | ||
| 889 | .data = .{ | ||
| 890 | .lhs = 0, | ||
| 891 | .rhs = undefined, | ||
| 892 | }, | ||
| 893 | }), | ||
| 894 | .@"break" => return c.addNode(.{ | ||
| 895 | .tag = .@"break", | ||
| 896 | .main_token = try c.addToken(.keyword_break, "break"), | ||
| 897 | .data = .{ | ||
| 898 | .lhs = 0, | ||
| 899 | .rhs = 0, | ||
| 900 | }, | ||
| 901 | }), | ||
| 902 | .break_val => { | ||
| 903 | const payload = node.castTag(.break_val).?.data; | ||
| 904 | const tok = try c.addToken(.keyword_break, "break"); | ||
| 905 | const break_label = if (payload.label) |some| blk: { | ||
| 906 | _ = try c.addToken(.colon, ":"); | ||
| 907 | break :blk try c.addIdentifier(some); | ||
| 908 | } else 0; | ||
| 909 | return c.addNode(.{ | ||
| 910 | .tag = .@"break", | ||
| 911 | .main_token = tok, | ||
| 912 | .data = .{ | ||
| 913 | .lhs = break_label, | ||
| 914 | .rhs = try renderNode(c, payload.val), | ||
| 915 | }, | ||
| 916 | }); | ||
| 917 | }, | ||
| 918 | .@"return" => { | ||
| 919 | const payload = node.castTag(.@"return").?.data; | ||
| 920 | return c.addNode(.{ | ||
| 921 | .tag = .@"return", | ||
| 922 | .main_token = try c.addToken(.keyword_return, "return"), | ||
| 923 | .data = .{ | ||
| 924 | .lhs = try renderNode(c, payload), | ||
| 925 | .rhs = undefined, | ||
| 926 | }, | ||
| 927 | }); | ||
| 928 | }, | ||
| 929 | .type => { | ||
| 930 | const payload = node.castTag(.type).?.data; | ||
| 931 | return c.addNode(.{ | ||
| 932 | .tag = .identifier, | ||
| 933 | .main_token = try c.addToken(.identifier, payload), | ||
| 934 | .data = undefined, | ||
| 935 | }); | ||
| 936 | }, | ||
| 937 | .log2_int_type => { | ||
| 938 | const payload = node.castTag(.log2_int_type).?.data; | ||
| 939 | return c.addNode(.{ | ||
| 940 | .tag = .identifier, | ||
| 941 | .main_token = try c.addTokenFmt(.identifier, "u{d}", .{payload}), | ||
| 942 | .data = undefined, | ||
| 943 | }); | ||
| 944 | }, | ||
| 945 | .identifier => { | ||
| 946 | const payload = node.castTag(.identifier).?.data; | ||
| 947 | return c.addNode(.{ | ||
| 948 | .tag = .identifier, | ||
| 949 | .main_token = try c.addIdentifier(payload), | ||
| 950 | .data = undefined, | ||
| 951 | }); | ||
| 952 | }, | ||
| 953 | .float_literal => { | ||
| 954 | const payload = node.castTag(.float_literal).?.data; | ||
| 955 | return c.addNode(.{ | ||
| 956 | .tag = .float_literal, | ||
| 957 | .main_token = try c.addToken(.float_literal, payload), | ||
| 958 | .data = undefined, | ||
| 959 | }); | ||
| 960 | }, | ||
| 961 | .integer_literal => { | ||
| 962 | const payload = node.castTag(.integer_literal).?.data; | ||
| 963 | return c.addNode(.{ | ||
| 964 | .tag = .integer_literal, | ||
| 965 | .main_token = try c.addToken(.integer_literal, payload), | ||
| 966 | .data = undefined, | ||
| 967 | }); | ||
| 968 | }, | ||
| 969 | .string_literal => { | ||
| 970 | const payload = node.castTag(.string_literal).?.data; | ||
| 971 | return c.addNode(.{ | ||
| 972 | .tag = .identifier, | ||
| 973 | .main_token = try c.addToken(.string_literal, payload), | ||
| 974 | .data = undefined, | ||
| 975 | }); | ||
| 976 | }, | ||
| 977 | .char_literal => { | ||
| 978 | const payload = node.castTag(.char_literal).?.data; | ||
| 979 | return c.addNode(.{ | ||
| 980 | .tag = .identifier, | ||
| 981 | .main_token = try c.addToken(.char_literal, payload), | ||
| 982 | .data = undefined, | ||
| 983 | }); | ||
| 984 | }, | ||
| 985 | .fail_decl => { | ||
| 986 | const payload = node.castTag(.fail_decl).?.data; | ||
| 987 | // pub const name = @compileError(msg); | ||
| 988 | _ = try c.addToken(.keyword_pub, "pub"); | ||
| 989 | const const_tok = try c.addToken(.keyword_const, "const"); | ||
| 990 | _ = try c.addIdentifier(payload.actual); | ||
| 991 | _ = try c.addToken(.equal, "="); | ||
| 992 | |||
| 993 | const compile_error_tok = try c.addToken(.builtin, "@compileError"); | ||
| 994 | _ = try c.addToken(.l_paren, "("); | ||
| 995 | const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{s}\"", .{std.zig.fmtEscapes(payload.mangled)}); | ||
| 996 | const err_msg = try c.addNode(.{ | ||
| 997 | .tag = .string_literal, | ||
| 998 | .main_token = err_msg_tok, | ||
| 999 | .data = undefined, | ||
| 1000 | }); | ||
| 1001 | _ = try c.addToken(.r_paren, ")"); | ||
| 1002 | const compile_error = try c.addNode(.{ | ||
| 1003 | .tag = .builtin_call_two, | ||
| 1004 | .main_token = compile_error_tok, | ||
| 1005 | .data = .{ | ||
| 1006 | .lhs = err_msg, | ||
| 1007 | .rhs = 0, | ||
| 1008 | }, | ||
| 1009 | }); | ||
| 1010 | _ = try c.addToken(.semicolon, ";"); | ||
| 1011 | |||
| 1012 | return c.addNode(.{ | ||
| 1013 | .tag = .simple_var_decl, | ||
| 1014 | .main_token = const_tok, | ||
| 1015 | .data = .{ | ||
| 1016 | .lhs = 0, | ||
| 1017 | .rhs = compile_error, | ||
| 1018 | }, | ||
| 1019 | }); | ||
| 1020 | }, | ||
| 1021 | .pub_var_simple, .var_simple => { | ||
| 1022 | const payload = @fieldParentPtr(Payload.SimpleVarDecl, "base", node.ptr_otherwise).data; | ||
| 1023 | if (node.tag() == .pub_var_simple) _ = try c.addToken(.keyword_pub, "pub"); | ||
| 1024 | const const_tok = try c.addToken(.keyword_const, "const"); | ||
| 1025 | _ = try c.addIdentifier(payload.name); | ||
| 1026 | _ = try c.addToken(.equal, "="); | ||
| 1027 | |||
| 1028 | const init = try renderNode(c, payload.init); | ||
| 1029 | _ = try c.addToken(.semicolon, ";"); | ||
| 1030 | |||
| 1031 | return c.addNode(.{ | ||
| 1032 | .tag = .simple_var_decl, | ||
| 1033 | .main_token = const_tok, | ||
| 1034 | .data = .{ | ||
| 1035 | .lhs = 0, | ||
| 1036 | .rhs = init, | ||
| 1037 | }, | ||
| 1038 | }); | ||
| 1039 | }, | ||
| 1040 | .var_decl => return renderVar(c, node), | ||
| 1041 | .arg_redecl, .alias => { | ||
| 1042 | const payload = @fieldParentPtr(Payload.ArgRedecl, "base", node.ptr_otherwise).data; | ||
| 1043 | if (node.tag() == .alias) _ = try c.addToken(.keyword_pub, "pub"); | ||
| 1044 | const mut_tok = if (node.tag() == .alias) | ||
| 1045 | try c.addToken(.keyword_const, "const") | ||
| 1046 | else | ||
| 1047 | try c.addToken(.keyword_var, "var"); | ||
| 1048 | _ = try c.addIdentifier(payload.actual); | ||
| 1049 | _ = try c.addToken(.equal, "="); | ||
| 1050 | |||
| 1051 | const init = try c.addNode(.{ | ||
| 1052 | .tag = .identifier, | ||
| 1053 | .main_token = try c.addIdentifier(payload.mangled), | ||
| 1054 | .data = undefined, | ||
| 1055 | }); | ||
| 1056 | _ = try c.addToken(.semicolon, ";"); | ||
| 1057 | |||
| 1058 | return c.addNode(.{ | ||
| 1059 | .tag = .simple_var_decl, | ||
| 1060 | .main_token = mut_tok, | ||
| 1061 | .data = .{ | ||
| 1062 | .lhs = 0, | ||
| 1063 | .rhs = init, | ||
| 1064 | }, | ||
| 1065 | }); | ||
| 1066 | }, | ||
| 1067 | .int_cast => { | ||
| 1068 | const payload = node.castTag(.int_cast).?.data; | ||
| 1069 | return renderBuiltinCall(c, "@intCast", &.{ payload.lhs, payload.rhs }); | ||
| 1070 | }, | ||
| 1071 | .rem => { | ||
| 1072 | const payload = node.castTag(.rem).?.data; | ||
| 1073 | return renderBuiltinCall(c, "@rem", &.{ payload.lhs, payload.rhs }); | ||
| 1074 | }, | ||
| 1075 | .div_trunc => { | ||
| 1076 | const payload = node.castTag(.div_trunc).?.data; | ||
| 1077 | return renderBuiltinCall(c, "@divTrunc", &.{ payload.lhs, payload.rhs }); | ||
| 1078 | }, | ||
| 1079 | .bool_to_int => { | ||
| 1080 | const payload = node.castTag(.bool_to_int).?.data; | ||
| 1081 | return renderBuiltinCall(c, "@boolToInt", &.{payload}); | ||
| 1082 | }, | ||
| 1083 | .as => { | ||
| 1084 | const payload = node.castTag(.as).?.data; | ||
| 1085 | return renderBuiltinCall(c, "@as", &.{ payload.lhs, payload.rhs }); | ||
| 1086 | }, | ||
| 1087 | .truncate => { | ||
| 1088 | const payload = node.castTag(.truncate).?.data; | ||
| 1089 | return renderBuiltinCall(c, "@truncate", &.{ payload.lhs, payload.rhs }); | ||
| 1090 | }, | ||
| 1091 | .bit_cast => { | ||
| 1092 | const payload = node.castTag(.bit_cast).?.data; | ||
| 1093 | return renderBuiltinCall(c, "@bitCast", &.{ payload.lhs, payload.rhs }); | ||
| 1094 | }, | ||
| 1095 | .float_cast => { | ||
| 1096 | const payload = node.castTag(.float_cast).?.data; | ||
| 1097 | return renderBuiltinCall(c, "@floatCast", &.{ payload.lhs, payload.rhs }); | ||
| 1098 | }, | ||
| 1099 | .float_to_int => { | ||
| 1100 | const payload = node.castTag(.float_to_int).?.data; | ||
| 1101 | return renderBuiltinCall(c, "@floatToInt", &.{ payload.lhs, payload.rhs }); | ||
| 1102 | }, | ||
| 1103 | .int_to_float => { | ||
| 1104 | const payload = node.castTag(.int_to_float).?.data; | ||
| 1105 | return renderBuiltinCall(c, "@intToFloat", &.{ payload.lhs, payload.rhs }); | ||
| 1106 | }, | ||
| 1107 | .int_to_enum => { | ||
| 1108 | const payload = node.castTag(.int_to_enum).?.data; | ||
| 1109 | return renderBuiltinCall(c, "@intToEnum", &.{ payload.lhs, payload.rhs }); | ||
| 1110 | }, | ||
| 1111 | .enum_to_int => { | ||
| 1112 | const payload = node.castTag(.enum_to_int).?.data; | ||
| 1113 | return renderBuiltinCall(c, "@enumToInt", &.{payload}); | ||
| 1114 | }, | ||
| 1115 | .int_to_ptr => { | ||
| 1116 | const payload = node.castTag(.int_to_ptr).?.data; | ||
| 1117 | return renderBuiltinCall(c, "@intToPtr", &.{ payload.lhs, payload.rhs }); | ||
| 1118 | }, | ||
| 1119 | .ptr_to_int => { | ||
| 1120 | const payload = node.castTag(.ptr_to_int).?.data; | ||
| 1121 | return renderBuiltinCall(c, "@ptrToInt", &.{payload}); | ||
| 1122 | }, | ||
| 1123 | .align_cast => { | ||
| 1124 | const payload = node.castTag(.align_cast).?.data; | ||
| 1125 | return renderBuiltinCall(c, "@alignCast", &.{ payload.lhs, payload.rhs }); | ||
| 1126 | }, | ||
| 1127 | .ptr_cast => { | ||
| 1128 | const payload = node.castTag(.ptr_cast).?.data; | ||
| 1129 | return renderBuiltinCall(c, "@ptrCast", &.{ payload.lhs, payload.rhs }); | ||
| 1130 | }, | ||
| 1131 | .sizeof => { | ||
| 1132 | const payload = node.castTag(.sizeof).?.data; | ||
| 1133 | return renderBuiltinCall(c, "@sizeOf", &.{payload}); | ||
| 1134 | }, | ||
| 1135 | .alignof => { | ||
| 1136 | const payload = node.castTag(.alignof).?.data; | ||
| 1137 | return renderBuiltinCall(c, "@alignOf", &.{payload}); | ||
| 1138 | }, | ||
| 1139 | .typeof => { | ||
| 1140 | const payload = node.castTag(.typeof).?.data; | ||
| 1141 | return renderBuiltinCall(c, "@TypeOf", &.{payload}); | ||
| 1142 | }, | ||
| 1143 | .negate => return renderPrefixOp(c, node, .negation, .minus, "-"), | ||
| 1144 | .negate_wrap => return renderPrefixOp(c, node, .negation_wrap, .minus_percent, "-%"), | ||
| 1145 | .bit_not => return renderPrefixOp(c, node, .bit_not, .tilde, "~"), | ||
| 1146 | .not => return renderPrefixOp(c, node, .bool_not, .bang, "!"), | ||
| 1147 | .optional_type => return renderPrefixOp(c, node, .optional_type, .question_mark, "?"), | ||
| 1148 | .address_of => return renderPrefixOp(c, node, .address_of, .ampersand, "&"), | ||
| 1149 | .deref => { | ||
| 1150 | const payload = node.castTag(.deref).?.data; | ||
| 1151 | const operand = try renderNodeGrouped(c, payload); | ||
| 1152 | const deref_tok = try c.addToken(.period_asterisk, ".*"); | ||
| 1153 | return c.addNode(.{ | ||
| 1154 | .tag = .deref, | ||
| 1155 | .main_token = deref_tok, | ||
| 1156 | .data = .{ | ||
| 1157 | .lhs = operand, | ||
| 1158 | .rhs = undefined, | ||
| 1159 | }, | ||
| 1160 | }); | ||
| 1161 | }, | ||
| 1162 | .unwrap => { | ||
| 1163 | const payload = node.castTag(.unwrap).?.data; | ||
| 1164 | const operand = try renderNodeGrouped(c, payload); | ||
| 1165 | const period = try c.addToken(.period, "."); | ||
| 1166 | const question_mark = try c.addToken(.question_mark, "?"); | ||
| 1167 | return c.addNode(.{ | ||
| 1168 | .tag = .unwrap_optional, | ||
| 1169 | .main_token = period, | ||
| 1170 | .data = .{ | ||
| 1171 | .lhs = operand, | ||
| 1172 | .rhs = question_mark, | ||
| 1173 | }, | ||
| 1174 | }); | ||
| 1175 | }, | ||
| 1176 | .c_pointer, .single_pointer => { | ||
| 1177 | const payload = @fieldParentPtr(Payload.Pointer, "base", node.ptr_otherwise).data; | ||
| 1178 | |||
| 1179 | const asterisk = if (node.tag() == .single_pointer) | ||
| 1180 | try c.addToken(.asterisk, "*") | ||
| 1181 | else blk: { | ||
| 1182 | _ = try c.addToken(.l_bracket, "["); | ||
| 1183 | const res = try c.addToken(.asterisk, "*"); | ||
| 1184 | _ = try c.addIdentifier("c"); | ||
| 1185 | _ = try c.addToken(.r_bracket, "]"); | ||
| 1186 | break :blk res; | ||
| 1187 | }; | ||
| 1188 | if (payload.is_const) _ = try c.addToken(.keyword_const, "const"); | ||
| 1189 | if (payload.is_volatile) _ = try c.addToken(.keyword_volatile, "volatile"); | ||
| 1190 | const elem_type = try renderNodeGrouped(c, payload.elem_type); | ||
| 1191 | |||
| 1192 | return c.addNode(.{ | ||
| 1193 | .tag = .ptr_type_aligned, | ||
| 1194 | .main_token = asterisk, | ||
| 1195 | .data = .{ | ||
| 1196 | .lhs = 0, | ||
| 1197 | .rhs = elem_type, | ||
| 1198 | }, | ||
| 1199 | }); | ||
| 1200 | }, | ||
| 1201 | .add => return renderBinOpGrouped(c, node, .add, .plus, "+"), | ||
| 1202 | .add_assign => return renderBinOp(c, node, .assign_add, .plus_equal, "+="), | ||
| 1203 | .add_wrap => return renderBinOpGrouped(c, node, .add_wrap, .plus_percent, "+%"), | ||
| 1204 | .add_wrap_assign => return renderBinOp(c, node, .assign_add_wrap, .plus_percent_equal, "+%="), | ||
| 1205 | .sub => return renderBinOpGrouped(c, node, .sub, .minus, "-"), | ||
| 1206 | .sub_assign => return renderBinOp(c, node, .assign_sub, .minus_equal, "-="), | ||
| 1207 | .sub_wrap => return renderBinOpGrouped(c, node, .sub_wrap, .minus_percent, "-%"), | ||
| 1208 | .sub_wrap_assign => return renderBinOp(c, node, .assign_sub_wrap, .minus_percent_equal, "-%="), | ||
| 1209 | .mul => return renderBinOpGrouped(c, node, .mul, .asterisk, "*"), | ||
| 1210 | .mul_assign => return renderBinOp(c, node, .assign_mul, .asterisk_equal, "*="), | ||
| 1211 | .mul_wrap => return renderBinOpGrouped(c, node, .mul_wrap, .asterisk_percent, "*%"), | ||
| 1212 | .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="), | ||
| 1213 | .div => return renderBinOpGrouped(c, node, .div, .slash, "/"), | ||
| 1214 | .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="), | ||
| 1215 | .shl => return renderBinOpGrouped(c, node, .bit_shift_left, .angle_bracket_angle_bracket_left, "<<"), | ||
| 1216 | .shl_assign => return renderBinOp(c, node, .assign_bit_shift_left, .angle_bracket_angle_bracket_left_equal, "<<="), | ||
| 1217 | .shr => return renderBinOpGrouped(c, node, .bit_shift_right, .angle_bracket_angle_bracket_right, ">>"), | ||
| 1218 | .shr_assign => return renderBinOp(c, node, .assign_bit_shift_right, .angle_bracket_angle_bracket_right_equal, ">>="), | ||
| 1219 | .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"), | ||
| 1220 | .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="), | ||
| 1221 | .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"), | ||
| 1222 | .@"or" => return renderBinOpGrouped(c, node, .bool_or, .keyword_or, "or"), | ||
| 1223 | .less_than => return renderBinOpGrouped(c, node, .less_than, .angle_bracket_left, "<"), | ||
| 1224 | .less_than_equal => return renderBinOpGrouped(c, node, .less_or_equal, .angle_bracket_left_equal, "<="), | ||
| 1225 | .greater_than => return renderBinOpGrouped(c, node, .greater_than, .angle_bracket_right, ">="), | ||
| 1226 | .greater_than_equal => return renderBinOpGrouped(c, node, .greater_or_equal, .angle_bracket_right_equal, ">="), | ||
| 1227 | .equal => return renderBinOpGrouped(c, node, .equal_equal, .equal_equal, "=="), | ||
| 1228 | .not_equal => return renderBinOpGrouped(c, node, .bang_equal, .bang_equal, "!="), | ||
| 1229 | .bit_and => return renderBinOpGrouped(c, node, .bit_and, .ampersand, "&"), | ||
| 1230 | .bit_and_assign => return renderBinOp(c, node, .assign_bit_and, .ampersand_equal, "&="), | ||
| 1231 | .bit_or => return renderBinOpGrouped(c, node, .bit_or, .pipe, "|"), | ||
| 1232 | .bit_or_assign => return renderBinOp(c, node, .assign_bit_or, .pipe_equal, "|="), | ||
| 1233 | .bit_xor => return renderBinOpGrouped(c, node, .bit_xor, .caret, "^"), | ||
| 1234 | .bit_xor_assign => return renderBinOp(c, node, .assign_bit_xor, .caret_equal, "^="), | ||
| 1235 | .array_cat => return renderBinOp(c, node, .array_cat, .plus_plus, "++"), | ||
| 1236 | .ellipsis3 => return renderBinOpGrouped(c, node, .switch_range, .ellipsis3, "..."), | ||
| 1237 | .assign => return renderBinOp(c, node, .assign, .equal, "="), | ||
| 1238 | .empty_block => { | ||
| 1239 | const l_brace = try c.addToken(.l_brace, "{"); | ||
| 1240 | _ = try c.addToken(.r_brace, "}"); | ||
| 1241 | return c.addNode(.{ | ||
| 1242 | .tag = .block_two, | ||
| 1243 | .main_token = l_brace, | ||
| 1244 | .data = .{ | ||
| 1245 | .lhs = 0, | ||
| 1246 | .rhs = 0, | ||
| 1247 | }, | ||
| 1248 | }); | ||
| 1249 | }, | ||
| 1250 | .block_single => { | ||
| 1251 | const payload = node.castTag(.block_single).?.data; | ||
| 1252 | const l_brace = try c.addToken(.l_brace, "{"); | ||
| 1253 | |||
| 1254 | const stmt = try renderNode(c, payload); | ||
| 1255 | try addSemicolonIfNeeded(c, payload); | ||
| 1256 | |||
| 1257 | _ = try c.addToken(.r_brace, "}"); | ||
| 1258 | return c.addNode(.{ | ||
| 1259 | .tag = .block_two_semicolon, | ||
| 1260 | .main_token = l_brace, | ||
| 1261 | .data = .{ | ||
| 1262 | .lhs = stmt, | ||
| 1263 | .rhs = 0, | ||
| 1264 | }, | ||
| 1265 | }); | ||
| 1266 | }, | ||
| 1267 | .block => { | ||
| 1268 | const payload = node.castTag(.block).?.data; | ||
| 1269 | if (payload.label) |some| { | ||
| 1270 | _ = try c.addIdentifier(some); | ||
| 1271 | _ = try c.addToken(.colon, ":"); | ||
| 1272 | } | ||
| 1273 | const l_brace = try c.addToken(.l_brace, "{"); | ||
| 1274 | |||
| 1275 | var stmts = std.ArrayList(NodeIndex).init(c.gpa); | ||
| 1276 | defer stmts.deinit(); | ||
| 1277 | for (payload.stmts) |stmt| { | ||
| 1278 | const res = try renderNode(c, stmt); | ||
| 1279 | if (res == 0) continue; | ||
| 1280 | try addSemicolonIfNeeded(c, stmt); | ||
| 1281 | try stmts.append(res); | ||
| 1282 | } | ||
| 1283 | const span = try c.listToSpan(stmts.items); | ||
| 1284 | _ = try c.addToken(.r_brace, "}"); | ||
| 1285 | |||
| 1286 | const semicolon = c.tokens.items(.tag)[c.tokens.len - 2] == .semicolon; | ||
| 1287 | return c.addNode(.{ | ||
| 1288 | .tag = if (semicolon) .block_semicolon else .block, | ||
| 1289 | .main_token = l_brace, | ||
| 1290 | .data = .{ | ||
| 1291 | .lhs = span.start, | ||
| 1292 | .rhs = span.end, | ||
| 1293 | }, | ||
| 1294 | }); | ||
| 1295 | }, | ||
| 1296 | .func => return renderFunc(c, node), | ||
| 1297 | .pub_inline_fn => return renderMacroFunc(c, node), | ||
| 1298 | .discard => { | ||
| 1299 | const payload = node.castTag(.discard).?.data; | ||
| 1300 | const lhs = try c.addNode(.{ | ||
| 1301 | .tag = .identifier, | ||
| 1302 | .main_token = try c.addToken(.identifier, "_"), | ||
| 1303 | .data = undefined, | ||
| 1304 | }); | ||
| 1305 | return c.addNode(.{ | ||
| 1306 | .tag = .assign, | ||
| 1307 | .main_token = try c.addToken(.equal, "="), | ||
| 1308 | .data = .{ | ||
| 1309 | .lhs = lhs, | ||
| 1310 | .rhs = try renderNode(c, payload), | ||
| 1311 | }, | ||
| 1312 | }); | ||
| 1313 | }, | ||
| 1314 | .@"while" => { | ||
| 1315 | const payload = node.castTag(.@"while").?.data; | ||
| 1316 | const while_tok = try c.addToken(.keyword_while, "while"); | ||
| 1317 | _ = try c.addToken(.l_paren, "("); | ||
| 1318 | const cond = try renderNode(c, payload.cond); | ||
| 1319 | _ = try c.addToken(.r_paren, ")"); | ||
| 1320 | |||
| 1321 | const cont_expr = if (payload.cont_expr) |some| blk: { | ||
| 1322 | _ = try c.addToken(.colon, ":"); | ||
| 1323 | _ = try c.addToken(.l_paren, "("); | ||
| 1324 | const res = try renderNode(c, some); | ||
| 1325 | _ = try c.addToken(.r_paren, ")"); | ||
| 1326 | break :blk res; | ||
| 1327 | } else 0; | ||
| 1328 | const body = try renderNode(c, payload.body); | ||
| 1329 | |||
| 1330 | if (cont_expr == 0) { | ||
| 1331 | return c.addNode(.{ | ||
| 1332 | .tag = .while_simple, | ||
| 1333 | .main_token = while_tok, | ||
| 1334 | .data = .{ | ||
| 1335 | .lhs = cond, | ||
| 1336 | .rhs = body, | ||
| 1337 | }, | ||
| 1338 | }); | ||
| 1339 | } else { | ||
| 1340 | return c.addNode(.{ | ||
| 1341 | .tag = .while_cont, | ||
| 1342 | .main_token = while_tok, | ||
| 1343 | .data = .{ | ||
| 1344 | .lhs = cond, | ||
| 1345 | .rhs = try c.addExtra(std.zig.ast.Node.WhileCont{ | ||
| 1346 | .cont_expr = cont_expr, | ||
| 1347 | .then_expr = body, | ||
| 1348 | }), | ||
| 1349 | }, | ||
| 1350 | }); | ||
| 1351 | } | ||
| 1352 | }, | ||
| 1353 | .while_true => { | ||
| 1354 | const payload = node.castTag(.while_true).?.data; | ||
| 1355 | const while_tok = try c.addToken(.keyword_while, "while"); | ||
| 1356 | _ = try c.addToken(.l_paren, "("); | ||
| 1357 | const cond = try c.addNode(.{ | ||
| 1358 | .tag = .true_literal, | ||
| 1359 | .main_token = try c.addToken(.keyword_true, "true"), | ||
| 1360 | .data = undefined, | ||
| 1361 | }); | ||
| 1362 | _ = try c.addToken(.r_paren, ")"); | ||
| 1363 | const body = try renderNode(c, payload); | ||
| 1364 | |||
| 1365 | return c.addNode(.{ | ||
| 1366 | .tag = .while_simple, | ||
| 1367 | .main_token = while_tok, | ||
| 1368 | .data = .{ | ||
| 1369 | .lhs = cond, | ||
| 1370 | .rhs = body, | ||
| 1371 | }, | ||
| 1372 | }); | ||
| 1373 | }, | ||
| 1374 | .@"if" => { | ||
| 1375 | const payload = node.castTag(.@"if").?.data; | ||
| 1376 | const if_tok = try c.addToken(.keyword_if, "if"); | ||
| 1377 | _ = try c.addToken(.l_paren, "("); | ||
| 1378 | const cond = try renderNode(c, payload.cond); | ||
| 1379 | _ = try c.addToken(.r_paren, ")"); | ||
| 1380 | |||
| 1381 | const then_expr = try renderNode(c, payload.then); | ||
| 1382 | const else_node = payload.@"else" orelse return c.addNode(.{ | ||
| 1383 | .tag = .if_simple, | ||
| 1384 | .main_token = if_tok, | ||
| 1385 | .data = .{ | ||
| 1386 | .lhs = cond, | ||
| 1387 | .rhs = then_expr, | ||
| 1388 | }, | ||
| 1389 | }); | ||
| 1390 | _ = try c.addToken(.keyword_else, "else"); | ||
| 1391 | const else_expr = try renderNode(c, else_node); | ||
| 1392 | |||
| 1393 | return c.addNode(.{ | ||
| 1394 | .tag = .@"if", | ||
| 1395 | .main_token = if_tok, | ||
| 1396 | .data = .{ | ||
| 1397 | .lhs = cond, | ||
| 1398 | .rhs = try c.addExtra(std.zig.ast.Node.If{ | ||
| 1399 | .then_expr = then_expr, | ||
| 1400 | .else_expr = else_expr, | ||
| 1401 | }), | ||
| 1402 | }, | ||
| 1403 | }); | ||
| 1404 | }, | ||
| 1405 | .if_not_break => { | ||
| 1406 | const payload = node.castTag(.if_not_break).?.data; | ||
| 1407 | const if_tok = try c.addToken(.keyword_if, "if"); | ||
| 1408 | _ = try c.addToken(.l_paren, "("); | ||
| 1409 | const cond = try c.addNode(.{ | ||
| 1410 | .tag = .bool_not, | ||
| 1411 | .main_token = try c.addToken(.bang, "!"), | ||
| 1412 | .data = .{ | ||
| 1413 | .lhs = try renderNodeGrouped(c, payload), | ||
| 1414 | .rhs = undefined, | ||
| 1415 | }, | ||
| 1416 | }); | ||
| 1417 | _ = try c.addToken(.r_paren, ")"); | ||
| 1418 | const then_expr = try c.addNode(.{ | ||
| 1419 | .tag = .@"break", | ||
| 1420 | .main_token = try c.addToken(.keyword_break, "break"), | ||
| 1421 | .data = .{ | ||
| 1422 | .lhs = 0, | ||
| 1423 | .rhs = 0, | ||
| 1424 | }, | ||
| 1425 | }); | ||
| 1426 | |||
| 1427 | return c.addNode(.{ | ||
| 1428 | .tag = .if_simple, | ||
| 1429 | .main_token = if_tok, | ||
| 1430 | .data = .{ | ||
| 1431 | .lhs = cond, | ||
| 1432 | .rhs = then_expr, | ||
| 1433 | }, | ||
| 1434 | }); | ||
| 1435 | }, | ||
| 1436 | .@"switch" => { | ||
| 1437 | const payload = node.castTag(.@"switch").?.data; | ||
| 1438 | const switch_tok = try c.addToken(.keyword_switch, "switch"); | ||
| 1439 | _ = try c.addToken(.l_paren, "("); | ||
| 1440 | const cond = try renderNode(c, payload.cond); | ||
| 1441 | _ = try c.addToken(.r_paren, ")"); | ||
| 1442 | |||
| 1443 | _ = try c.addToken(.l_brace, "{"); | ||
| 1444 | var cases = try c.gpa.alloc(NodeIndex, payload.cases.len); | ||
| 1445 | defer c.gpa.free(cases); | ||
| 1446 | for (payload.cases) |case, i| { | ||
| 1447 | cases[i] = try renderNode(c, case); | ||
| 1448 | _ = try c.addToken(.comma, ","); | ||
| 1449 | } | ||
| 1450 | const span = try c.listToSpan(cases); | ||
| 1451 | _ = try c.addToken(.r_brace, "}"); | ||
| 1452 | return c.addNode(.{ | ||
| 1453 | .tag = .switch_comma, | ||
| 1454 | .main_token = switch_tok, | ||
| 1455 | .data = .{ | ||
| 1456 | .lhs = cond, | ||
| 1457 | .rhs = try c.addExtra(NodeSubRange{ | ||
| 1458 | .start = span.start, | ||
| 1459 | .end = span.end, | ||
| 1460 | }), | ||
| 1461 | }, | ||
| 1462 | }); | ||
| 1463 | }, | ||
| 1464 | .switch_else => { | ||
| 1465 | const payload = node.castTag(.switch_else).?.data; | ||
| 1466 | _ = try c.addToken(.keyword_else, "else"); | ||
| 1467 | return c.addNode(.{ | ||
| 1468 | .tag = .switch_case_one, | ||
| 1469 | .main_token = try c.addToken(.equal_angle_bracket_right, "=>"), | ||
| 1470 | .data = .{ | ||
| 1471 | .lhs = 0, | ||
| 1472 | .rhs = try renderNode(c, payload), | ||
| 1473 | }, | ||
| 1474 | }); | ||
| 1475 | }, | ||
| 1476 | .switch_prong => { | ||
| 1477 | const payload = node.castTag(.switch_prong).?.data; | ||
| 1478 | var items = try c.gpa.alloc(NodeIndex, std.math.max(payload.cases.len, 1)); | ||
| 1479 | defer c.gpa.free(items); | ||
| 1480 | items[0] = 0; | ||
| 1481 | for (payload.cases) |item, i| { | ||
| 1482 | if (i != 0) _ = try c.addToken(.comma, ","); | ||
| 1483 | items[i] = try renderNode(c, item); | ||
| 1484 | } | ||
| 1485 | _ = try c.addToken(.r_brace, "}"); | ||
| 1486 | if (items.len < 2) { | ||
| 1487 | return c.addNode(.{ | ||
| 1488 | .tag = .switch_case_one, | ||
| 1489 | .main_token = try c.addToken(.equal_angle_bracket_right, "=>"), | ||
| 1490 | .data = .{ | ||
| 1491 | .lhs = items[0], | ||
| 1492 | .rhs = try renderNode(c, payload.cond), | ||
| 1493 | }, | ||
| 1494 | }); | ||
| 1495 | } else { | ||
| 1496 | const span = try c.listToSpan(items); | ||
| 1497 | return c.addNode(.{ | ||
| 1498 | .tag = .switch_case, | ||
| 1499 | .main_token = try c.addToken(.equal_angle_bracket_right, "=>"), | ||
| 1500 | .data = .{ | ||
| 1501 | .lhs = try c.addExtra(NodeSubRange{ | ||
| 1502 | .start = span.start, | ||
| 1503 | .end = span.end, | ||
| 1504 | }), | ||
| 1505 | .rhs = try renderNode(c, payload.cond), | ||
| 1506 | }, | ||
| 1507 | }); | ||
| 1508 | } | ||
| 1509 | }, | ||
| 1510 | .opaque_literal => { | ||
| 1511 | const opaque_tok = try c.addToken(.keyword_opaque, "opaque"); | ||
| 1512 | _ = try c.addToken(.l_brace, "{"); | ||
| 1513 | _ = try c.addToken(.r_brace, "}"); | ||
| 1514 | |||
| 1515 | return c.addNode(.{ | ||
| 1516 | .tag = .container_decl_two, | ||
| 1517 | .main_token = opaque_tok, | ||
| 1518 | .data = .{ | ||
| 1519 | .lhs = 0, | ||
| 1520 | .rhs = 0, | ||
| 1521 | }, | ||
| 1522 | }); | ||
| 1523 | }, | ||
| 1524 | .array_access => { | ||
| 1525 | const payload = node.castTag(.array_access).?.data; | ||
| 1526 | const lhs = try renderNode(c, payload.lhs); | ||
| 1527 | const l_bracket = try c.addToken(.l_bracket, "["); | ||
| 1528 | const index_expr = try renderNode(c, payload.rhs); | ||
| 1529 | _ = try c.addToken(.r_bracket, "]"); | ||
| 1530 | return c.addNode(.{ | ||
| 1531 | .tag = .array_access, | ||
| 1532 | .main_token = l_bracket, | ||
| 1533 | .data = .{ | ||
| 1534 | .lhs = lhs, | ||
| 1535 | .rhs = index_expr, | ||
| 1536 | }, | ||
| 1537 | }); | ||
| 1538 | }, | ||
| 1539 | .array_type => { | ||
| 1540 | const payload = node.castTag(.array_type).?.data; | ||
| 1541 | return renderArrayType(c, payload.len, payload.elem_type); | ||
| 1542 | }, | ||
| 1543 | .array_filler => { | ||
| 1544 | const payload = node.castTag(.array_filler).?.data; | ||
| 1545 | |||
| 1546 | const type_expr = try renderArrayType(c, 1, payload.type); | ||
| 1547 | const l_brace = try c.addToken(.l_brace, "{"); | ||
| 1548 | const val = try renderNode(c, payload.filler); | ||
| 1549 | _ = try c.addToken(.r_brace, "}"); | ||
| 1550 | |||
| 1551 | const init = try c.addNode(.{ | ||
| 1552 | .tag = .array_init_one, | ||
| 1553 | .main_token = l_brace, | ||
| 1554 | .data = .{ | ||
| 1555 | .lhs = type_expr, | ||
| 1556 | .rhs = val, | ||
| 1557 | }, | ||
| 1558 | }); | ||
| 1559 | return c.addNode(.{ | ||
| 1560 | .tag = .array_cat, | ||
| 1561 | .main_token = try c.addToken(.asterisk_asterisk, "**"), | ||
| 1562 | .data = .{ | ||
| 1563 | .lhs = init, | ||
| 1564 | .rhs = try c.addNode(.{ | ||
| 1565 | .tag = .integer_literal, | ||
| 1566 | .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{payload.count}), | ||
| 1567 | .data = undefined, | ||
| 1568 | }), | ||
| 1569 | }, | ||
| 1570 | }); | ||
| 1571 | }, | ||
| 1572 | .empty_array => { | ||
| 1573 | const payload = node.castTag(.empty_array).?.data; | ||
| 1574 | |||
| 1575 | const type_expr = try renderArrayType(c, 0, payload); | ||
| 1576 | return renderArrayInit(c, type_expr, &.{}); | ||
| 1577 | }, | ||
| 1578 | .array_init => { | ||
| 1579 | const payload = node.castTag(.array_init).?.data; | ||
| 1580 | const type_expr = try renderNode(c, payload.cond); | ||
| 1581 | return renderArrayInit(c, type_expr, payload.cases); | ||
| 1582 | }, | ||
| 1583 | .field_access => { | ||
| 1584 | const payload = node.castTag(.field_access).?.data; | ||
| 1585 | const lhs = try renderNode(c, payload.lhs); | ||
| 1586 | return renderFieldAccess(c, lhs, payload.field_name); | ||
| 1587 | }, | ||
| 1588 | .@"struct", .@"union" => return renderRecord(c, node), | ||
| 1589 | .@"enum" => { | ||
| 1590 | const payload = node.castTag(.@"enum").?.data; | ||
| 1591 | _ = try c.addToken(.keyword_extern, "extern"); | ||
| 1592 | const enum_tok = try c.addToken(.keyword_enum, "enum"); | ||
| 1593 | _ = try c.addToken(.l_paren, "("); | ||
| 1594 | const arg_expr = try renderNode(c, payload.int_type); | ||
| 1595 | _ = try c.addToken(.r_paren, ")"); | ||
| 1596 | _ = try c.addToken(.l_brace, "{"); | ||
| 1597 | const members = try c.gpa.alloc(NodeIndex, std.math.max(payload.fields.len + 1, 1)); | ||
| 1598 | defer c.gpa.free(members); | ||
| 1599 | members[0] = 0; | ||
| 1600 | |||
| 1601 | for (payload.fields) |field, i| { | ||
| 1602 | const name_tok = try c.addIdentifier(field.name); | ||
| 1603 | const value_expr = if (field.value) |some| blk: { | ||
| 1604 | _ = try c.addToken(.equal, "="); | ||
| 1605 | break :blk try renderNode(c, some); | ||
| 1606 | } else 0; | ||
| 1607 | |||
| 1608 | members[i] = try c.addNode(.{ | ||
| 1609 | .tag = .container_field_init, | ||
| 1610 | .main_token = name_tok, | ||
| 1611 | .data = .{ | ||
| 1612 | .lhs = 0, | ||
| 1613 | .rhs = value_expr, | ||
| 1614 | }, | ||
| 1615 | }); | ||
| 1616 | _ = try c.addToken(.comma, ","); | ||
| 1617 | } | ||
| 1618 | // make non-exhaustive | ||
| 1619 | members[payload.fields.len] = try c.addNode(.{ | ||
| 1620 | .tag = .container_field_init, | ||
| 1621 | .main_token = try c.addIdentifier("_"), | ||
| 1622 | .data = .{ | ||
| 1623 | .lhs = 0, | ||
| 1624 | .rhs = 0, | ||
| 1625 | }, | ||
| 1626 | }); | ||
| 1627 | _ = try c.addToken(.comma, ","); | ||
| 1628 | _ = try c.addToken(.r_brace, "}"); | ||
| 1629 | |||
| 1630 | const span = try c.listToSpan(members); | ||
| 1631 | return c.addNode(.{ | ||
| 1632 | .tag = .container_decl_arg_trailing, | ||
| 1633 | .main_token = enum_tok, | ||
| 1634 | .data = .{ | ||
| 1635 | .lhs = arg_expr, | ||
| 1636 | .rhs = try c.addExtra(NodeSubRange{ | ||
| 1637 | .start = span.start, | ||
| 1638 | .end = span.end, | ||
| 1639 | }), | ||
| 1640 | }, | ||
| 1641 | }); | ||
| 1642 | }, | ||
| 1643 | .pub_enum_redecl, .enum_redecl => { | ||
| 1644 | const payload = @fieldParentPtr(Payload.EnumRedecl, "base", node.ptr_otherwise).data; | ||
| 1645 | if (node.tag() == .pub_enum_redecl) _ = try c.addToken(.keyword_pub, "pub"); | ||
| 1646 | const const_tok = try c.addToken(.keyword_const, "const"); | ||
| 1647 | _ = try c.addIdentifier(payload.enum_val_name); | ||
| 1648 | _ = try c.addToken(.equal, "="); | ||
| 1649 | |||
| 1650 | const enum_to_int_tok = try c.addToken(.builtin, "@enumToInt"); | ||
| 1651 | _ = try c.addToken(.l_paren, "("); | ||
| 1652 | const enum_name = try c.addNode(.{ | ||
| 1653 | .tag = .identifier, | ||
| 1654 | .main_token = try c.addIdentifier(payload.enum_name), | ||
| 1655 | .data = undefined, | ||
| 1656 | }); | ||
| 1657 | const field_access = try renderFieldAccess(c, enum_name, payload.field_name); | ||
| 1658 | const init_node = try c.addNode(.{ | ||
| 1659 | .tag = .builtin_call_two, | ||
| 1660 | .main_token = enum_to_int_tok, | ||
| 1661 | .data = .{ | ||
| 1662 | .lhs = field_access, | ||
| 1663 | .rhs = 0, | ||
| 1664 | }, | ||
| 1665 | }); | ||
| 1666 | _ = try c.addToken(.r_paren, ")"); | ||
| 1667 | _ = try c.addToken(.semicolon, ";"); | ||
| 1668 | |||
| 1669 | return c.addNode(.{ | ||
| 1670 | .tag = .simple_var_decl, | ||
| 1671 | .main_token = const_tok, | ||
| 1672 | .data = .{ | ||
| 1673 | .lhs = 0, | ||
| 1674 | .rhs = init_node, | ||
| 1675 | }, | ||
| 1676 | }); | ||
| 1677 | }, | ||
| 1678 | .tuple => { | ||
| 1679 | const payload = node.castTag(.tuple).?.data; | ||
| 1680 | _ = try c.addToken(.period, "."); | ||
| 1681 | const l_brace = try c.addToken(.l_brace, "{"); | ||
| 1682 | var inits = try c.gpa.alloc(NodeIndex, std.math.max(payload.len, 2)); | ||
| 1683 | defer c.gpa.free(inits); | ||
| 1684 | inits[0] = 0; | ||
| 1685 | inits[1] = 0; | ||
| 1686 | for (payload) |init, i| { | ||
| 1687 | if (i != 0) _ = try c.addToken(.comma, ","); | ||
| 1688 | inits[i] = try renderNode(c, init); | ||
| 1689 | } | ||
| 1690 | _ = try c.addToken(.r_brace, "}"); | ||
| 1691 | if (payload.len < 3) { | ||
| 1692 | return c.addNode(.{ | ||
| 1693 | .tag = .array_init_dot_two, | ||
| 1694 | .main_token = l_brace, | ||
| 1695 | .data = .{ | ||
| 1696 | .lhs = inits[0], | ||
| 1697 | .rhs = inits[1], | ||
| 1698 | }, | ||
| 1699 | }); | ||
| 1700 | } else { | ||
| 1701 | const span = try c.listToSpan(inits); | ||
| 1702 | return c.addNode(.{ | ||
| 1703 | .tag = .array_init_dot, | ||
| 1704 | .main_token = l_brace, | ||
| 1705 | .data = .{ | ||
| 1706 | .lhs = span.start, | ||
| 1707 | .rhs = span.end, | ||
| 1708 | }, | ||
| 1709 | }); | ||
| 1710 | } | ||
| 1711 | }, | ||
| 1712 | .container_init => { | ||
| 1713 | const payload = node.castTag(.container_init).?.data; | ||
| 1714 | const lhs = try renderNode(c, payload.lhs); | ||
| 1715 | |||
| 1716 | const l_brace = try c.addToken(.l_brace, "{"); | ||
| 1717 | var inits = try c.gpa.alloc(NodeIndex, std.math.max(payload.inits.len, 1)); | ||
| 1718 | defer c.gpa.free(inits); | ||
| 1719 | inits[0] = 0; | ||
| 1720 | for (payload.inits) |init, i| { | ||
| 1721 | _ = try c.addToken(.period, "."); | ||
| 1722 | _ = try c.addIdentifier(init.name); | ||
| 1723 | _ = try c.addToken(.equal, "="); | ||
| 1724 | inits[i] = try renderNode(c, init.value); | ||
| 1725 | _ = try c.addToken(.comma, ","); | ||
| 1726 | } | ||
| 1727 | _ = try c.addToken(.r_brace, "}"); | ||
| 1728 | |||
| 1729 | if (payload.inits.len < 2) { | ||
| 1730 | return c.addNode(.{ | ||
| 1731 | .tag = .struct_init_one_comma, | ||
| 1732 | .main_token = l_brace, | ||
| 1733 | .data = .{ | ||
| 1734 | .lhs = lhs, | ||
| 1735 | .rhs = inits[0], | ||
| 1736 | }, | ||
| 1737 | }); | ||
| 1738 | } else { | ||
| 1739 | const span = try c.listToSpan(inits); | ||
| 1740 | return c.addNode(.{ | ||
| 1741 | .tag = .struct_init_comma, | ||
| 1742 | .main_token = l_brace, | ||
| 1743 | .data = .{ | ||
| 1744 | .lhs = lhs, | ||
| 1745 | .rhs = try c.addExtra(NodeSubRange{ | ||
| 1746 | .start = span.start, | ||
| 1747 | .end = span.end, | ||
| 1748 | }), | ||
| 1749 | }, | ||
| 1750 | }); | ||
| 1751 | } | ||
| 1752 | }, | ||
| 1753 | .@"anytype" => unreachable, // Handled in renderParams | ||
| 1754 | } | ||
| 1755 | } | ||
| 1756 | |||
| 1757 | fn renderRecord(c: *Context, node: Node) !NodeIndex { | ||
| 1758 | const payload = @fieldParentPtr(Payload.Record, "base", node.ptr_otherwise).data; | ||
| 1759 | if (payload.is_packed) | ||
| 1760 | _ = try c.addToken(.keyword_packed, "packed") | ||
| 1761 | else | ||
| 1762 | _ = try c.addToken(.keyword_extern, "extern"); | ||
| 1763 | const kind_tok = if (node.tag() == .@"struct") | ||
| 1764 | try c.addToken(.keyword_struct, "struct") | ||
| 1765 | else | ||
| 1766 | try c.addToken(.keyword_union, "union"); | ||
| 1767 | |||
| 1768 | _ = try c.addToken(.l_brace, "{"); | ||
| 1769 | const members = try c.gpa.alloc(NodeIndex, std.math.max(payload.fields.len, 2)); | ||
| 1770 | defer c.gpa.free(members); | ||
| 1771 | members[0] = 0; | ||
| 1772 | members[1] = 0; | ||
| 1773 | |||
| 1774 | for (payload.fields) |field, i| { | ||
| 1775 | const name_tok = try c.addIdentifier(field.name); | ||
| 1776 | _ = try c.addToken(.colon, ":"); | ||
| 1777 | const type_expr = try renderNode(c, field.type); | ||
| 1778 | |||
| 1779 | const alignment = field.alignment orelse { | ||
| 1780 | members[i] = try c.addNode(.{ | ||
| 1781 | .tag = .container_field_init, | ||
| 1782 | .main_token = name_tok, | ||
| 1783 | .data = .{ | ||
| 1784 | .lhs = type_expr, | ||
| 1785 | .rhs = 0, | ||
| 1786 | }, | ||
| 1787 | }); | ||
| 1788 | _ = try c.addToken(.comma, ","); | ||
| 1789 | continue; | ||
| 1790 | }; | ||
| 1791 | _ = try c.addToken(.keyword_align, "align"); | ||
| 1792 | _ = try c.addToken(.l_paren, "("); | ||
| 1793 | const align_expr = try c.addNode(.{ | ||
| 1794 | .tag = .integer_literal, | ||
| 1795 | .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{alignment}), | ||
| 1796 | .data = undefined, | ||
| 1797 | }); | ||
| 1798 | _ = try c.addToken(.r_paren, ")"); | ||
| 1799 | |||
| 1800 | members[i] = try c.addNode(.{ | ||
| 1801 | .tag = .container_field_align, | ||
| 1802 | .main_token = name_tok, | ||
| 1803 | .data = .{ | ||
| 1804 | .lhs = type_expr, | ||
| 1805 | .rhs = align_expr, | ||
| 1806 | }, | ||
| 1807 | }); | ||
| 1808 | _ = try c.addToken(.comma, ","); | ||
| 1809 | } | ||
| 1810 | _ = try c.addToken(.r_brace, "}"); | ||
| 1811 | |||
| 1812 | if (payload.fields.len == 0) { | ||
| 1813 | return c.addNode(.{ | ||
| 1814 | .tag = .container_decl_two, | ||
| 1815 | .main_token = kind_tok, | ||
| 1816 | .data = .{ | ||
| 1817 | .lhs = 0, | ||
| 1818 | .rhs = 0, | ||
| 1819 | }, | ||
| 1820 | }); | ||
| 1821 | } else if (payload.fields.len <= 2) { | ||
| 1822 | return c.addNode(.{ | ||
| 1823 | .tag = .container_decl_two_trailing, | ||
| 1824 | .main_token = kind_tok, | ||
| 1825 | .data = .{ | ||
| 1826 | .lhs = members[0], | ||
| 1827 | .rhs = members[1], | ||
| 1828 | }, | ||
| 1829 | }); | ||
| 1830 | } else { | ||
| 1831 | const span = try c.listToSpan(members); | ||
| 1832 | return c.addNode(.{ | ||
| 1833 | .tag = .container_decl_trailing, | ||
| 1834 | .main_token = kind_tok, | ||
| 1835 | .data = .{ | ||
| 1836 | .lhs = span.start, | ||
| 1837 | .rhs = span.end, | ||
| 1838 | }, | ||
| 1839 | }); | ||
| 1840 | } | ||
| 1841 | } | ||
| 1842 | |||
| 1843 | fn renderFieldAccess(c: *Context, lhs: NodeIndex, field_name: []const u8) !NodeIndex { | ||
| 1844 | return c.addNode(.{ | ||
| 1845 | .tag = .field_access, | ||
| 1846 | .main_token = try c.addToken(.period, "."), | ||
| 1847 | .data = .{ | ||
| 1848 | .lhs = lhs, | ||
| 1849 | .rhs = try c.addIdentifier(field_name), | ||
| 1850 | }, | ||
| 1851 | }); | ||
| 1852 | } | ||
| 1853 | |||
| 1854 | fn renderArrayInit(c: *Context, lhs: NodeIndex, inits: []const Node) !NodeIndex { | ||
| 1855 | const l_brace = try c.addToken(.l_brace, "{"); | ||
| 1856 | var rendered = try c.gpa.alloc(NodeIndex, std.math.max(inits.len, 1)); | ||
| 1857 | defer c.gpa.free(rendered); | ||
| 1858 | rendered[0] = 0; | ||
| 1859 | for (inits) |init, i| { | ||
| 1860 | rendered[i] = try renderNode(c, init); | ||
| 1861 | _ = try c.addToken(.comma, ","); | ||
| 1862 | } | ||
| 1863 | _ = try c.addToken(.r_brace, "}"); | ||
| 1864 | if (inits.len < 2) { | ||
| 1865 | return c.addNode(.{ | ||
| 1866 | .tag = .array_init_one_comma, | ||
| 1867 | .main_token = l_brace, | ||
| 1868 | .data = .{ | ||
| 1869 | .lhs = lhs, | ||
| 1870 | .rhs = rendered[0], | ||
| 1871 | }, | ||
| 1872 | }); | ||
| 1873 | } else { | ||
| 1874 | const span = try c.listToSpan(rendered); | ||
| 1875 | return c.addNode(.{ | ||
| 1876 | .tag = .array_init_comma, | ||
| 1877 | .main_token = l_brace, | ||
| 1878 | .data = .{ | ||
| 1879 | .lhs = lhs, | ||
| 1880 | .rhs = try c.addExtra(NodeSubRange{ | ||
| 1881 | .start = span.start, | ||
| 1882 | .end = span.end, | ||
| 1883 | }), | ||
| 1884 | }, | ||
| 1885 | }); | ||
| 1886 | } | ||
| 1887 | } | ||
| 1888 | |||
| 1889 | fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex { | ||
| 1890 | const l_bracket = try c.addToken(.l_bracket, "["); | ||
| 1891 | const len_expr = try c.addNode(.{ | ||
| 1892 | .tag = .integer_literal, | ||
| 1893 | .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{len}), | ||
| 1894 | .data = undefined, | ||
| 1895 | }); | ||
| 1896 | _ = try c.addToken(.r_bracket, "]"); | ||
| 1897 | const elem_type_expr = try renderNode(c, elem_type); | ||
| 1898 | return c.addNode(.{ | ||
| 1899 | .tag = .array_type, | ||
| 1900 | .main_token = l_bracket, | ||
| 1901 | .data = .{ | ||
| 1902 | .lhs = len_expr, | ||
| 1903 | .rhs = elem_type_expr, | ||
| 1904 | }, | ||
| 1905 | }); | ||
| 1906 | } | ||
| 1907 | |||
| 1908 | fn addSemicolonIfNeeded(c: *Context, node: Node) !void { | ||
| 1909 | switch (node.tag()) { | ||
| 1910 | .warning => unreachable, | ||
| 1911 | .var_decl, .var_simple, .arg_redecl, .alias, .enum_redecl, .block, .empty_block, .block_single, .@"switch" => {}, | ||
| 1912 | .while_true => { | ||
| 1913 | const payload = node.castTag(.while_true).?.data; | ||
| 1914 | return addSemicolonIfNotBlock(c, payload); | ||
| 1915 | }, | ||
| 1916 | .@"while" => { | ||
| 1917 | const payload = node.castTag(.@"while").?.data; | ||
| 1918 | return addSemicolonIfNotBlock(c, payload.body); | ||
| 1919 | }, | ||
| 1920 | .@"if" => { | ||
| 1921 | const payload = node.castTag(.@"if").?.data; | ||
| 1922 | if (payload.@"else") |some| | ||
| 1923 | return addSemicolonIfNeeded(c, some); | ||
| 1924 | return addSemicolonIfNotBlock(c, payload.then); | ||
| 1925 | }, | ||
| 1926 | else => _ = try c.addToken(.semicolon, ";"), | ||
| 1927 | } | ||
| 1928 | } | ||
| 1929 | |||
| 1930 | fn addSemicolonIfNotBlock(c: *Context, node: Node) !void { | ||
| 1931 | switch (node.tag()) { | ||
| 1932 | .block, .empty_block, .block_single => {}, | ||
| 1933 | else => _ = try c.addToken(.semicolon, ";"), | ||
| 1934 | } | ||
| 1935 | } | ||
| 1936 | |||
| 1937 | fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { | ||
| 1938 | switch (node.tag()) { | ||
| 1939 | .declaration => unreachable, | ||
| 1940 | .null_literal, | ||
| 1941 | .undefined_literal, | ||
| 1942 | .true_literal, | ||
| 1943 | .false_literal, | ||
| 1944 | .return_void, | ||
| 1945 | .zero_literal, | ||
| 1946 | .one_literal, | ||
| 1947 | .void_type, | ||
| 1948 | .noreturn_type, | ||
| 1949 | .@"anytype", | ||
| 1950 | .div_trunc, | ||
| 1951 | .rem, | ||
| 1952 | .int_cast, | ||
| 1953 | .as, | ||
| 1954 | .truncate, | ||
| 1955 | .bit_cast, | ||
| 1956 | .float_cast, | ||
| 1957 | .float_to_int, | ||
| 1958 | .int_to_float, | ||
| 1959 | .int_to_enum, | ||
| 1960 | .int_to_ptr, | ||
| 1961 | .std_mem_zeroes, | ||
| 1962 | .std_math_Log2Int, | ||
| 1963 | .log2_int_type, | ||
| 1964 | .ptr_to_int, | ||
| 1965 | .enum_to_int, | ||
| 1966 | .sizeof, | ||
| 1967 | .alignof, | ||
| 1968 | .typeof, | ||
| 1969 | .std_meta_sizeof, | ||
| 1970 | .std_meta_cast, | ||
| 1971 | .std_mem_zeroinit, | ||
| 1972 | .integer_literal, | ||
| 1973 | .float_literal, | ||
| 1974 | .string_literal, | ||
| 1975 | .char_literal, | ||
| 1976 | .identifier, | ||
| 1977 | .field_access, | ||
| 1978 | .ptr_cast, | ||
| 1979 | .type, | ||
| 1980 | .array_access, | ||
| 1981 | .align_cast, | ||
| 1982 | .optional_type, | ||
| 1983 | .c_pointer, | ||
| 1984 | .single_pointer, | ||
| 1985 | .unwrap, | ||
| 1986 | .deref, | ||
| 1987 | .address_of, | ||
| 1988 | .not, | ||
| 1989 | .negate, | ||
| 1990 | .negate_wrap, | ||
| 1991 | .bit_not, | ||
| 1992 | .func, | ||
| 1993 | .call, | ||
| 1994 | .array_type, | ||
| 1995 | .bool_to_int, | ||
| 1996 | => { | ||
| 1997 | // no grouping needed | ||
| 1998 | return renderNode(c, node); | ||
| 1999 | }, | ||
| 2000 | |||
| 2001 | .opaque_literal, | ||
| 2002 | .empty_array, | ||
| 2003 | .block_single, | ||
| 2004 | .add, | ||
| 2005 | .add_wrap, | ||
| 2006 | .sub, | ||
| 2007 | .sub_wrap, | ||
| 2008 | .mul, | ||
| 2009 | .mul_wrap, | ||
| 2010 | .div, | ||
| 2011 | .shl, | ||
| 2012 | .shr, | ||
| 2013 | .mod, | ||
| 2014 | .@"and", | ||
| 2015 | .@"or", | ||
| 2016 | .less_than, | ||
| 2017 | .less_than_equal, | ||
| 2018 | .greater_than, | ||
| 2019 | .greater_than_equal, | ||
| 2020 | .equal, | ||
| 2021 | .not_equal, | ||
| 2022 | .bit_and, | ||
| 2023 | .bit_or, | ||
| 2024 | .bit_xor, | ||
| 2025 | .empty_block, | ||
| 2026 | .array_cat, | ||
| 2027 | .array_filler, | ||
| 2028 | .@"if", | ||
| 2029 | .@"enum", | ||
| 2030 | .@"struct", | ||
| 2031 | .@"union", | ||
| 2032 | .array_init, | ||
| 2033 | .tuple, | ||
| 2034 | .container_init, | ||
| 2035 | .block, | ||
| 2036 | => return c.addNode(.{ | ||
| 2037 | .tag = .grouped_expression, | ||
| 2038 | .main_token = try c.addToken(.l_paren, "("), | ||
| 2039 | .data = .{ | ||
| 2040 | .lhs = try renderNode(c, node), | ||
| 2041 | .rhs = try c.addToken(.r_paren, ")"), | ||
| 2042 | }, | ||
| 2043 | }), | ||
| 2044 | .ellipsis3, | ||
| 2045 | .switch_prong, | ||
| 2046 | .warning, | ||
| 2047 | .var_decl, | ||
| 2048 | .fail_decl, | ||
| 2049 | .arg_redecl, | ||
| 2050 | .alias, | ||
| 2051 | .var_simple, | ||
| 2052 | .pub_var_simple, | ||
| 2053 | .pub_enum_redecl, | ||
| 2054 | .enum_redecl, | ||
| 2055 | .@"while", | ||
| 2056 | .@"switch", | ||
| 2057 | .@"break", | ||
| 2058 | .break_val, | ||
| 2059 | .pub_inline_fn, | ||
| 2060 | .discard, | ||
| 2061 | .@"continue", | ||
| 2062 | .@"return", | ||
| 2063 | .usingnamespace_builtins, | ||
| 2064 | .while_true, | ||
| 2065 | .if_not_break, | ||
| 2066 | .switch_else, | ||
| 2067 | .add_assign, | ||
| 2068 | .add_wrap_assign, | ||
| 2069 | .sub_assign, | ||
| 2070 | .sub_wrap_assign, | ||
| 2071 | .mul_assign, | ||
| 2072 | .mul_wrap_assign, | ||
| 2073 | .div_assign, | ||
| 2074 | .shl_assign, | ||
| 2075 | .shr_assign, | ||
| 2076 | .mod_assign, | ||
| 2077 | .bit_and_assign, | ||
| 2078 | .bit_or_assign, | ||
| 2079 | .bit_xor_assign, | ||
| 2080 | .assign, | ||
| 2081 | => { | ||
| 2082 | // these should never appear in places where grouping might be needed. | ||
| 2083 | unreachable; | ||
| 2084 | }, | ||
| 2085 | } | ||
| 2086 | } | ||
| 2087 | |||
| 2088 | fn renderPrefixOp(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex { | ||
| 2089 | const payload = @fieldParentPtr(Payload.UnOp, "base", node.ptr_otherwise).data; | ||
| 2090 | return c.addNode(.{ | ||
| 2091 | .tag = tag, | ||
| 2092 | .main_token = try c.addToken(tok_tag, bytes), | ||
| 2093 | .data = .{ | ||
| 2094 | .lhs = try renderNodeGrouped(c, payload), | ||
| 2095 | .rhs = undefined, | ||
| 2096 | }, | ||
| 2097 | }); | ||
| 2098 | } | ||
| 2099 | |||
| 2100 | fn renderBinOpGrouped(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex { | ||
| 2101 | const payload = @fieldParentPtr(Payload.BinOp, "base", node.ptr_otherwise).data; | ||
| 2102 | const lhs = try renderNodeGrouped(c, payload.lhs); | ||
| 2103 | return c.addNode(.{ | ||
| 2104 | .tag = tag, | ||
| 2105 | .main_token = try c.addToken(tok_tag, bytes), | ||
| 2106 | .data = .{ | ||
| 2107 | .lhs = lhs, | ||
| 2108 | .rhs = try renderNodeGrouped(c, payload.rhs), | ||
| 2109 | }, | ||
| 2110 | }); | ||
| 2111 | } | ||
| 2112 | |||
| 2113 | fn renderBinOp(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex { | ||
| 2114 | const payload = @fieldParentPtr(Payload.BinOp, "base", node.ptr_otherwise).data; | ||
| 2115 | const lhs = try renderNode(c, payload.lhs); | ||
| 2116 | return c.addNode(.{ | ||
| 2117 | .tag = tag, | ||
| 2118 | .main_token = try c.addToken(tok_tag, bytes), | ||
| 2119 | .data = .{ | ||
| 2120 | .lhs = lhs, | ||
| 2121 | .rhs = try renderNode(c, payload.rhs), | ||
| 2122 | }, | ||
| 2123 | }); | ||
| 2124 | } | ||
| 2125 | |||
| 2126 | fn renderStdImport(c: *Context, first: []const u8, second: []const u8) !NodeIndex { | ||
| 2127 | const import_tok = try c.addToken(.builtin, "@import"); | ||
| 2128 | _ = try c.addToken(.l_paren, "("); | ||
| 2129 | const std_tok = try c.addToken(.string_literal, "\"std\""); | ||
| 2130 | const std_node = try c.addNode(.{ | ||
| 2131 | .tag = .string_literal, | ||
| 2132 | .main_token = std_tok, | ||
| 2133 | .data = undefined, | ||
| 2134 | }); | ||
| 2135 | _ = try c.addToken(.r_paren, ")"); | ||
| 2136 | |||
| 2137 | const import_node = try c.addNode(.{ | ||
| 2138 | .tag = .builtin_call_two, | ||
| 2139 | .main_token = import_tok, | ||
| 2140 | .data = .{ | ||
| 2141 | .lhs = std_node, | ||
| 2142 | .rhs = 0, | ||
| 2143 | }, | ||
| 2144 | }); | ||
| 2145 | |||
| 2146 | var access_chain = import_node; | ||
| 2147 | access_chain = try renderFieldAccess(c, access_chain, first); | ||
| 2148 | access_chain = try renderFieldAccess(c, access_chain, second); | ||
| 2149 | return access_chain; | ||
| 2150 | } | ||
| 2151 | |||
| 2152 | fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex { | ||
| 2153 | const lparen = try c.addToken(.l_paren, "("); | ||
| 2154 | const res = switch (args.len) { | ||
| 2155 | 0 => try c.addNode(.{ | ||
| 2156 | .tag = .call_one, | ||
| 2157 | .main_token = lparen, | ||
| 2158 | .data = .{ | ||
| 2159 | .lhs = lhs, | ||
| 2160 | .rhs = 0, | ||
| 2161 | }, | ||
| 2162 | }), | ||
| 2163 | 1 => blk: { | ||
| 2164 | const arg = try renderNode(c, args[0]); | ||
| 2165 | break :blk try c.addNode(.{ | ||
| 2166 | .tag = .call_one, | ||
| 2167 | .main_token = lparen, | ||
| 2168 | .data = .{ | ||
| 2169 | .lhs = lhs, | ||
| 2170 | .rhs = arg, | ||
| 2171 | }, | ||
| 2172 | }); | ||
| 2173 | }, | ||
| 2174 | else => blk: { | ||
| 2175 | var rendered = try c.gpa.alloc(NodeIndex, args.len); | ||
| 2176 | defer c.gpa.free(rendered); | ||
| 2177 | |||
| 2178 | for (args) |arg, i| { | ||
| 2179 | if (i != 0) _ = try c.addToken(.comma, ","); | ||
| 2180 | rendered[i] = try renderNode(c, arg); | ||
| 2181 | } | ||
| 2182 | const span = try c.listToSpan(rendered); | ||
| 2183 | break :blk try c.addNode(.{ | ||
| 2184 | .tag = .call, | ||
| 2185 | .main_token = lparen, | ||
| 2186 | .data = .{ | ||
| 2187 | .lhs = lhs, | ||
| 2188 | .rhs = try c.addExtra(NodeSubRange{ | ||
| 2189 | .start = span.start, | ||
| 2190 | .end = span.end, | ||
| 2191 | }), | ||
| 2192 | }, | ||
| 2193 | }); | ||
| 2194 | }, | ||
| 2195 | }; | ||
| 2196 | _ = try c.addToken(.r_paren, ")"); | ||
| 2197 | return res; | ||
| 2198 | } | ||
| 2199 | |||
| 2200 | fn renderBuiltinCall(c: *Context, builtin: []const u8, args: []const Node) !NodeIndex { | ||
| 2201 | const builtin_tok = try c.addToken(.builtin, builtin); | ||
| 2202 | _ = try c.addToken(.l_paren, "("); | ||
| 2203 | var arg_1: NodeIndex = 0; | ||
| 2204 | var arg_2: NodeIndex = 0; | ||
| 2205 | switch (args.len) { | ||
| 2206 | 0 => {}, | ||
| 2207 | 1 => { | ||
| 2208 | arg_1 = try renderNode(c, args[0]); | ||
| 2209 | }, | ||
| 2210 | 2 => { | ||
| 2211 | arg_1 = try renderNode(c, args[0]); | ||
| 2212 | _ = try c.addToken(.comma, ","); | ||
| 2213 | arg_2 = try renderNode(c, args[1]); | ||
| 2214 | }, | ||
| 2215 | else => unreachable, // expand this function as needed. | ||
| 2216 | } | ||
| 2217 | |||
| 2218 | _ = try c.addToken(.r_paren, ")"); | ||
| 2219 | return c.addNode(.{ | ||
| 2220 | .tag = .builtin_call_two, | ||
| 2221 | .main_token = builtin_tok, | ||
| 2222 | .data = .{ | ||
| 2223 | .lhs = arg_1, | ||
| 2224 | .rhs = arg_2, | ||
| 2225 | }, | ||
| 2226 | }); | ||
| 2227 | } | ||
| 2228 | |||
| 2229 | fn renderVar(c: *Context, node: Node) !NodeIndex { | ||
| 2230 | const payload = node.castTag(.var_decl).?.data; | ||
| 2231 | if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub"); | ||
| 2232 | if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern"); | ||
| 2233 | if (payload.is_export) _ = try c.addToken(.keyword_export, "export"); | ||
| 2234 | if (payload.is_threadlocal) _ = try c.addToken(.keyword_threadlocal, "threadlocal"); | ||
| 2235 | const mut_tok = if (payload.is_const) | ||
| 2236 | try c.addToken(.keyword_const, "const") | ||
| 2237 | else | ||
| 2238 | try c.addToken(.keyword_var, "var"); | ||
| 2239 | _ = try c.addIdentifier(payload.name); | ||
| 2240 | _ = try c.addToken(.colon, ":"); | ||
| 2241 | const type_node = try renderNode(c, payload.type); | ||
| 2242 | |||
| 2243 | const align_node = if (payload.alignment) |some| blk: { | ||
| 2244 | _ = try c.addToken(.keyword_align, "align"); | ||
| 2245 | _ = try c.addToken(.l_paren, "("); | ||
| 2246 | const res = try c.addNode(.{ | ||
| 2247 | .tag = .integer_literal, | ||
| 2248 | .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{some}), | ||
| 2249 | .data = undefined, | ||
| 2250 | }); | ||
| 2251 | _ = try c.addToken(.r_paren, ")"); | ||
| 2252 | break :blk res; | ||
| 2253 | } else 0; | ||
| 2254 | |||
| 2255 | const section_node = if (payload.linksection_string) |some| blk: { | ||
| 2256 | _ = try c.addToken(.keyword_linksection, "linksection"); | ||
| 2257 | _ = try c.addToken(.l_paren, "("); | ||
| 2258 | const res = try c.addNode(.{ | ||
| 2259 | .tag = .string_literal, | ||
| 2260 | .main_token = try c.addTokenFmt(.string_literal, "\"{s}\"", .{std.zig.fmtEscapes(some)}), | ||
| 2261 | .data = undefined, | ||
| 2262 | }); | ||
| 2263 | _ = try c.addToken(.r_paren, ")"); | ||
| 2264 | break :blk res; | ||
| 2265 | } else 0; | ||
| 2266 | |||
| 2267 | const init_node = if (payload.init) |some| blk: { | ||
| 2268 | _ = try c.addToken(.equal, "="); | ||
| 2269 | break :blk try renderNode(c, some); | ||
| 2270 | } else 0; | ||
| 2271 | _ = try c.addToken(.semicolon, ";"); | ||
| 2272 | |||
| 2273 | if (section_node == 0) { | ||
| 2274 | if (align_node == 0) { | ||
| 2275 | return c.addNode(.{ | ||
| 2276 | .tag = .simple_var_decl, | ||
| 2277 | .main_token = mut_tok, | ||
| 2278 | .data = .{ | ||
| 2279 | .lhs = type_node, | ||
| 2280 | .rhs = init_node, | ||
| 2281 | }, | ||
| 2282 | }); | ||
| 2283 | } else { | ||
| 2284 | return c.addNode(.{ | ||
| 2285 | .tag = .local_var_decl, | ||
| 2286 | .main_token = mut_tok, | ||
| 2287 | .data = .{ | ||
| 2288 | .lhs = try c.addExtra(std.zig.ast.Node.LocalVarDecl{ | ||
| 2289 | .type_node = type_node, | ||
| 2290 | .align_node = align_node, | ||
| 2291 | }), | ||
| 2292 | .rhs = init_node, | ||
| 2293 | }, | ||
| 2294 | }); | ||
| 2295 | } | ||
| 2296 | } else { | ||
| 2297 | return c.addNode(.{ | ||
| 2298 | .tag = .global_var_decl, | ||
| 2299 | .main_token = mut_tok, | ||
| 2300 | .data = .{ | ||
| 2301 | .lhs = try c.addExtra(std.zig.ast.Node.GlobalVarDecl{ | ||
| 2302 | .type_node = type_node, | ||
| 2303 | .align_node = align_node, | ||
| 2304 | .section_node = section_node, | ||
| 2305 | }), | ||
| 2306 | .rhs = init_node, | ||
| 2307 | }, | ||
| 2308 | }); | ||
| 2309 | } | ||
| 2310 | } | ||
| 2311 | |||
| 2312 | fn renderFunc(c: *Context, node: Node) !NodeIndex { | ||
| 2313 | const payload = node.castTag(.func).?.data; | ||
| 2314 | if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub"); | ||
| 2315 | if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern"); | ||
| 2316 | if (payload.is_export) _ = try c.addToken(.keyword_export, "export"); | ||
| 2317 | const fn_token = try c.addToken(.keyword_fn, "fn"); | ||
| 2318 | if (payload.name) |some| _ = try c.addIdentifier(some); | ||
| 2319 | |||
| 2320 | const params = try renderParams(c, payload.params, payload.is_var_args); | ||
| 2321 | defer params.deinit(); | ||
| 2322 | var span: NodeSubRange = undefined; | ||
| 2323 | if (params.items.len > 1) span = try c.listToSpan(params.items); | ||
| 2324 | |||
| 2325 | const align_expr = if (payload.alignment) |some| blk: { | ||
| 2326 | _ = try c.addToken(.keyword_align, "align"); | ||
| 2327 | _ = try c.addToken(.l_paren, "("); | ||
| 2328 | const res = try c.addNode(.{ | ||
| 2329 | .tag = .integer_literal, | ||
| 2330 | .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{some}), | ||
| 2331 | .data = undefined, | ||
| 2332 | }); | ||
| 2333 | _ = try c.addToken(.r_paren, ")"); | ||
| 2334 | break :blk res; | ||
| 2335 | } else 0; | ||
| 2336 | |||
| 2337 | const section_expr = if (payload.linksection_string) |some| blk: { | ||
| 2338 | _ = try c.addToken(.keyword_linksection, "linksection"); | ||
| 2339 | _ = try c.addToken(.l_paren, "("); | ||
| 2340 | const res = try c.addNode(.{ | ||
| 2341 | .tag = .string_literal, | ||
| 2342 | .main_token = try c.addTokenFmt(.string_literal, "\"{s}\"", .{std.zig.fmtEscapes(some)}), | ||
| 2343 | .data = undefined, | ||
| 2344 | }); | ||
| 2345 | _ = try c.addToken(.r_paren, ")"); | ||
| 2346 | break :blk res; | ||
| 2347 | } else 0; | ||
| 2348 | |||
| 2349 | const callconv_expr = if (payload.explicit_callconv) |some| blk: { | ||
| 2350 | _ = try c.addToken(.keyword_callconv, "callconv"); | ||
| 2351 | _ = try c.addToken(.l_paren, "("); | ||
| 2352 | _ = try c.addToken(.period, "."); | ||
| 2353 | const res = try c.addNode(.{ | ||
| 2354 | .tag = .enum_literal, | ||
| 2355 | .main_token = try c.addTokenFmt(.identifier, "{s}", .{@tagName(some)}), | ||
| 2356 | .data = undefined, | ||
| 2357 | }); | ||
| 2358 | _ = try c.addToken(.r_paren, ")"); | ||
| 2359 | break :blk res; | ||
| 2360 | } else 0; | ||
| 2361 | |||
| 2362 | const return_type_expr = try renderNode(c, payload.return_type); | ||
| 2363 | |||
| 2364 | const fn_proto = try blk: { | ||
| 2365 | if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) { | ||
| 2366 | if (params.items.len < 2) | ||
| 2367 | break :blk c.addNode(.{ | ||
| 2368 | .tag = .fn_proto_simple, | ||
| 2369 | .main_token = fn_token, | ||
| 2370 | .data = .{ | ||
| 2371 | .lhs = params.items[0], | ||
| 2372 | .rhs = return_type_expr, | ||
| 2373 | }, | ||
| 2374 | }) | ||
| 2375 | else | ||
| 2376 | break :blk c.addNode(.{ | ||
| 2377 | .tag = .fn_proto_multi, | ||
| 2378 | .main_token = fn_token, | ||
| 2379 | .data = .{ | ||
| 2380 | .lhs = try c.addExtra(NodeSubRange{ | ||
| 2381 | .start = span.start, | ||
| 2382 | .end = span.end, | ||
| 2383 | }), | ||
| 2384 | .rhs = return_type_expr, | ||
| 2385 | }, | ||
| 2386 | }); | ||
| 2387 | } | ||
| 2388 | if (params.items.len < 2) | ||
| 2389 | break :blk c.addNode(.{ | ||
| 2390 | .tag = .fn_proto_one, | ||
| 2391 | .main_token = fn_token, | ||
| 2392 | .data = .{ | ||
| 2393 | .lhs = try c.addExtra(std.zig.ast.Node.FnProtoOne{ | ||
| 2394 | .param = params.items[0], | ||
| 2395 | .align_expr = align_expr, | ||
| 2396 | .section_expr = section_expr, | ||
| 2397 | .callconv_expr = callconv_expr, | ||
| 2398 | }), | ||
| 2399 | .rhs = return_type_expr, | ||
| 2400 | }, | ||
| 2401 | }) | ||
| 2402 | else | ||
| 2403 | break :blk c.addNode(.{ | ||
| 2404 | .tag = .fn_proto, | ||
| 2405 | .main_token = fn_token, | ||
| 2406 | .data = .{ | ||
| 2407 | .lhs = try c.addExtra(std.zig.ast.Node.FnProto{ | ||
| 2408 | .params_start = span.start, | ||
| 2409 | .params_end = span.end, | ||
| 2410 | .align_expr = align_expr, | ||
| 2411 | .section_expr = section_expr, | ||
| 2412 | .callconv_expr = callconv_expr, | ||
| 2413 | }), | ||
| 2414 | .rhs = return_type_expr, | ||
| 2415 | }, | ||
| 2416 | }); | ||
| 2417 | }; | ||
| 2418 | |||
| 2419 | const payload_body = payload.body orelse { | ||
| 2420 | if (payload.is_extern) { | ||
| 2421 | _ = try c.addToken(.semicolon, ";"); | ||
| 2422 | } | ||
| 2423 | return fn_proto; | ||
| 2424 | }; | ||
| 2425 | const body = try renderNode(c, payload_body); | ||
| 2426 | return c.addNode(.{ | ||
| 2427 | .tag = .fn_decl, | ||
| 2428 | .main_token = fn_token, | ||
| 2429 | .data = .{ | ||
| 2430 | .lhs = fn_proto, | ||
| 2431 | .rhs = body, | ||
| 2432 | }, | ||
| 2433 | }); | ||
| 2434 | } | ||
| 2435 | |||
| 2436 | fn renderMacroFunc(c: *Context, node: Node) !NodeIndex { | ||
| 2437 | const payload = node.castTag(.pub_inline_fn).?.data; | ||
| 2438 | _ = try c.addToken(.keyword_pub, "pub"); | ||
| 2439 | const fn_token = try c.addToken(.keyword_fn, "fn"); | ||
| 2440 | _ = try c.addIdentifier(payload.name); | ||
| 2441 | |||
| 2442 | const params = try renderParams(c, payload.params, false); | ||
| 2443 | defer params.deinit(); | ||
| 2444 | var span: NodeSubRange = undefined; | ||
| 2445 | if (params.items.len > 1) span = try c.listToSpan(params.items); | ||
| 2446 | |||
| 2447 | const callconv_expr = blk: { | ||
| 2448 | _ = try c.addToken(.keyword_callconv, "callconv"); | ||
| 2449 | _ = try c.addToken(.l_paren, "("); | ||
| 2450 | _ = try c.addToken(.period, "."); | ||
| 2451 | const res = try c.addNode(.{ | ||
| 2452 | .tag = .enum_literal, | ||
| 2453 | .main_token = try c.addToken(.identifier, "Inline"), | ||
| 2454 | .data = undefined, | ||
| 2455 | }); | ||
| 2456 | _ = try c.addToken(.r_paren, ")"); | ||
| 2457 | break :blk res; | ||
| 2458 | }; | ||
| 2459 | const return_type_expr = try renderNodeGrouped(c, payload.return_type); | ||
| 2460 | |||
| 2461 | const fn_proto = try blk: { | ||
| 2462 | if (params.items.len < 2) | ||
| 2463 | break :blk c.addNode(.{ | ||
| 2464 | .tag = .fn_proto_one, | ||
| 2465 | .main_token = fn_token, | ||
| 2466 | .data = .{ | ||
| 2467 | .lhs = try c.addExtra(std.zig.ast.Node.FnProtoOne{ | ||
| 2468 | .param = params.items[0], | ||
| 2469 | .align_expr = 0, | ||
| 2470 | .section_expr = 0, | ||
| 2471 | .callconv_expr = callconv_expr, | ||
| 2472 | }), | ||
| 2473 | .rhs = return_type_expr, | ||
| 2474 | }, | ||
| 2475 | }) | ||
| 2476 | else | ||
| 2477 | break :blk c.addNode(.{ | ||
| 2478 | .tag = .fn_proto, | ||
| 2479 | .main_token = fn_token, | ||
| 2480 | .data = .{ | ||
| 2481 | .lhs = try c.addExtra(std.zig.ast.Node.FnProto{ | ||
| 2482 | .params_start = span.start, | ||
| 2483 | .params_end = span.end, | ||
| 2484 | .align_expr = 0, | ||
| 2485 | .section_expr = 0, | ||
| 2486 | .callconv_expr = callconv_expr, | ||
| 2487 | }), | ||
| 2488 | .rhs = return_type_expr, | ||
| 2489 | }, | ||
| 2490 | }); | ||
| 2491 | }; | ||
| 2492 | return c.addNode(.{ | ||
| 2493 | .tag = .fn_decl, | ||
| 2494 | .main_token = fn_token, | ||
| 2495 | .data = .{ | ||
| 2496 | .lhs = fn_proto, | ||
| 2497 | .rhs = try renderNode(c, payload.body), | ||
| 2498 | }, | ||
| 2499 | }); | ||
| 2500 | } | ||
| 2501 | |||
| 2502 | fn renderParams(c: *Context, params: []Payload.Param, is_var_args: bool) !std.ArrayList(NodeIndex) { | ||
| 2503 | _ = try c.addToken(.l_paren, "("); | ||
| 2504 | var rendered = std.ArrayList(NodeIndex).init(c.gpa); | ||
| 2505 | errdefer rendered.deinit(); | ||
| 2506 | try rendered.ensureCapacity(std.math.max(params.len, 1)); | ||
| 2507 | |||
| 2508 | for (params) |param, i| { | ||
| 2509 | if (i != 0) _ = try c.addToken(.comma, ","); | ||
| 2510 | if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias"); | ||
| 2511 | if (param.name) |some| { | ||
| 2512 | _ = try c.addIdentifier(some); | ||
| 2513 | _ = try c.addToken(.colon, ":"); | ||
| 2514 | } | ||
| 2515 | if (param.type.tag() == .@"anytype") { | ||
| 2516 | _ = try c.addToken(.keyword_anytype, "anytype"); | ||
| 2517 | continue; | ||
| 2518 | } | ||
| 2519 | rendered.appendAssumeCapacity(try renderNode(c, param.type)); | ||
| 2520 | } | ||
| 2521 | if (is_var_args) { | ||
| 2522 | if (params.len != 0) _ = try c.addToken(.comma, ","); | ||
| 2523 | _ = try c.addToken(.ellipsis3, "..."); | ||
| 2524 | } | ||
| 2525 | _ = try c.addToken(.r_paren, ")"); | ||
| 2526 | |||
| 2527 | if (rendered.items.len == 0) rendered.appendAssumeCapacity(0); | ||
| 2528 | return rendered; | ||
| 2529 | } | ||
src/type.zig+65| ... | @@ -28,6 +28,8 @@ pub const Type = extern union { | ... | @@ -28,6 +28,8 @@ pub const Type = extern union { |
| 28 | .i32, | 28 | .i32, |
| 29 | .u64, | 29 | .u64, |
| 30 | .i64, | 30 | .i64, |
| 31 | .u128, | ||
| 32 | .i128, | ||
| 31 | .usize, | 33 | .usize, |
| 32 | .isize, | 34 | .isize, |
| 33 | .c_short, | 35 | .c_short, |
| ... | @@ -357,6 +359,8 @@ pub const Type = extern union { | ... | @@ -357,6 +359,8 @@ pub const Type = extern union { |
| 357 | .i32, | 359 | .i32, |
| 358 | .u64, | 360 | .u64, |
| 359 | .i64, | 361 | .i64, |
| 362 | .u128, | ||
| 363 | .i128, | ||
| 360 | .usize, | 364 | .usize, |
| 361 | .isize, | 365 | .isize, |
| 362 | .c_short, | 366 | .c_short, |
| ... | @@ -506,6 +510,8 @@ pub const Type = extern union { | ... | @@ -506,6 +510,8 @@ pub const Type = extern union { |
| 506 | .i32, | 510 | .i32, |
| 507 | .u64, | 511 | .u64, |
| 508 | .i64, | 512 | .i64, |
| 513 | .u128, | ||
| 514 | .i128, | ||
| 509 | .usize, | 515 | .usize, |
| 510 | .isize, | 516 | .isize, |
| 511 | .c_short, | 517 | .c_short, |
| ... | @@ -772,6 +778,8 @@ pub const Type = extern union { | ... | @@ -772,6 +778,8 @@ pub const Type = extern union { |
| 772 | .i32, | 778 | .i32, |
| 773 | .u64, | 779 | .u64, |
| 774 | .i64, | 780 | .i64, |
| 781 | .u128, | ||
| 782 | .i128, | ||
| 775 | .usize, | 783 | .usize, |
| 776 | .isize, | 784 | .isize, |
| 777 | .c_short, | 785 | .c_short, |
| ... | @@ -868,6 +876,7 @@ pub const Type = extern union { | ... | @@ -868,6 +876,7 @@ pub const Type = extern union { |
| 868 | .i16, .u16 => return 2, | 876 | .i16, .u16 => return 2, |
| 869 | .i32, .u32 => return 4, | 877 | .i32, .u32 => return 4, |
| 870 | .i64, .u64 => return 8, | 878 | .i64, .u64 => return 8, |
| 879 | .u128, .i128 => return 16, | ||
| 871 | 880 | ||
| 872 | .isize, | 881 | .isize, |
| 873 | .usize, | 882 | .usize, |
| ... | @@ -1010,6 +1019,7 @@ pub const Type = extern union { | ... | @@ -1010,6 +1019,7 @@ pub const Type = extern union { |
| 1010 | .i16, .u16 => return 2, | 1019 | .i16, .u16 => return 2, |
| 1011 | .i32, .u32 => return 4, | 1020 | .i32, .u32 => return 4, |
| 1012 | .i64, .u64 => return 8, | 1021 | .i64, .u64 => return 8, |
| 1022 | .u128, .i128 => return 16, | ||
| 1013 | 1023 | ||
| 1014 | .@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8), | 1024 | .@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8), |
| 1015 | 1025 | ||
| ... | @@ -1109,6 +1119,8 @@ pub const Type = extern union { | ... | @@ -1109,6 +1119,8 @@ pub const Type = extern union { |
| 1109 | .i32, | 1119 | .i32, |
| 1110 | .u64, | 1120 | .u64, |
| 1111 | .i64, | 1121 | .i64, |
| 1122 | .u128, | ||
| 1123 | .i128, | ||
| 1112 | .usize, | 1124 | .usize, |
| 1113 | .isize, | 1125 | .isize, |
| 1114 | .c_short, | 1126 | .c_short, |
| ... | @@ -1191,6 +1203,8 @@ pub const Type = extern union { | ... | @@ -1191,6 +1203,8 @@ pub const Type = extern union { |
| 1191 | .i32, | 1203 | .i32, |
| 1192 | .u64, | 1204 | .u64, |
| 1193 | .i64, | 1205 | .i64, |
| 1206 | .u128, | ||
| 1207 | .i128, | ||
| 1194 | .usize, | 1208 | .usize, |
| 1195 | .isize, | 1209 | .isize, |
| 1196 | .c_short, | 1210 | .c_short, |
| ... | @@ -1278,6 +1292,8 @@ pub const Type = extern union { | ... | @@ -1278,6 +1292,8 @@ pub const Type = extern union { |
| 1278 | .i32, | 1292 | .i32, |
| 1279 | .u64, | 1293 | .u64, |
| 1280 | .i64, | 1294 | .i64, |
| 1295 | .u128, | ||
| 1296 | .i128, | ||
| 1281 | .usize, | 1297 | .usize, |
| 1282 | .isize, | 1298 | .isize, |
| 1283 | .c_short, | 1299 | .c_short, |
| ... | @@ -1359,6 +1375,8 @@ pub const Type = extern union { | ... | @@ -1359,6 +1375,8 @@ pub const Type = extern union { |
| 1359 | .i32, | 1375 | .i32, |
| 1360 | .u64, | 1376 | .u64, |
| 1361 | .i64, | 1377 | .i64, |
| 1378 | .u128, | ||
| 1379 | .i128, | ||
| 1362 | .usize, | 1380 | .usize, |
| 1363 | .isize, | 1381 | .isize, |
| 1364 | .c_short, | 1382 | .c_short, |
| ... | @@ -1440,6 +1458,8 @@ pub const Type = extern union { | ... | @@ -1440,6 +1458,8 @@ pub const Type = extern union { |
| 1440 | .i32, | 1458 | .i32, |
| 1441 | .u64, | 1459 | .u64, |
| 1442 | .i64, | 1460 | .i64, |
| 1461 | .u128, | ||
| 1462 | .i128, | ||
| 1443 | .usize, | 1463 | .usize, |
| 1444 | .isize, | 1464 | .isize, |
| 1445 | .c_short, | 1465 | .c_short, |
| ... | @@ -1522,6 +1542,8 @@ pub const Type = extern union { | ... | @@ -1522,6 +1542,8 @@ pub const Type = extern union { |
| 1522 | .i32, | 1542 | .i32, |
| 1523 | .u64, | 1543 | .u64, |
| 1524 | .i64, | 1544 | .i64, |
| 1545 | .u128, | ||
| 1546 | .i128, | ||
| 1525 | .usize, | 1547 | .usize, |
| 1526 | .isize, | 1548 | .isize, |
| 1527 | .c_short, | 1549 | .c_short, |
| ... | @@ -1660,6 +1682,8 @@ pub const Type = extern union { | ... | @@ -1660,6 +1682,8 @@ pub const Type = extern union { |
| 1660 | .i32 => unreachable, | 1682 | .i32 => unreachable, |
| 1661 | .u64 => unreachable, | 1683 | .u64 => unreachable, |
| 1662 | .i64 => unreachable, | 1684 | .i64 => unreachable, |
| 1685 | .u128 => unreachable, | ||
| 1686 | .i128 => unreachable, | ||
| 1663 | .usize => unreachable, | 1687 | .usize => unreachable, |
| 1664 | .isize => unreachable, | 1688 | .isize => unreachable, |
| 1665 | .c_short => unreachable, | 1689 | .c_short => unreachable, |
| ... | @@ -1776,6 +1800,8 @@ pub const Type = extern union { | ... | @@ -1776,6 +1800,8 @@ pub const Type = extern union { |
| 1776 | .i32, | 1800 | .i32, |
| 1777 | .u64, | 1801 | .u64, |
| 1778 | .i64, | 1802 | .i64, |
| 1803 | .u128, | ||
| 1804 | .i128, | ||
| 1779 | .usize, | 1805 | .usize, |
| 1780 | .isize, | 1806 | .isize, |
| 1781 | .c_short, | 1807 | .c_short, |
| ... | @@ -1856,6 +1882,8 @@ pub const Type = extern union { | ... | @@ -1856,6 +1882,8 @@ pub const Type = extern union { |
| 1856 | .i32, | 1882 | .i32, |
| 1857 | .u64, | 1883 | .u64, |
| 1858 | .i64, | 1884 | .i64, |
| 1885 | .u128, | ||
| 1886 | .i128, | ||
| 1859 | .usize, | 1887 | .usize, |
| 1860 | .isize, | 1888 | .isize, |
| 1861 | .c_short, | 1889 | .c_short, |
| ... | @@ -2009,6 +2037,8 @@ pub const Type = extern union { | ... | @@ -2009,6 +2037,8 @@ pub const Type = extern union { |
| 2009 | .i16, | 2037 | .i16, |
| 2010 | .i32, | 2038 | .i32, |
| 2011 | .i64, | 2039 | .i64, |
| 2040 | .u128, | ||
| 2041 | .i128, | ||
| 2012 | => true, | 2042 | => true, |
| 2013 | }; | 2043 | }; |
| 2014 | } | 2044 | } |
| ... | @@ -2061,6 +2091,8 @@ pub const Type = extern union { | ... | @@ -2061,6 +2091,8 @@ pub const Type = extern union { |
| 2061 | .i16, | 2091 | .i16, |
| 2062 | .i32, | 2092 | .i32, |
| 2063 | .i64, | 2093 | .i64, |
| 2094 | .u128, | ||
| 2095 | .i128, | ||
| 2064 | .optional, | 2096 | .optional, |
| 2065 | .optional_single_mut_pointer, | 2097 | .optional_single_mut_pointer, |
| 2066 | .optional_single_const_pointer, | 2098 | .optional_single_const_pointer, |
| ... | @@ -2167,6 +2199,8 @@ pub const Type = extern union { | ... | @@ -2167,6 +2199,8 @@ pub const Type = extern union { |
| 2167 | .i32 => .{ .signedness = .signed, .bits = 32 }, | 2199 | .i32 => .{ .signedness = .signed, .bits = 32 }, |
| 2168 | .u64 => .{ .signedness = .unsigned, .bits = 64 }, | 2200 | .u64 => .{ .signedness = .unsigned, .bits = 64 }, |
| 2169 | .i64 => .{ .signedness = .signed, .bits = 64 }, | 2201 | .i64 => .{ .signedness = .signed, .bits = 64 }, |
| 2202 | .u128 => .{ .signedness = .unsigned, .bits = 128 }, | ||
| 2203 | .i128 => .{ .signedness = .signed, .bits = 128 }, | ||
| 2170 | .usize => .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, | 2204 | .usize => .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, |
| 2171 | .isize => .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, | 2205 | .isize => .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, |
| 2172 | .c_short => .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) }, | 2206 | .c_short => .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) }, |
| ... | @@ -2227,6 +2261,8 @@ pub const Type = extern union { | ... | @@ -2227,6 +2261,8 @@ pub const Type = extern union { |
| 2227 | .i32, | 2261 | .i32, |
| 2228 | .u64, | 2262 | .u64, |
| 2229 | .i64, | 2263 | .i64, |
| 2264 | .u128, | ||
| 2265 | .i128, | ||
| 2230 | .optional, | 2266 | .optional, |
| 2231 | .optional_single_mut_pointer, | 2267 | .optional_single_mut_pointer, |
| 2232 | .optional_single_const_pointer, | 2268 | .optional_single_const_pointer, |
| ... | @@ -2333,6 +2369,8 @@ pub const Type = extern union { | ... | @@ -2333,6 +2369,8 @@ pub const Type = extern union { |
| 2333 | .i32, | 2369 | .i32, |
| 2334 | .u64, | 2370 | .u64, |
| 2335 | .i64, | 2371 | .i64, |
| 2372 | .u128, | ||
| 2373 | .i128, | ||
| 2336 | .usize, | 2374 | .usize, |
| 2337 | .isize, | 2375 | .isize, |
| 2338 | .c_short, | 2376 | .c_short, |
| ... | @@ -2417,6 +2455,8 @@ pub const Type = extern union { | ... | @@ -2417,6 +2455,8 @@ pub const Type = extern union { |
| 2417 | .i32, | 2455 | .i32, |
| 2418 | .u64, | 2456 | .u64, |
| 2419 | .i64, | 2457 | .i64, |
| 2458 | .u128, | ||
| 2459 | .i128, | ||
| 2420 | .usize, | 2460 | .usize, |
| 2421 | .isize, | 2461 | .isize, |
| 2422 | .c_short, | 2462 | .c_short, |
| ... | @@ -2500,6 +2540,8 @@ pub const Type = extern union { | ... | @@ -2500,6 +2540,8 @@ pub const Type = extern union { |
| 2500 | .i32, | 2540 | .i32, |
| 2501 | .u64, | 2541 | .u64, |
| 2502 | .i64, | 2542 | .i64, |
| 2543 | .u128, | ||
| 2544 | .i128, | ||
| 2503 | .usize, | 2545 | .usize, |
| 2504 | .isize, | 2546 | .isize, |
| 2505 | .c_short, | 2547 | .c_short, |
| ... | @@ -2583,6 +2625,8 @@ pub const Type = extern union { | ... | @@ -2583,6 +2625,8 @@ pub const Type = extern union { |
| 2583 | .i32, | 2625 | .i32, |
| 2584 | .u64, | 2626 | .u64, |
| 2585 | .i64, | 2627 | .i64, |
| 2628 | .u128, | ||
| 2629 | .i128, | ||
| 2586 | .usize, | 2630 | .usize, |
| 2587 | .isize, | 2631 | .isize, |
| 2588 | .c_short, | 2632 | .c_short, |
| ... | @@ -2663,6 +2707,8 @@ pub const Type = extern union { | ... | @@ -2663,6 +2707,8 @@ pub const Type = extern union { |
| 2663 | .i32, | 2707 | .i32, |
| 2664 | .u64, | 2708 | .u64, |
| 2665 | .i64, | 2709 | .i64, |
| 2710 | .u128, | ||
| 2711 | .i128, | ||
| 2666 | .usize, | 2712 | .usize, |
| 2667 | .isize, | 2713 | .isize, |
| 2668 | .c_short, | 2714 | .c_short, |
| ... | @@ -2743,6 +2789,8 @@ pub const Type = extern union { | ... | @@ -2743,6 +2789,8 @@ pub const Type = extern union { |
| 2743 | .i32, | 2789 | .i32, |
| 2744 | .u64, | 2790 | .u64, |
| 2745 | .i64, | 2791 | .i64, |
| 2792 | .u128, | ||
| 2793 | .i128, | ||
| 2746 | .usize, | 2794 | .usize, |
| 2747 | .isize, | 2795 | .isize, |
| 2748 | .c_short, | 2796 | .c_short, |
| ... | @@ -2793,6 +2841,8 @@ pub const Type = extern union { | ... | @@ -2793,6 +2841,8 @@ pub const Type = extern union { |
| 2793 | .i32, | 2841 | .i32, |
| 2794 | .u64, | 2842 | .u64, |
| 2795 | .i64, | 2843 | .i64, |
| 2844 | .u128, | ||
| 2845 | .i128, | ||
| 2796 | .usize, | 2846 | .usize, |
| 2797 | .isize, | 2847 | .isize, |
| 2798 | .c_short, | 2848 | .c_short, |
| ... | @@ -2874,6 +2924,8 @@ pub const Type = extern union { | ... | @@ -2874,6 +2924,8 @@ pub const Type = extern union { |
| 2874 | .i32, | 2924 | .i32, |
| 2875 | .u64, | 2925 | .u64, |
| 2876 | .i64, | 2926 | .i64, |
| 2927 | .u128, | ||
| 2928 | .i128, | ||
| 2877 | .usize, | 2929 | .usize, |
| 2878 | .isize, | 2930 | .isize, |
| 2879 | .c_short, | 2931 | .c_short, |
| ... | @@ -2971,6 +3023,8 @@ pub const Type = extern union { | ... | @@ -2971,6 +3023,8 @@ pub const Type = extern union { |
| 2971 | .i32, | 3023 | .i32, |
| 2972 | .u64, | 3024 | .u64, |
| 2973 | .i64, | 3025 | .i64, |
| 3026 | .u128, | ||
| 3027 | .i128, | ||
| 2974 | .usize, | 3028 | .usize, |
| 2975 | .isize, | 3029 | .isize, |
| 2976 | .c_short, | 3030 | .c_short, |
| ... | @@ -3060,6 +3114,8 @@ pub const Type = extern union { | ... | @@ -3060,6 +3114,8 @@ pub const Type = extern union { |
| 3060 | .i32, | 3114 | .i32, |
| 3061 | .u64, | 3115 | .u64, |
| 3062 | .i64, | 3116 | .i64, |
| 3117 | .u128, | ||
| 3118 | .i128, | ||
| 3063 | .usize, | 3119 | .usize, |
| 3064 | .isize, | 3120 | .isize, |
| 3065 | .c_short, | 3121 | .c_short, |
| ... | @@ -3193,6 +3249,8 @@ pub const Type = extern union { | ... | @@ -3193,6 +3249,8 @@ pub const Type = extern union { |
| 3193 | i32, | 3249 | i32, |
| 3194 | u64, | 3250 | u64, |
| 3195 | i64, | 3251 | i64, |
| 3252 | u128, | ||
| 3253 | i128, | ||
| 3196 | usize, | 3254 | usize, |
| 3197 | isize, | 3255 | isize, |
| 3198 | c_short, | 3256 | c_short, |
| ... | @@ -3277,6 +3335,8 @@ pub const Type = extern union { | ... | @@ -3277,6 +3335,8 @@ pub const Type = extern union { |
| 3277 | .i32, | 3335 | .i32, |
| 3278 | .u64, | 3336 | .u64, |
| 3279 | .i64, | 3337 | .i64, |
| 3338 | .u128, | ||
| 3339 | .i128, | ||
| 3280 | .usize, | 3340 | .usize, |
| 3281 | .isize, | 3341 | .isize, |
| 3282 | .c_short, | 3342 | .c_short, |
| ... | @@ -3352,6 +3412,11 @@ pub const Type = extern union { | ... | @@ -3352,6 +3412,11 @@ pub const Type = extern union { |
| 3352 | }; | 3412 | }; |
| 3353 | } | 3413 | } |
| 3354 | 3414 | ||
| 3415 | pub fn init(comptime t: Tag) Type { | ||
| 3416 | comptime std.debug.assert(@enumToInt(t) < Tag.no_payload_count); | ||
| 3417 | return .{ .tag_if_small_enough = @enumToInt(t) }; | ||
| 3418 | } | ||
| 3419 | |||
| 3355 | pub fn create(comptime t: Tag, ally: *Allocator, data: Data(t)) error{OutOfMemory}!Type { | 3420 | pub fn create(comptime t: Tag, ally: *Allocator, data: Data(t)) error{OutOfMemory}!Type { |
| 3356 | const ptr = try ally.create(t.Type()); | 3421 | const ptr = try ally.create(t.Type()); |
| 3357 | ptr.* = .{ | 3422 | ptr.* = .{ |
src/zig_clang.cpp+5| ... | @@ -2244,6 +2244,11 @@ unsigned ZigClangAPSInt_getNumWords(const ZigClangAPSInt *self) { | ... | @@ -2244,6 +2244,11 @@ unsigned ZigClangAPSInt_getNumWords(const ZigClangAPSInt *self) { |
| 2244 | return casted->getNumWords(); | 2244 | return casted->getNumWords(); |
| 2245 | } | 2245 | } |
| 2246 | 2246 | ||
| 2247 | bool ZigClangAPSInt_lessThanEqual(const ZigClangAPSInt *self, uint64_t rhs) { | ||
| 2248 | auto casted = reinterpret_cast<const llvm::APSInt *>(self); | ||
| 2249 | return casted->ule(rhs); | ||
| 2250 | } | ||
| 2251 | |||
| 2247 | uint64_t ZigClangAPInt_getLimitedValue(const ZigClangAPInt *self, uint64_t limit) { | 2252 | uint64_t ZigClangAPInt_getLimitedValue(const ZigClangAPInt *self, uint64_t limit) { |
| 2248 | auto casted = reinterpret_cast<const llvm::APInt *>(self); | 2253 | auto casted = reinterpret_cast<const llvm::APInt *>(self); |
| 2249 | return casted->getLimitedValue(limit); | 2254 | return casted->getLimitedValue(limit); |
src/zig_clang.h+1| ... | @@ -1097,6 +1097,7 @@ ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangAPSInt_negate(const struct Zig | ... | @@ -1097,6 +1097,7 @@ ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangAPSInt_negate(const struct Zig |
| 1097 | ZIG_EXTERN_C void ZigClangAPSInt_free(const struct ZigClangAPSInt *self); | 1097 | ZIG_EXTERN_C void ZigClangAPSInt_free(const struct ZigClangAPSInt *self); |
| 1098 | ZIG_EXTERN_C const uint64_t *ZigClangAPSInt_getRawData(const struct ZigClangAPSInt *self); | 1098 | ZIG_EXTERN_C const uint64_t *ZigClangAPSInt_getRawData(const struct ZigClangAPSInt *self); |
| 1099 | ZIG_EXTERN_C unsigned ZigClangAPSInt_getNumWords(const struct ZigClangAPSInt *self); | 1099 | ZIG_EXTERN_C unsigned ZigClangAPSInt_getNumWords(const struct ZigClangAPSInt *self); |
| 1100 | ZIG_EXTERN_C bool ZigClangAPSInt_lessThanEqual(const struct ZigClangAPSInt *self, uint64_t rhs); | ||
| 1100 | 1101 | ||
| 1101 | ZIG_EXTERN_C uint64_t ZigClangAPInt_getLimitedValue(const struct ZigClangAPInt *self, uint64_t limit); | 1102 | ZIG_EXTERN_C uint64_t ZigClangAPInt_getLimitedValue(const struct ZigClangAPInt *self, uint64_t limit); |
| 1102 | 1103 |
src/zir.zig+41-26| ... | @@ -53,6 +53,9 @@ pub const Inst = struct { | ... | @@ -53,6 +53,9 @@ pub const Inst = struct { |
| 53 | indexable_ptr_len, | 53 | indexable_ptr_len, |
| 54 | /// Function parameter value. These must be first in a function's main block, | 54 | /// Function parameter value. These must be first in a function's main block, |
| 55 | /// in respective order with the parameters. | 55 | /// in respective order with the parameters. |
| 56 | /// TODO make this instruction implicit; after we transition to having ZIR | ||
| 57 | /// instructions be same sized and referenced by index, the first N indexes | ||
| 58 | /// will implicitly be references to the parameters of the function. | ||
| 56 | arg, | 59 | arg, |
| 57 | /// Type coercion. | 60 | /// Type coercion. |
| 58 | as, | 61 | as, |
| ... | @@ -169,8 +172,10 @@ pub const Inst = struct { | ... | @@ -169,8 +172,10 @@ pub const Inst = struct { |
| 169 | floatcast, | 172 | floatcast, |
| 170 | /// Declare a function body. | 173 | /// Declare a function body. |
| 171 | @"fn", | 174 | @"fn", |
| 172 | /// Returns a function type. | 175 | /// Returns a function type, assuming unspecified calling convention. |
| 173 | fntype, | 176 | fn_type, |
| 177 | /// Returns a function type, with a calling convention instruction operand. | ||
| 178 | fn_type_cc, | ||
| 174 | /// @import(operand) | 179 | /// @import(operand) |
| 175 | import, | 180 | import, |
| 176 | /// Integer literal. | 181 | /// Integer literal. |
| ... | @@ -340,6 +345,8 @@ pub const Inst = struct { | ... | @@ -340,6 +345,8 @@ pub const Inst = struct { |
| 340 | void_value, | 345 | void_value, |
| 341 | /// A switch expression. | 346 | /// A switch expression. |
| 342 | switchbr, | 347 | switchbr, |
| 348 | /// Same as `switchbr` but the target is a pointer to the value being switched on. | ||
| 349 | switchbr_ref, | ||
| 343 | /// A range in a switch case, `lhs...rhs`. | 350 | /// A range in a switch case, `lhs...rhs`. |
| 344 | /// Only checks that `lhs >= rhs` if they are ints, everything else is | 351 | /// Only checks that `lhs >= rhs` if they are ints, everything else is |
| 345 | /// validated by the .switch instruction. | 352 | /// validated by the .switch instruction. |
| ... | @@ -450,6 +457,8 @@ pub const Inst = struct { | ... | @@ -450,6 +457,8 @@ pub const Inst = struct { |
| 450 | .block_comptime_flat, | 457 | .block_comptime_flat, |
| 451 | => Block, | 458 | => Block, |
| 452 | 459 | ||
| 460 | .switchbr, .switchbr_ref => SwitchBr, | ||
| 461 | |||
| 453 | .arg => Arg, | 462 | .arg => Arg, |
| 454 | .array_type_sentinel => ArrayTypeSentinel, | 463 | .array_type_sentinel => ArrayTypeSentinel, |
| 455 | .@"break" => Break, | 464 | .@"break" => Break, |
| ... | @@ -471,7 +480,8 @@ pub const Inst = struct { | ... | @@ -471,7 +480,8 @@ pub const Inst = struct { |
| 471 | .@"export" => Export, | 480 | .@"export" => Export, |
| 472 | .param_type => ParamType, | 481 | .param_type => ParamType, |
| 473 | .primitive => Primitive, | 482 | .primitive => Primitive, |
| 474 | .fntype => FnType, | 483 | .fn_type => FnType, |
| 484 | .fn_type_cc => FnTypeCc, | ||
| 475 | .elem_ptr, .elem_val => Elem, | 485 | .elem_ptr, .elem_val => Elem, |
| 476 | .condbr => CondBr, | 486 | .condbr => CondBr, |
| 477 | .ptr_type => PtrType, | 487 | .ptr_type => PtrType, |
| ... | @@ -485,7 +495,6 @@ pub const Inst = struct { | ... | @@ -485,7 +495,6 @@ pub const Inst = struct { |
| 485 | .enum_type => EnumType, | 495 | .enum_type => EnumType, |
| 486 | .union_type => UnionType, | 496 | .union_type => UnionType, |
| 487 | .struct_type => StructType, | 497 | .struct_type => StructType, |
| 488 | .switchbr => SwitchBr, | ||
| 489 | }; | 498 | }; |
| 490 | } | 499 | } |
| 491 | 500 | ||
| ... | @@ -546,7 +555,8 @@ pub const Inst = struct { | ... | @@ -546,7 +555,8 @@ pub const Inst = struct { |
| 546 | .field_ptr_named, | 555 | .field_ptr_named, |
| 547 | .field_val_named, | 556 | .field_val_named, |
| 548 | .@"fn", | 557 | .@"fn", |
| 549 | .fntype, | 558 | .fn_type, |
| 559 | .fn_type_cc, | ||
| 550 | .int, | 560 | .int, |
| 551 | .intcast, | 561 | .intcast, |
| 552 | .int_type, | 562 | .int_type, |
| ... | @@ -614,7 +624,6 @@ pub const Inst = struct { | ... | @@ -614,7 +624,6 @@ pub const Inst = struct { |
| 614 | .struct_type, | 624 | .struct_type, |
| 615 | .void_value, | 625 | .void_value, |
| 616 | .switch_range, | 626 | .switch_range, |
| 617 | .switchbr, | ||
| 618 | => false, | 627 | => false, |
| 619 | 628 | ||
| 620 | .@"break", | 629 | .@"break", |
| ... | @@ -629,6 +638,8 @@ pub const Inst = struct { | ... | @@ -629,6 +638,8 @@ pub const Inst = struct { |
| 629 | .container_field_named, | 638 | .container_field_named, |
| 630 | .container_field_typed, | 639 | .container_field_typed, |
| 631 | .container_field, | 640 | .container_field, |
| 641 | .switchbr, | ||
| 642 | .switchbr_ref, | ||
| 632 | => true, | 643 | => true, |
| 633 | }; | 644 | }; |
| 634 | } | 645 | } |
| ... | @@ -689,6 +700,8 @@ pub const Inst = struct { | ... | @@ -689,6 +700,8 @@ pub const Inst = struct { |
| 689 | base: Inst, | 700 | base: Inst, |
| 690 | 701 | ||
| 691 | positionals: struct { | 702 | positionals: struct { |
| 703 | /// This exists to be passed to the arg TZIR instruction, which | ||
| 704 | /// needs it for debug info. | ||
| 692 | name: []const u8, | 705 | name: []const u8, |
| 693 | }, | 706 | }, |
| 694 | kw_args: struct {}, | 707 | kw_args: struct {}, |
| ... | @@ -725,6 +738,8 @@ pub const Inst = struct { | ... | @@ -725,6 +738,8 @@ pub const Inst = struct { |
| 725 | kw_args: struct {}, | 738 | kw_args: struct {}, |
| 726 | }; | 739 | }; |
| 727 | 740 | ||
| 741 | // TODO break this into multiple call instructions to avoid paying the cost | ||
| 742 | // of the calling convention field most of the time. | ||
| 728 | pub const Call = struct { | 743 | pub const Call = struct { |
| 729 | pub const base_tag = Tag.call; | 744 | pub const base_tag = Tag.call; |
| 730 | base: Inst, | 745 | base: Inst, |
| ... | @@ -732,10 +747,9 @@ pub const Inst = struct { | ... | @@ -732,10 +747,9 @@ pub const Inst = struct { |
| 732 | positionals: struct { | 747 | positionals: struct { |
| 733 | func: *Inst, | 748 | func: *Inst, |
| 734 | args: []*Inst, | 749 | args: []*Inst, |
| 735 | }, | ||
| 736 | kw_args: struct { | ||
| 737 | modifier: std.builtin.CallOptions.Modifier = .auto, | 750 | modifier: std.builtin.CallOptions.Modifier = .auto, |
| 738 | }, | 751 | }, |
| 752 | kw_args: struct {}, | ||
| 739 | }; | 753 | }; |
| 740 | 754 | ||
| 741 | pub const DeclRef = struct { | 755 | pub const DeclRef = struct { |
| ... | @@ -849,8 +863,8 @@ pub const Inst = struct { | ... | @@ -849,8 +863,8 @@ pub const Inst = struct { |
| 849 | kw_args: struct { | 863 | kw_args: struct { |
| 850 | @"volatile": bool = false, | 864 | @"volatile": bool = false, |
| 851 | output: ?*Inst = null, | 865 | output: ?*Inst = null, |
| 852 | inputs: []*Inst = &[0]*Inst{}, | 866 | inputs: []const []const u8 = &.{}, |
| 853 | clobbers: []*Inst = &[0]*Inst{}, | 867 | clobbers: []const []const u8 = &.{}, |
| 854 | args: []*Inst = &[0]*Inst{}, | 868 | args: []*Inst = &[0]*Inst{}, |
| 855 | }, | 869 | }, |
| 856 | }; | 870 | }; |
| ... | @@ -867,7 +881,18 @@ pub const Inst = struct { | ... | @@ -867,7 +881,18 @@ pub const Inst = struct { |
| 867 | }; | 881 | }; |
| 868 | 882 | ||
| 869 | pub const FnType = struct { | 883 | pub const FnType = struct { |
| 870 | pub const base_tag = Tag.fntype; | 884 | pub const base_tag = Tag.fn_type; |
| 885 | base: Inst, | ||
| 886 | |||
| 887 | positionals: struct { | ||
| 888 | param_types: []*Inst, | ||
| 889 | return_type: *Inst, | ||
| 890 | }, | ||
| 891 | kw_args: struct {}, | ||
| 892 | }; | ||
| 893 | |||
| 894 | pub const FnTypeCc = struct { | ||
| 895 | pub const base_tag = Tag.fn_type_cc; | ||
| 871 | base: Inst, | 896 | base: Inst, |
| 872 | 897 | ||
| 873 | positionals: struct { | 898 | positionals: struct { |
| ... | @@ -1167,20 +1192,12 @@ pub const Inst = struct { | ... | @@ -1167,20 +1192,12 @@ pub const Inst = struct { |
| 1167 | }, | 1192 | }, |
| 1168 | kw_args: struct { | 1193 | kw_args: struct { |
| 1169 | init_inst: ?*Inst = null, | 1194 | init_inst: ?*Inst = null, |
| 1170 | init_kind: InitKind = .none, | 1195 | has_enum_token: bool, |
| 1171 | layout: std.builtin.TypeInfo.ContainerLayout = .Auto, | 1196 | layout: std.builtin.TypeInfo.ContainerLayout = .Auto, |
| 1172 | }, | 1197 | }, |
| 1173 | |||
| 1174 | // TODO error: values of type '(enum literal)' must be comptime known | ||
| 1175 | pub const InitKind = enum { | ||
| 1176 | enum_type, | ||
| 1177 | tag_type, | ||
| 1178 | none, | ||
| 1179 | }; | ||
| 1180 | }; | 1198 | }; |
| 1181 | 1199 | ||
| 1182 | pub const SwitchBr = struct { | 1200 | pub const SwitchBr = struct { |
| 1183 | pub const base_tag = Tag.switchbr; | ||
| 1184 | base: Inst, | 1201 | base: Inst, |
| 1185 | 1202 | ||
| 1186 | positionals: struct { | 1203 | positionals: struct { |
| ... | @@ -1189,14 +1206,12 @@ pub const Inst = struct { | ... | @@ -1189,14 +1206,12 @@ pub const Inst = struct { |
| 1189 | items: []*Inst, | 1206 | items: []*Inst, |
| 1190 | cases: []Case, | 1207 | cases: []Case, |
| 1191 | else_body: Body, | 1208 | else_body: Body, |
| 1192 | }, | ||
| 1193 | kw_args: struct { | ||
| 1194 | /// Pointer to first range if such exists. | 1209 | /// Pointer to first range if such exists. |
| 1195 | range: ?*Inst = null, | 1210 | range: ?*Inst = null, |
| 1196 | special_prong: SpecialProng = .none, | 1211 | special_prong: SpecialProng = .none, |
| 1197 | }, | 1212 | }, |
| 1213 | kw_args: struct {}, | ||
| 1198 | 1214 | ||
| 1199 | // Not anonymous due to stage1 limitations | ||
| 1200 | pub const SpecialProng = enum { | 1215 | pub const SpecialProng = enum { |
| 1201 | none, | 1216 | none, |
| 1202 | @"else", | 1217 | @"else", |
| ... | @@ -1391,6 +1406,7 @@ const Writer = struct { | ... | @@ -1391,6 +1406,7 @@ const Writer = struct { |
| 1391 | } | 1406 | } |
| 1392 | switch (@TypeOf(param)) { | 1407 | switch (@TypeOf(param)) { |
| 1393 | *Inst => return self.writeInstParamToStream(stream, param), | 1408 | *Inst => return self.writeInstParamToStream(stream, param), |
| 1409 | ?*Inst => return self.writeInstParamToStream(stream, param.?), | ||
| 1394 | []*Inst => { | 1410 | []*Inst => { |
| 1395 | try stream.writeByte('['); | 1411 | try stream.writeByte('['); |
| 1396 | for (param) |inst, i| { | 1412 | for (param) |inst, i| { |
| ... | @@ -1458,7 +1474,7 @@ const Writer = struct { | ... | @@ -1458,7 +1474,7 @@ const Writer = struct { |
| 1458 | const name = self.loop_table.get(param).?; | 1474 | const name = self.loop_table.get(param).?; |
| 1459 | return stream.print("\"{}\"", .{std.zig.fmtEscapes(name)}); | 1475 | return stream.print("\"{}\"", .{std.zig.fmtEscapes(name)}); |
| 1460 | }, | 1476 | }, |
| 1461 | [][]const u8 => { | 1477 | [][]const u8, []const []const u8 => { |
| 1462 | try stream.writeByte('['); | 1478 | try stream.writeByte('['); |
| 1463 | for (param) |str, i| { | 1479 | for (param) |str, i| { |
| 1464 | if (i != 0) { | 1480 | if (i != 0) { |
| ... | @@ -1586,6 +1602,7 @@ const DumpTzir = struct { | ... | @@ -1586,6 +1602,7 @@ const DumpTzir = struct { |
| 1586 | .unreach, | 1602 | .unreach, |
| 1587 | .breakpoint, | 1603 | .breakpoint, |
| 1588 | .dbg_stmt, | 1604 | .dbg_stmt, |
| 1605 | .arg, | ||
| 1589 | => {}, | 1606 | => {}, |
| 1590 | 1607 | ||
| 1591 | .ref, | 1608 | .ref, |
| ... | @@ -1630,8 +1647,6 @@ const DumpTzir = struct { | ... | @@ -1630,8 +1647,6 @@ const DumpTzir = struct { |
| 1630 | try dtz.findConst(bin_op.rhs); | 1647 | try dtz.findConst(bin_op.rhs); |
| 1631 | }, | 1648 | }, |
| 1632 | 1649 | ||
| 1633 | .arg => {}, | ||
| 1634 | |||
| 1635 | .br => { | 1650 | .br => { |
| 1636 | const br = inst.castTag(.br).?; | 1651 | const br = inst.castTag(.br).?; |
| 1637 | try dtz.findConst(&br.block.base); | 1652 | try dtz.findConst(&br.block.base); |
src/zir_sema.zig+74-30| ... | @@ -91,7 +91,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! | ... | @@ -91,7 +91,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 91 | .@"fn" => return zirFn(mod, scope, old_inst.castTag(.@"fn").?), | 91 | .@"fn" => return zirFn(mod, scope, old_inst.castTag(.@"fn").?), |
| 92 | .@"export" => return zirExport(mod, scope, old_inst.castTag(.@"export").?), | 92 | .@"export" => return zirExport(mod, scope, old_inst.castTag(.@"export").?), |
| 93 | .primitive => return zirPrimitive(mod, scope, old_inst.castTag(.primitive).?), | 93 | .primitive => return zirPrimitive(mod, scope, old_inst.castTag(.primitive).?), |
| 94 | .fntype => return zirFnType(mod, scope, old_inst.castTag(.fntype).?), | 94 | .fn_type => return zirFnType(mod, scope, old_inst.castTag(.fn_type).?), |
| 95 | .fn_type_cc => return zirFnTypeCc(mod, scope, old_inst.castTag(.fn_type_cc).?), | ||
| 95 | .intcast => return zirIntcast(mod, scope, old_inst.castTag(.intcast).?), | 96 | .intcast => return zirIntcast(mod, scope, old_inst.castTag(.intcast).?), |
| 96 | .bitcast => return zirBitcast(mod, scope, old_inst.castTag(.bitcast).?), | 97 | .bitcast => return zirBitcast(mod, scope, old_inst.castTag(.bitcast).?), |
| 97 | .floatcast => return zirFloatcast(mod, scope, old_inst.castTag(.floatcast).?), | 98 | .floatcast => return zirFloatcast(mod, scope, old_inst.castTag(.floatcast).?), |
| ... | @@ -154,7 +155,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! | ... | @@ -154,7 +155,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 154 | .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?), | 155 | .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?), |
| 155 | .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?), | 156 | .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?), |
| 156 | .void_value => return mod.constVoid(scope, old_inst.src), | 157 | .void_value => return mod.constVoid(scope, old_inst.src), |
| 157 | .switchbr => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr).?), | 158 | .switchbr => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr).?, false), |
| 159 | .switchbr_ref => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr_ref).?, true), | ||
| 158 | .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?), | 160 | .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?), |
| 159 | 161 | ||
| 160 | .container_field_named, | 162 | .container_field_named, |
| ... | @@ -957,11 +959,11 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { | ... | @@ -957,11 +959,11 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { |
| 957 | ); | 959 | ); |
| 958 | } | 960 | } |
| 959 | 961 | ||
| 960 | if (inst.kw_args.modifier == .compile_time) { | 962 | if (inst.positionals.modifier == .compile_time) { |
| 961 | return mod.fail(scope, inst.base.src, "TODO implement comptime function calls", .{}); | 963 | return mod.fail(scope, inst.base.src, "TODO implement comptime function calls", .{}); |
| 962 | } | 964 | } |
| 963 | if (inst.kw_args.modifier != .auto) { | 965 | if (inst.positionals.modifier != .auto) { |
| 964 | return mod.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.kw_args.modifier}); | 966 | return mod.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.positionals.modifier}); |
| 965 | } | 967 | } |
| 966 | 968 | ||
| 967 | // TODO handle function calls of generic functions | 969 | // TODO handle function calls of generic functions |
| ... | @@ -979,8 +981,8 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { | ... | @@ -979,8 +981,8 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { |
| 979 | const ret_type = func.ty.fnReturnType(); | 981 | const ret_type = func.ty.fnReturnType(); |
| 980 | 982 | ||
| 981 | const b = try mod.requireFunctionBlock(scope, inst.base.src); | 983 | const b = try mod.requireFunctionBlock(scope, inst.base.src); |
| 982 | const is_comptime_call = b.is_comptime or inst.kw_args.modifier == .compile_time; | 984 | const is_comptime_call = b.is_comptime or inst.positionals.modifier == .compile_time; |
| 983 | const is_inline_call = is_comptime_call or inst.kw_args.modifier == .always_inline or | 985 | const is_inline_call = is_comptime_call or inst.positionals.modifier == .always_inline or |
| 984 | func.ty.fnCallingConvention() == .Inline; | 986 | func.ty.fnCallingConvention() == .Inline; |
| 985 | if (is_inline_call) { | 987 | if (is_inline_call) { |
| 986 | const func_val = try mod.resolveConstValue(scope, func); | 988 | const func_val = try mod.resolveConstValue(scope, func); |
| ... | @@ -1294,34 +1296,69 @@ fn zirEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) | ... | @@ -1294,34 +1296,69 @@ fn zirEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) |
| 1294 | fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { | 1296 | fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { |
| 1295 | const tracy = trace(@src()); | 1297 | const tracy = trace(@src()); |
| 1296 | defer tracy.end(); | 1298 | defer tracy.end(); |
| 1297 | const return_type = try resolveType(mod, scope, fntype.positionals.return_type); | 1299 | |
| 1300 | return fnTypeCommon( | ||
| 1301 | mod, | ||
| 1302 | scope, | ||
| 1303 | &fntype.base, | ||
| 1304 | fntype.positionals.param_types, | ||
| 1305 | fntype.positionals.return_type, | ||
| 1306 | .Unspecified, | ||
| 1307 | ); | ||
| 1308 | } | ||
| 1309 | |||
| 1310 | fn zirFnTypeCc(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnTypeCc) InnerError!*Inst { | ||
| 1311 | const tracy = trace(@src()); | ||
| 1312 | defer tracy.end(); | ||
| 1313 | |||
| 1298 | const cc_tv = try resolveInstConst(mod, scope, fntype.positionals.cc); | 1314 | const cc_tv = try resolveInstConst(mod, scope, fntype.positionals.cc); |
| 1315 | // TODO once we're capable of importing and analyzing decls from | ||
| 1316 | // std.builtin, this needs to change | ||
| 1299 | const cc_str = cc_tv.val.castTag(.enum_literal).?.data; | 1317 | const cc_str = cc_tv.val.castTag(.enum_literal).?.data; |
| 1300 | const cc = std.meta.stringToEnum(std.builtin.CallingConvention, cc_str) orelse | 1318 | const cc = std.meta.stringToEnum(std.builtin.CallingConvention, cc_str) orelse |
| 1301 | return mod.fail(scope, fntype.positionals.cc.src, "Unknown calling convention {s}", .{cc_str}); | 1319 | return mod.fail(scope, fntype.positionals.cc.src, "Unknown calling convention {s}", .{cc_str}); |
| 1320 | return fnTypeCommon( | ||
| 1321 | mod, | ||
| 1322 | scope, | ||
| 1323 | &fntype.base, | ||
| 1324 | fntype.positionals.param_types, | ||
| 1325 | fntype.positionals.return_type, | ||
| 1326 | cc, | ||
| 1327 | ); | ||
| 1328 | } | ||
| 1329 | |||
| 1330 | fn fnTypeCommon( | ||
| 1331 | mod: *Module, | ||
| 1332 | scope: *Scope, | ||
| 1333 | zir_inst: *zir.Inst, | ||
| 1334 | zir_param_types: []*zir.Inst, | ||
| 1335 | zir_return_type: *zir.Inst, | ||
| 1336 | cc: std.builtin.CallingConvention, | ||
| 1337 | ) InnerError!*Inst { | ||
| 1338 | const return_type = try resolveType(mod, scope, zir_return_type); | ||
| 1302 | 1339 | ||
| 1303 | // Hot path for some common function types. | 1340 | // Hot path for some common function types. |
| 1304 | if (fntype.positionals.param_types.len == 0) { | 1341 | if (zir_param_types.len == 0) { |
| 1305 | if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { | 1342 | if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { |
| 1306 | return mod.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args)); | 1343 | return mod.constType(scope, zir_inst.src, Type.initTag(.fn_noreturn_no_args)); |
| 1307 | } | 1344 | } |
| 1308 | 1345 | ||
| 1309 | if (return_type.zigTypeTag() == .Void and cc == .Unspecified) { | 1346 | if (return_type.zigTypeTag() == .Void and cc == .Unspecified) { |
| 1310 | return mod.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args)); | 1347 | return mod.constType(scope, zir_inst.src, Type.initTag(.fn_void_no_args)); |
| 1311 | } | 1348 | } |
| 1312 | 1349 | ||
| 1313 | if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) { | 1350 | if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) { |
| 1314 | return mod.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args)); | 1351 | return mod.constType(scope, zir_inst.src, Type.initTag(.fn_naked_noreturn_no_args)); |
| 1315 | } | 1352 | } |
| 1316 | 1353 | ||
| 1317 | if (return_type.zigTypeTag() == .Void and cc == .C) { | 1354 | if (return_type.zigTypeTag() == .Void and cc == .C) { |
| 1318 | return mod.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args)); | 1355 | return mod.constType(scope, zir_inst.src, Type.initTag(.fn_ccc_void_no_args)); |
| 1319 | } | 1356 | } |
| 1320 | } | 1357 | } |
| 1321 | 1358 | ||
| 1322 | const arena = scope.arena(); | 1359 | const arena = scope.arena(); |
| 1323 | const param_types = try arena.alloc(Type, fntype.positionals.param_types.len); | 1360 | const param_types = try arena.alloc(Type, zir_param_types.len); |
| 1324 | for (fntype.positionals.param_types) |param_type, i| { | 1361 | for (zir_param_types) |param_type, i| { |
| 1325 | const resolved = try resolveType(mod, scope, param_type); | 1362 | const resolved = try resolveType(mod, scope, param_type); |
| 1326 | // TODO skip for comptime params | 1363 | // TODO skip for comptime params |
| 1327 | if (!resolved.isValidVarType(false)) { | 1364 | if (!resolved.isValidVarType(false)) { |
| ... | @@ -1335,7 +1372,7 @@ fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!* | ... | @@ -1335,7 +1372,7 @@ fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!* |
| 1335 | .return_type = return_type, | 1372 | .return_type = return_type, |
| 1336 | .cc = cc, | 1373 | .cc = cc, |
| 1337 | }); | 1374 | }); |
| 1338 | return mod.constType(scope, fntype.base.src, fn_ty); | 1375 | return mod.constType(scope, zir_inst.src, fn_ty); |
| 1339 | } | 1376 | } |
| 1340 | 1377 | ||
| 1341 | fn zirPrimitive(mod: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst { | 1378 | fn zirPrimitive(mod: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst { |
| ... | @@ -1554,10 +1591,15 @@ fn zirSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError | ... | @@ -1554,10 +1591,15 @@ fn zirSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError |
| 1554 | return mod.constVoid(scope, inst.base.src); | 1591 | return mod.constVoid(scope, inst.base.src); |
| 1555 | } | 1592 | } |
| 1556 | 1593 | ||
| 1557 | fn zirSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError!*Inst { | 1594 | fn zirSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr, ref: bool) InnerError!*Inst { |
| 1558 | const tracy = trace(@src()); | 1595 | const tracy = trace(@src()); |
| 1559 | defer tracy.end(); | 1596 | defer tracy.end(); |
| 1560 | const target = try resolveInst(mod, scope, inst.positionals.target); | 1597 | |
| 1598 | const target_ptr = try resolveInst(mod, scope, inst.positionals.target); | ||
| 1599 | const target = if (ref) | ||
| 1600 | try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target.src) | ||
| 1601 | else | ||
| 1602 | target_ptr; | ||
| 1561 | try validateSwitch(mod, scope, target, inst); | 1603 | try validateSwitch(mod, scope, target, inst); |
| 1562 | 1604 | ||
| 1563 | if (try mod.resolveDefinedValue(scope, target)) |target_val| { | 1605 | if (try mod.resolveDefinedValue(scope, target)) |target_val| { |
| ... | @@ -1626,13 +1668,13 @@ fn zirSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError | ... | @@ -1626,13 +1668,13 @@ fn zirSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError |
| 1626 | 1668 | ||
| 1627 | fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void { | 1669 | fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void { |
| 1628 | // validate usage of '_' prongs | 1670 | // validate usage of '_' prongs |
| 1629 | if (inst.kw_args.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) { | 1671 | if (inst.positionals.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) { |
| 1630 | return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{}); | 1672 | return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{}); |
| 1631 | // TODO notes "'_' prong here" inst.positionals.cases[last].src | 1673 | // TODO notes "'_' prong here" inst.positionals.cases[last].src |
| 1632 | } | 1674 | } |
| 1633 | 1675 | ||
| 1634 | // check that target type supports ranges | 1676 | // check that target type supports ranges |
| 1635 | if (inst.kw_args.range) |range_inst| { | 1677 | if (inst.positionals.range) |range_inst| { |
| 1636 | switch (target.ty.zigTypeTag()) { | 1678 | switch (target.ty.zigTypeTag()) { |
| 1637 | .Int, .ComptimeInt => {}, | 1679 | .Int, .ComptimeInt => {}, |
| 1638 | else => { | 1680 | else => { |
| ... | @@ -1683,14 +1725,14 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw | ... | @@ -1683,14 +1725,14 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw |
| 1683 | const start = try target.ty.minInt(&arena, mod.getTarget()); | 1725 | const start = try target.ty.minInt(&arena, mod.getTarget()); |
| 1684 | const end = try target.ty.maxInt(&arena, mod.getTarget()); | 1726 | const end = try target.ty.maxInt(&arena, mod.getTarget()); |
| 1685 | if (try range_set.spans(start, end)) { | 1727 | if (try range_set.spans(start, end)) { |
| 1686 | if (inst.kw_args.special_prong == .@"else") { | 1728 | if (inst.positionals.special_prong == .@"else") { |
| 1687 | return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); | 1729 | return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); |
| 1688 | } | 1730 | } |
| 1689 | return; | 1731 | return; |
| 1690 | } | 1732 | } |
| 1691 | } | 1733 | } |
| 1692 | 1734 | ||
| 1693 | if (inst.kw_args.special_prong != .@"else") { | 1735 | if (inst.positionals.special_prong != .@"else") { |
| 1694 | return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{}); | 1736 | return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{}); |
| 1695 | } | 1737 | } |
| 1696 | }, | 1738 | }, |
| ... | @@ -1710,15 +1752,15 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw | ... | @@ -1710,15 +1752,15 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw |
| 1710 | return mod.fail(scope, item.src, "duplicate switch value", .{}); | 1752 | return mod.fail(scope, item.src, "duplicate switch value", .{}); |
| 1711 | } | 1753 | } |
| 1712 | } | 1754 | } |
| 1713 | if ((true_count + false_count < 2) and inst.kw_args.special_prong != .@"else") { | 1755 | if ((true_count + false_count < 2) and inst.positionals.special_prong != .@"else") { |
| 1714 | return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{}); | 1756 | return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{}); |
| 1715 | } | 1757 | } |
| 1716 | if ((true_count + false_count == 2) and inst.kw_args.special_prong == .@"else") { | 1758 | if ((true_count + false_count == 2) and inst.positionals.special_prong == .@"else") { |
| 1717 | return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); | 1759 | return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); |
| 1718 | } | 1760 | } |
| 1719 | }, | 1761 | }, |
| 1720 | .EnumLiteral, .Void, .Fn, .Pointer, .Type => { | 1762 | .EnumLiteral, .Void, .Fn, .Pointer, .Type => { |
| 1721 | if (inst.kw_args.special_prong != .@"else") { | 1763 | if (inst.positionals.special_prong != .@"else") { |
| 1722 | return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty}); | 1764 | return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty}); |
| 1723 | } | 1765 | } |
| 1724 | 1766 | ||
| ... | @@ -1981,19 +2023,21 @@ fn zirDeref(mod: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst | ... | @@ -1981,19 +2023,21 @@ fn zirDeref(mod: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst |
| 1981 | fn zirAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst { | 2023 | fn zirAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst { |
| 1982 | const tracy = trace(@src()); | 2024 | const tracy = trace(@src()); |
| 1983 | defer tracy.end(); | 2025 | defer tracy.end(); |
| 2026 | |||
| 1984 | const return_type = try resolveType(mod, scope, assembly.positionals.return_type); | 2027 | const return_type = try resolveType(mod, scope, assembly.positionals.return_type); |
| 1985 | const asm_source = try resolveConstString(mod, scope, assembly.positionals.asm_source); | 2028 | const asm_source = try resolveConstString(mod, scope, assembly.positionals.asm_source); |
| 1986 | const output = if (assembly.kw_args.output) |o| try resolveConstString(mod, scope, o) else null; | 2029 | const output = if (assembly.kw_args.output) |o| try resolveConstString(mod, scope, o) else null; |
| 1987 | 2030 | ||
| 1988 | const inputs = try scope.arena().alloc([]const u8, assembly.kw_args.inputs.len); | 2031 | const arena = scope.arena(); |
| 1989 | const clobbers = try scope.arena().alloc([]const u8, assembly.kw_args.clobbers.len); | 2032 | const inputs = try arena.alloc([]const u8, assembly.kw_args.inputs.len); |
| 1990 | const args = try scope.arena().alloc(*Inst, assembly.kw_args.args.len); | 2033 | const clobbers = try arena.alloc([]const u8, assembly.kw_args.clobbers.len); |
| 2034 | const args = try arena.alloc(*Inst, assembly.kw_args.args.len); | ||
| 1991 | 2035 | ||
| 1992 | for (inputs) |*elem, i| { | 2036 | for (inputs) |*elem, i| { |
| 1993 | elem.* = try resolveConstString(mod, scope, assembly.kw_args.inputs[i]); | 2037 | elem.* = try arena.dupe(u8, assembly.kw_args.inputs[i]); |
| 1994 | } | 2038 | } |
| 1995 | for (clobbers) |*elem, i| { | 2039 | for (clobbers) |*elem, i| { |
| 1996 | elem.* = try resolveConstString(mod, scope, assembly.kw_args.clobbers[i]); | 2040 | elem.* = try arena.dupe(u8, assembly.kw_args.clobbers[i]); |
| 1997 | } | 2041 | } |
| 1998 | for (args) |*elem, i| { | 2042 | for (args) |*elem, i| { |
| 1999 | const arg = try resolveInst(mod, scope, assembly.kw_args.args[i]); | 2043 | const arg = try resolveInst(mod, scope, assembly.kw_args.args[i]); |
test/run_translated_c.zig+36| ... | @@ -3,6 +3,33 @@ const tests = @import("tests.zig"); | ... | @@ -3,6 +3,33 @@ const tests = @import("tests.zig"); |
| 3 | const nl = std.cstr.line_sep; | 3 | const nl = std.cstr.line_sep; |
| 4 | 4 | ||
| 5 | pub fn addCases(cases: *tests.RunTranslatedCContext) void { | 5 | pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 6 | cases.add("use global scope for record/enum/typedef type transalation if needed", | ||
| 7 | \\void bar(void); | ||
| 8 | \\void baz(void); | ||
| 9 | \\struct foo { int x; }; | ||
| 10 | \\void bar() { | ||
| 11 | \\	struct foo tmp; | ||
| 12 | \\} | ||
| 13 | \\ | ||
| 14 | \\void baz() { | ||
| 15 | \\	struct foo tmp; | ||
| 16 | \\} | ||
| 17 | \\ | ||
| 18 | \\int main(void) { | ||
| 19 | \\	bar(); | ||
| 20 | \\	baz(); | ||
| 21 | \\	return 0; | ||
| 22 | \\} | ||
| 23 | , ""); | ||
| 24 | |||
| 25 | cases.add("failed macros are only declared once", | ||
| 26 | \\#define FOO = | ||
| 27 | \\#define FOO = | ||
| 28 | \\#define PtrToPtr64(p) ((void *POINTER_64) p) | ||
| 29 | \\#define STRUC_ALIGNED_STACK_COPY(t,s) ((CONST t *)(s)) | ||
| 30 | \\int main(void) {} | ||
| 31 | , ""); | ||
| 32 | |||
| 6 | cases.add("parenthesized string literal", | 33 | cases.add("parenthesized string literal", |
| 7 | \\void foo(const char *s) {} | 34 | \\void foo(const char *s) {} |
| 8 | \\int main(void) { | 35 | \\int main(void) { |
| ... | @@ -922,4 +949,13 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { | ... | @@ -922,4 +949,13 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 922 | \\ return 0; | 949 | \\ return 0; |
| 923 | \\} | 950 | \\} |
| 924 | , ""); | 951 | , ""); |
| 952 | |||
| 953 | cases.add("Use correct break label for statement expression in nested scope", | ||
| 954 | \\#include <stdlib.h> | ||
| 955 | \\int main(void) { | ||
| 956 | \\ int x = ({1, ({2; 3;});}); | ||
| 957 | \\ if (x != 3) abort(); | ||
| 958 | \\ return 0; | ||
| 959 | \\} | ||
| 960 | , ""); | ||
| 925 | } | 961 | } |
test/stage2/test.zig+8-4| ... | @@ -1088,7 +1088,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -1088,7 +1088,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1088 | \\ _ = foo; | 1088 | \\ _ = foo; |
| 1089 | \\} | 1089 | \\} |
| 1090 | \\extern var foo; | 1090 | \\extern var foo; |
| 1091 | , &[_][]const u8{":4:1: error: unable to infer variable type"}); | 1091 | , &[_][]const u8{":4:8: error: unable to infer variable type"}); |
| 1092 | } | 1092 | } |
| 1093 | 1093 | ||
| 1094 | { | 1094 | { |
| ... | @@ -1194,12 +1194,12 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -1194,12 +1194,12 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1194 | \\comptime { | 1194 | \\comptime { |
| 1195 | \\ foo: while (true) {} | 1195 | \\ foo: while (true) {} |
| 1196 | \\} | 1196 | \\} |
| 1197 | , &[_][]const u8{":2:5: error: unused while label"}); | 1197 | , &[_][]const u8{":2:5: error: unused while loop label"}); |
| 1198 | case.addError( | 1198 | case.addError( |
| 1199 | \\comptime { | 1199 | \\comptime { |
| 1200 | \\ foo: for ("foo") |_| {} | 1200 | \\ foo: for ("foo") |_| {} |
| 1201 | \\} | 1201 | \\} |
| 1202 | , &[_][]const u8{":2:5: error: unused for label"}); | 1202 | , &[_][]const u8{":2:5: error: unused for loop label"}); |
| 1203 | case.addError( | 1203 | case.addError( |
| 1204 | \\comptime { | 1204 | \\comptime { |
| 1205 | \\ blk: {blk: {}} | 1205 | \\ blk: {blk: {}} |
| ... | @@ -1294,6 +1294,10 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -1294,6 +1294,10 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1294 | , | 1294 | , |
| 1295 | "", | 1295 | "", |
| 1296 | ); | 1296 | ); |
| 1297 | // TODO this should be :8:21 not :8:19. we need to improve source locations | ||
| 1298 | // to be relative to the containing Decl so that they can survive when the byte | ||
| 1299 | // offset of a previous Decl changes. Here the change from 7 to 999 introduces | ||
| 1300 | // +2 to the byte offset and makes the error location wrong by 2 bytes. | ||
| 1297 | case.addError( | 1301 | case.addError( |
| 1298 | \\export fn _start() noreturn { | 1302 | \\export fn _start() noreturn { |
| 1299 | \\ const y = fibonacci(999); | 1303 | \\ const y = fibonacci(999); |
| ... | @@ -1314,7 +1318,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -1314,7 +1318,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1314 | \\ ); | 1318 | \\ ); |
| 1315 | \\ unreachable; | 1319 | \\ unreachable; |
| 1316 | \\} | 1320 | \\} |
| 1317 | , &[_][]const u8{":8:10: error: evaluation exceeded 1000 backwards branches"}); | 1321 | , &[_][]const u8{":8:19: error: evaluation exceeded 1000 backwards branches"}); |
| 1318 | } | 1322 | } |
| 1319 | { | 1323 | { |
| 1320 | var case = ctx.exe("orelse at comptime", linux_x64); | 1324 | var case = ctx.exe("orelse at comptime", linux_x64); |
test/translate_c.zig+478-268| ... | @@ -3,12 +3,208 @@ const std = @import("std"); | ... | @@ -3,12 +3,208 @@ const std = @import("std"); |
| 3 | const CrossTarget = std.zig.CrossTarget; | 3 | const CrossTarget = std.zig.CrossTarget; |
| 4 | 4 | ||
| 5 | pub fn addCases(cases: *tests.TranslateCContext) void { | 5 | pub fn addCases(cases: *tests.TranslateCContext) void { |
| 6 | cases.add("variadic function demoted to prototype", | 6 | cases.add("if as while stmt has semicolon", |
| 7 | \\void foo() { | ||
| 8 | \\ while (1) if (1) { | ||
| 9 | \\ int a = 1; | ||
| 10 | \\ } else { | ||
| 11 | \\ int b = 2; | ||
| 12 | \\ } | ||
| 13 | \\ if (1) if (1) {} | ||
| 14 | \\} | ||
| 15 | , &[_][]const u8{ | ||
| 16 | \\pub export fn foo() void { | ||
| 17 | \\ while (true) if (true) { | ||
| 18 | \\ var a: c_int = 1; | ||
| 19 | \\ } else { | ||
| 20 | \\ var b: c_int = 2; | ||
| 21 | \\ }; | ||
| 22 | \\ if (true) if (true) {}; | ||
| 23 | \\} | ||
| 24 | }); | ||
| 25 | |||
| 26 | cases.add("conditional operator cast to void", | ||
| 27 | \\int bar(); | ||
| 28 | \\void foo() { | ||
| 29 | \\ int a; | ||
| 30 | \\ a ? a = 2 : bar(); | ||
| 31 | \\} | ||
| 32 | , &[_][]const u8{ | ||
| 33 | \\pub extern fn bar(...) c_int; | ||
| 34 | \\pub export fn foo() void { | ||
| 35 | \\ var a: c_int = undefined; | ||
| 36 | \\ if (a != 0) a = 2 else _ = bar(); | ||
| 37 | \\} | ||
| 38 | }); | ||
| 39 | |||
| 40 | cases.add("struct in struct init to zero", | ||
| 41 | \\struct Foo { | ||
| 42 | \\ int a; | ||
| 43 | \\ struct Bar { | ||
| 44 | \\ int a; | ||
| 45 | \\ } b; | ||
| 46 | \\} a = {}; | ||
| 47 | \\#define PTR void * | ||
| 48 | , &[_][]const u8{ | ||
| 49 | \\pub const struct_Bar = extern struct { | ||
| 50 | \\ a: c_int, | ||
| 51 | \\}; | ||
| 52 | \\pub const struct_Foo = extern struct { | ||
| 53 | \\ a: c_int, | ||
| 54 | \\ b: struct_Bar, | ||
| 55 | \\}; | ||
| 56 | \\pub export var a: struct_Foo = struct_Foo{ | ||
| 57 | \\ .a = 0, | ||
| 58 | \\ .b = @import("std").mem.zeroes(struct_Bar), | ||
| 59 | \\}; | ||
| 60 | , | ||
| 61 | \\pub const PTR = ?*c_void; | ||
| 62 | }); | ||
| 63 | |||
| 64 | cases.add("scoped enum", | ||
| 65 | \\void foo() { | ||
| 66 | \\	enum Foo { | ||
| 67 | \\		A, | ||
| 68 | \\		B, | ||
| 69 | \\		C, | ||
| 70 | \\	}; | ||
| 71 | \\	enum Foo a = B; | ||
| 72 | \\	{ | ||
| 73 | \\		enum Foo { | ||
| 74 | \\			A, | ||
| 75 | \\			B, | ||
| 76 | \\			C, | ||
| 77 | \\		}; | ||
| 78 | \\		enum Foo a = B; | ||
| 79 | \\	} | ||
| 80 | \\} | ||
| 81 | , &[_][]const u8{ | ||
| 82 | \\pub export fn foo() void { | ||
| 83 | \\ const enum_Foo = extern enum(c_int) { | ||
| 84 | \\ A, | ||
| 85 | \\ B, | ||
| 86 | \\ C, | ||
| 87 | \\ _, | ||
| 88 | \\ }; | ||
| 89 | \\ const A = @enumToInt(enum_Foo.A); | ||
| 90 | \\ const B = @enumToInt(enum_Foo.B); | ||
| 91 | \\ const C = @enumToInt(enum_Foo.C); | ||
| 92 | \\ var a: enum_Foo = @intToEnum(enum_Foo, B); | ||
| 93 | \\ { | ||
| 94 | \\ const enum_Foo = extern enum(c_int) { | ||
| 95 | \\ A, | ||
| 96 | \\ B, | ||
| 97 | \\ C, | ||
| 98 | \\ _, | ||
| 99 | \\ }; | ||
| 100 | \\ const A_2 = @enumToInt(enum_Foo.A); | ||
| 101 | \\ const B_3 = @enumToInt(enum_Foo.B); | ||
| 102 | \\ const C_4 = @enumToInt(enum_Foo.C); | ||
| 103 | \\ var a_5: enum_Foo = @intToEnum(enum_Foo, B_3); | ||
| 104 | \\ } | ||
| 105 | \\} | ||
| 106 | }); | ||
| 107 | |||
| 108 | cases.add("scoped record", | ||
| 109 | \\void foo() { | ||
| 110 | \\	struct Foo { | ||
| 111 | \\		int A; | ||
| 112 | \\		int B; | ||
| 113 | \\		int C; | ||
| 114 | \\	}; | ||
| 115 | \\	struct Foo a = {0}; | ||
| 116 | \\	{ | ||
| 117 | \\		struct Foo { | ||
| 118 | \\			int A; | ||
| 119 | \\			int B; | ||
| 120 | \\			int C; | ||
| 121 | \\		}; | ||
| 122 | \\		struct Foo a = {0}; | ||
| 123 | \\	} | ||
| 124 | \\} | ||
| 125 | , &[_][]const u8{ | ||
| 126 | \\pub export fn foo() void { | ||
| 127 | \\ const struct_Foo = extern struct { | ||
| 128 | \\ A: c_int, | ||
| 129 | \\ B: c_int, | ||
| 130 | \\ C: c_int, | ||
| 131 | \\ }; | ||
| 132 | \\ var a: struct_Foo = struct_Foo{ | ||
| 133 | \\ .A = @as(c_int, 0), | ||
| 134 | \\ .B = 0, | ||
| 135 | \\ .C = 0, | ||
| 136 | \\ }; | ||
| 137 | \\ { | ||
| 138 | \\ const struct_Foo_1 = extern struct { | ||
| 139 | \\ A: c_int, | ||
| 140 | \\ B: c_int, | ||
| 141 | \\ C: c_int, | ||
| 142 | \\ }; | ||
| 143 | \\ var a_2: struct_Foo_1 = struct_Foo_1{ | ||
| 144 | \\ .A = @as(c_int, 0), | ||
| 145 | \\ .B = 0, | ||
| 146 | \\ .C = 0, | ||
| 147 | \\ }; | ||
| 148 | \\ } | ||
| 149 | \\} | ||
| 150 | }); | ||
| 151 | |||
| 152 | cases.add("scoped typedef", | ||
| 153 | \\void foo() { | ||
| 154 | \\	typedef union { | ||
| 155 | \\		int A; | ||
| 156 | \\		int B; | ||
| 157 | \\		int C; | ||
| 158 | \\	} Foo; | ||
| 159 | \\	Foo a = {0}; | ||
| 160 | \\	{ | ||
| 161 | \\		typedef union { | ||
| 162 | \\			int A; | ||
| 163 | \\			int B; | ||
| 164 | \\			int C; | ||
| 165 | \\		} Foo; | ||
| 166 | \\		Foo a = {0}; | ||
| 167 | \\	} | ||
| 168 | \\} | ||
| 169 | , &[_][]const u8{ | ||
| 170 | \\pub export fn foo() void { | ||
| 171 | \\ const union_unnamed_1 = extern union { | ||
| 172 | \\ A: c_int, | ||
| 173 | \\ B: c_int, | ||
| 174 | \\ C: c_int, | ||
| 175 | \\ }; | ||
| 176 | \\ const Foo = union_unnamed_1; | ||
| 177 | \\ var a: Foo = Foo{ | ||
| 178 | \\ .A = @as(c_int, 0), | ||
| 179 | \\ }; | ||
| 180 | \\ { | ||
| 181 | \\ const union_unnamed_2 = extern union { | ||
| 182 | \\ A: c_int, | ||
| 183 | \\ B: c_int, | ||
| 184 | \\ C: c_int, | ||
| 185 | \\ }; | ||
| 186 | \\ const Foo_1 = union_unnamed_2; | ||
| 187 | \\ var a_2: Foo_1 = Foo_1{ | ||
| 188 | \\ .A = @as(c_int, 0), | ||
| 189 | \\ }; | ||
| 190 | \\ } | ||
| 191 | \\} | ||
| 192 | }); | ||
| 193 | |||
| 194 | cases.add("use cast param as macro fn return type", | ||
| 195 | \\#define MEM_PHYSICAL_TO_K0(x) (void*)((u32)(x) + SYS_BASE_CACHED) | ||
| 196 | , &[_][]const u8{ | ||
| 197 | \\pub fn MEM_PHYSICAL_TO_K0(x: anytype) callconv(.Inline) ?*c_void { | ||
| 198 | \\ return @import("std").meta.cast(?*c_void, @import("std").meta.cast(u32, x) + SYS_BASE_CACHED); | ||
| 199 | \\} | ||
| 200 | }); | ||
| 201 | |||
| 202 | cases.add("variadic function demoted to extern", | ||
| 7 | \\int foo(int bar, ...) { | 203 | \\int foo(int bar, ...) { |
| 8 | \\ return 1; | 204 | \\ return 1; |
| 9 | \\} | 205 | \\} |
| 10 | , &[_][]const u8{ | 206 | , &[_][]const u8{ |
| 11 | \\warning: TODO unable to translate variadic function, demoted to declaration | 207 | \\warning: TODO unable to translate variadic function, demoted to extern |
| 12 | \\pub extern fn foo(bar: c_int, ...) c_int; | 208 | \\pub extern fn foo(bar: c_int, ...) c_int; |
| 13 | }); | 209 | }); |
| 14 | 210 | ||
| ... | @@ -21,11 +217,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -21,11 +217,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 21 | \\ Foo *bar; | 217 | \\ Foo *bar; |
| 22 | \\} Bar; | 218 | \\} Bar; |
| 23 | , &[_][]const u8{ | 219 | , &[_][]const u8{ |
| 24 | \\const struct_unnamed_1 = // | 220 | \\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo |
| 25 | , | 221 | \\const struct_unnamed_1 = opaque {}; |
| 26 | \\warning: unsupported type: 'Atomic' | ||
| 27 | \\ opaque {}; // | ||
| 28 | , | ||
| 29 | \\pub const Foo = struct_unnamed_1; | 222 | \\pub const Foo = struct_unnamed_1; |
| 30 | \\const struct_unnamed_2 = extern struct { | 223 | \\const struct_unnamed_2 = extern struct { |
| 31 | \\ bar: ?*Foo, | 224 | \\ bar: ?*Foo, |
| ... | @@ -43,8 +236,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -43,8 +236,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 43 | , | 236 | , |
| 44 | \\pub const VALUE = ((((1 + (2 * 3)) + (4 * 5)) + 6) << 7) | @boolToInt(8 == 9); | 237 | \\pub const VALUE = ((((1 + (2 * 3)) + (4 * 5)) + 6) << 7) | @boolToInt(8 == 9); |
| 45 | , | 238 | , |
| 46 | \\pub fn _AL_READ3BYTES(p: anytype) callconv(.Inline) @TypeOf(((@import("std").meta.cast([*c]u8, p)).* | (((@import("std").meta.cast([*c]u8, p)) + 1).* << 8)) | (((@import("std").meta.cast([*c]u8, p)) + 2).* << 16)) { | 239 | \\pub fn _AL_READ3BYTES(p: anytype) callconv(.Inline) @TypeOf((@import("std").meta.cast([*c]u8, p).* | ((@import("std").meta.cast([*c]u8, p) + 1).* << 8)) | ((@import("std").meta.cast([*c]u8, p) + 2).* << 16)) { |
| 47 | \\ return ((@import("std").meta.cast([*c]u8, p)).* | (((@import("std").meta.cast([*c]u8, p)) + 1).* << 8)) | (((@import("std").meta.cast([*c]u8, p)) + 2).* << 16); | 240 | \\ return (@import("std").meta.cast([*c]u8, p).* | ((@import("std").meta.cast([*c]u8, p) + 1).* << 8)) | ((@import("std").meta.cast([*c]u8, p) + 2).* << 16); |
| 48 | \\} | 241 | \\} |
| 49 | }); | 242 | }); |
| 50 | 243 | ||
| ... | @@ -57,7 +250,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -57,7 +250,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 57 | \\pub export var bar: f32 = @import("std").mem.zeroes(f32); | 250 | \\pub export var bar: f32 = @import("std").mem.zeroes(f32); |
| 58 | \\threadlocal var bar_1: c_int = 2; | 251 | \\threadlocal var bar_1: c_int = 2; |
| 59 | \\pub export fn foo() c_int { | 252 | \\pub export fn foo() c_int { |
| 60 | \\ _ = bar_1; | ||
| 61 | \\ return 0; | 253 | \\ return 0; |
| 62 | \\} | 254 | \\} |
| 63 | }); | 255 | }); |
| ... | @@ -107,7 +299,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -107,7 +299,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 107 | \\ int i1; | 299 | \\ int i1; |
| 108 | \\} boom_t; | 300 | \\} boom_t; |
| 109 | \\#define FOO ((boom_t){1}) | 301 | \\#define FOO ((boom_t){1}) |
| 110 | , &[_][]const u8{ // TODO properly translate this | 302 | , &[_][]const u8{ |
| 111 | \\pub const struct_Color = extern struct { | 303 | \\pub const struct_Color = extern struct { |
| 112 | \\ r: u8, | 304 | \\ r: u8, |
| 113 | \\ g: u8, | 305 | \\ g: u8, |
| ... | @@ -127,7 +319,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -127,7 +319,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 127 | \\}; | 319 | \\}; |
| 128 | \\pub const boom_t = struct_boom_t; | 320 | \\pub const boom_t = struct_boom_t; |
| 129 | , | 321 | , |
| 130 | \\pub const FOO = @import("std").mem.zeroInit(boom_t, .{ 1 }); | 322 | \\pub const FOO = @import("std").mem.zeroInit(boom_t, .{1}); |
| 131 | }); | 323 | }); |
| 132 | 324 | ||
| 133 | cases.add("complex switch", | 325 | cases.add("complex switch", |
| ... | @@ -142,14 +334,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -142,14 +334,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 142 | \\ } | 334 | \\ } |
| 143 | \\} | 335 | \\} |
| 144 | , &[_][]const u8{ // TODO properly translate this | 336 | , &[_][]const u8{ // TODO properly translate this |
| 145 | \\pub const main = @compileError("unable to translate function"); | 337 | \\source.h:5:13: warning: TODO complex switch |
| 338 | , | ||
| 339 | \\source.h:1:5: warning: unable to translate function, demoted to extern | ||
| 340 | \\pub extern fn main() c_int; | ||
| 146 | }); | 341 | }); |
| 147 | 342 | ||
| 148 | cases.add("correct semicolon after infixop", | 343 | cases.add("correct semicolon after infixop", |
| 149 | \\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0) | 344 | \\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0) |
| 150 | , &[_][]const u8{ | 345 | , &[_][]const u8{ |
| 151 | \\pub fn __ferror_unlocked_body(_fp: anytype) callconv(.Inline) @TypeOf(((_fp.*._flags) & _IO_ERR_SEEN) != 0) { | 346 | \\pub fn __ferror_unlocked_body(_fp: anytype) callconv(.Inline) @TypeOf((_fp.*._flags & _IO_ERR_SEEN) != 0) { |
| 152 | \\ return ((_fp.*._flags) & _IO_ERR_SEEN) != 0; | 347 | \\ return (_fp.*._flags & _IO_ERR_SEEN) != 0; |
| 153 | \\} | 348 | \\} |
| 154 | }); | 349 | }); |
| 155 | 350 | ||
| ... | @@ -193,9 +388,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -193,9 +388,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 193 | \\pub export fn foo() void { | 388 | \\pub export fn foo() void { |
| 194 | \\ while (false) while (false) {}; | 389 | \\ while (false) while (false) {}; |
| 195 | \\ while (true) while (false) {}; | 390 | \\ while (true) while (false) {}; |
| 196 | \\ while (true) while (true) { | 391 | \\ while (true) {} |
| 197 | \\ if (!false) break; | ||
| 198 | \\ }; | ||
| 199 | \\} | 392 | \\} |
| 200 | }); | 393 | }); |
| 201 | 394 | ||
| ... | @@ -245,15 +438,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -245,15 +438,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 245 | \\ volatile _Atomic int abufused[12]; | 438 | \\ volatile _Atomic int abufused[12]; |
| 246 | \\}; | 439 | \\}; |
| 247 | , &[_][]const u8{ | 440 | , &[_][]const u8{ |
| 248 | \\pub const struct_arcan_shmif_page = // | 441 | \\source.h:4:8: warning: struct demoted to opaque type - unable to translate type of field abufused |
| 249 | , | 442 | \\pub const struct_arcan_shmif_page = opaque {}; |
| 250 | \\warning: unsupported type: 'Atomic' | ||
| 251 | \\ opaque {}; // | ||
| 252 | , | ||
| 253 | \\ warning: struct demoted to opaque type - unable to translate type of field abufused | ||
| 254 | , // TODO should be `addr: *struct_arcan_shmif_page` | ||
| 255 | \\pub const struct_arcan_shmif_cont = extern struct { | 443 | \\pub const struct_arcan_shmif_cont = extern struct { |
| 256 | \\ addr: [*c]struct_arcan_shmif_page, | 444 | \\ addr: ?*struct_arcan_shmif_page, |
| 257 | \\}; | 445 | \\}; |
| 258 | }); | 446 | }); |
| 259 | 447 | ||
| ... | @@ -293,22 +481,22 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -293,22 +481,22 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 293 | , &[_][]const u8{ | 481 | , &[_][]const u8{ |
| 294 | \\pub const uuid_t = [16]u8; | 482 | \\pub const uuid_t = [16]u8; |
| 295 | \\pub const UUID_NULL: uuid_t = [16]u8{ | 483 | \\pub const UUID_NULL: uuid_t = [16]u8{ |
| 296 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 484 | \\ 0, |
| 297 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 485 | \\ 0, |
| 298 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 486 | \\ 0, |
| 299 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 487 | \\ 0, |
| 300 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 488 | \\ 0, |
| 301 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 489 | \\ 0, |
| 302 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 490 | \\ 0, |
| 303 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 491 | \\ 0, |
| 304 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 492 | \\ 0, |
| 305 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 493 | \\ 0, |
| 306 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 494 | \\ 0, |
| 307 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 495 | \\ 0, |
| 308 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 496 | \\ 0, |
| 309 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 497 | \\ 0, |
| 310 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 498 | \\ 0, |
| 311 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), | 499 | \\ 0, |
| 312 | \\}; | 500 | \\}; |
| 313 | }); | 501 | }); |
| 314 | 502 | ||
| ... | @@ -362,10 +550,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -362,10 +550,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 362 | \\}; | 550 | \\}; |
| 363 | \\pub export var ub: union_unnamed_1 = union_unnamed_1{ | 551 | \\pub export var ub: union_unnamed_1 = union_unnamed_1{ |
| 364 | \\ .c = [4]u8{ | 552 | \\ .c = [4]u8{ |
| 365 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 'a'))), | 553 | \\ 'a', |
| 366 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 'b'))), | 554 | \\ 'b', |
| 367 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 'b'))), | 555 | \\ 'b', |
| 368 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 'a'))), | 556 | \\ 'a', |
| 369 | \\ }, | 557 | \\ }, |
| 370 | \\}; | 558 | \\}; |
| 371 | }); | 559 | }); |
| ... | @@ -492,7 +680,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -492,7 +680,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 492 | , &[_][]const u8{ | 680 | , &[_][]const u8{ |
| 493 | \\pub export fn foo() void { | 681 | \\pub export fn foo() void { |
| 494 | \\ var a: c_int = undefined; | 682 | \\ var a: c_int = undefined; |
| 495 | \\ var b: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 123))); | 683 | \\ var b: u8 = 123; |
| 496 | \\ const c: c_int = undefined; | 684 | \\ const c: c_int = undefined; |
| 497 | \\ const d: c_uint = @bitCast(c_uint, @as(c_int, 440)); | 685 | \\ const d: c_uint = @bitCast(c_uint, @as(c_int, 440)); |
| 498 | \\ var e: c_int = 10; | 686 | \\ var e: c_int = 10; |
| ... | @@ -514,8 +702,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -514,8 +702,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 514 | \\ var a: c_int = undefined; | 702 | \\ var a: c_int = undefined; |
| 515 | \\ _ = @as(c_int, 1); | 703 | \\ _ = @as(c_int, 1); |
| 516 | \\ _ = "hey"; | 704 | \\ _ = "hey"; |
| 517 | \\ _ = (@as(c_int, 1) + @as(c_int, 1)); | 705 | \\ _ = @as(c_int, 1) + @as(c_int, 1); |
| 518 | \\ _ = (@as(c_int, 1) - @as(c_int, 1)); | 706 | \\ _ = @as(c_int, 1) - @as(c_int, 1); |
| 519 | \\ a = 1; | 707 | \\ a = 1; |
| 520 | \\} | 708 | \\} |
| 521 | }); | 709 | }); |
| ... | @@ -559,9 +747,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -559,9 +747,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 559 | \\ '2', | 747 | \\ '2', |
| 560 | \\ 0, | 748 | \\ 0, |
| 561 | \\}; | 749 | \\}; |
| 562 | \\pub export fn foo() void { | 750 | \\pub export fn foo() void {} |
| 563 | \\ _ = v2; | ||
| 564 | \\} | ||
| 565 | }); | 751 | }); |
| 566 | 752 | ||
| 567 | cases.add("simple function definition", | 753 | cases.add("simple function definition", |
| ... | @@ -634,9 +820,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -634,9 +820,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 634 | \\ var a: c_int = undefined; | 820 | \\ var a: c_int = undefined; |
| 635 | \\ var b: c_int = undefined; | 821 | \\ var b: c_int = undefined; |
| 636 | \\ var c: c_int = undefined; | 822 | \\ var c: c_int = undefined; |
| 637 | \\ c = (a + b); | 823 | \\ c = a + b; |
| 638 | \\ c = (a - b); | 824 | \\ c = a - b; |
| 639 | \\ c = (a * b); | 825 | \\ c = a * b; |
| 640 | \\ c = @divTrunc(a, b); | 826 | \\ c = @divTrunc(a, b); |
| 641 | \\ c = @rem(a, b); | 827 | \\ c = @rem(a, b); |
| 642 | \\ return 0; | 828 | \\ return 0; |
| ... | @@ -645,11 +831,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -645,11 +831,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 645 | \\ var a: c_uint = undefined; | 831 | \\ var a: c_uint = undefined; |
| 646 | \\ var b: c_uint = undefined; | 832 | \\ var b: c_uint = undefined; |
| 647 | \\ var c: c_uint = undefined; | 833 | \\ var c: c_uint = undefined; |
| 648 | \\ c = (a +% b); | 834 | \\ c = a +% b; |
| 649 | \\ c = (a -% b); | 835 | \\ c = a -% b; |
| 650 | \\ c = (a *% b); | 836 | \\ c = a *% b; |
| 651 | \\ c = (a / b); | 837 | \\ c = a / b; |
| 652 | \\ c = (a % b); | 838 | \\ c = a % b; |
| 653 | \\ return 0; | 839 | \\ return 0; |
| 654 | \\} | 840 | \\} |
| 655 | }); | 841 | }); |
| ... | @@ -914,13 +1100,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -914,13 +1100,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 914 | \\ ;;;;; | 1100 | \\ ;;;;; |
| 915 | \\} | 1101 | \\} |
| 916 | , &[_][]const u8{ | 1102 | , &[_][]const u8{ |
| 917 | \\pub export fn foo() void { | 1103 | \\pub export fn foo() void {} |
| 918 | \\ {} | ||
| 919 | \\ {} | ||
| 920 | \\ {} | ||
| 921 | \\ {} | ||
| 922 | \\ {} | ||
| 923 | \\} | ||
| 924 | }); | 1104 | }); |
| 925 | 1105 | ||
| 926 | if (std.Target.current.os.tag != .windows) { | 1106 | if (std.Target.current.os.tag != .windows) { |
| ... | @@ -1335,11 +1515,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1335,11 +1515,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1335 | \\extern enum enum_ty my_enum; | 1515 | \\extern enum enum_ty my_enum; |
| 1336 | \\enum enum_ty { FOO }; | 1516 | \\enum enum_ty { FOO }; |
| 1337 | , &[_][]const u8{ | 1517 | , &[_][]const u8{ |
| 1338 | \\pub const FOO = @enumToInt(enum_enum_ty.FOO); | ||
| 1339 | \\pub const enum_enum_ty = extern enum(c_int) { | 1518 | \\pub const enum_enum_ty = extern enum(c_int) { |
| 1340 | \\ FOO, | 1519 | \\ FOO, |
| 1341 | \\ _, | 1520 | \\ _, |
| 1342 | \\}; | 1521 | \\}; |
| 1522 | \\pub const FOO = @enumToInt(enum_enum_ty.FOO); | ||
| 1343 | \\pub extern var my_enum: enum_enum_ty; | 1523 | \\pub extern var my_enum: enum_enum_ty; |
| 1344 | }); | 1524 | }); |
| 1345 | 1525 | ||
| ... | @@ -1448,7 +1628,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1448,7 +1628,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1448 | , &[_][]const u8{ | 1628 | , &[_][]const u8{ |
| 1449 | \\pub fn foo() callconv(.C) void { | 1629 | \\pub fn foo() callconv(.C) void { |
| 1450 | \\ var arr: [10]u8 = [1]u8{ | 1630 | \\ var arr: [10]u8 = [1]u8{ |
| 1451 | \\ @bitCast(u8, @truncate(i8, @as(c_int, 1))), | 1631 | \\ 1, |
| 1452 | \\ } ++ [1]u8{0} ** 9; | 1632 | \\ } ++ [1]u8{0} ** 9; |
| 1453 | \\ var arr1: [10][*c]u8 = [1][*c]u8{ | 1633 | \\ var arr1: [10][*c]u8 = [1][*c]u8{ |
| 1454 | \\ null, | 1634 | \\ null, |
| ... | @@ -1481,48 +1661,48 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1481,48 +1661,48 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1481 | \\ p, | 1661 | \\ p, |
| 1482 | \\}; | 1662 | \\}; |
| 1483 | , &[_][]const u8{ | 1663 | , &[_][]const u8{ |
| 1484 | \\pub const a = @enumToInt(enum_unnamed_1.a); | ||
| 1485 | \\pub const b = @enumToInt(enum_unnamed_1.b); | ||
| 1486 | \\pub const c = @enumToInt(enum_unnamed_1.c); | ||
| 1487 | \\const enum_unnamed_1 = extern enum(c_int) { | 1664 | \\const enum_unnamed_1 = extern enum(c_int) { |
| 1488 | \\ a, | 1665 | \\ a, |
| 1489 | \\ b, | 1666 | \\ b, |
| 1490 | \\ c, | 1667 | \\ c, |
| 1491 | \\ _, | 1668 | \\ _, |
| 1492 | \\}; | 1669 | \\}; |
| 1670 | \\pub const a = @enumToInt(enum_unnamed_1.a); | ||
| 1671 | \\pub const b = @enumToInt(enum_unnamed_1.b); | ||
| 1672 | \\pub const c = @enumToInt(enum_unnamed_1.c); | ||
| 1493 | \\pub const d = enum_unnamed_1; | 1673 | \\pub const d = enum_unnamed_1; |
| 1494 | \\pub const e = @enumToInt(enum_unnamed_2.e); | ||
| 1495 | \\pub const f = @enumToInt(enum_unnamed_2.f); | ||
| 1496 | \\pub const g = @enumToInt(enum_unnamed_2.g); | ||
| 1497 | \\const enum_unnamed_2 = extern enum(c_int) { | 1674 | \\const enum_unnamed_2 = extern enum(c_int) { |
| 1498 | \\ e = 0, | 1675 | \\ e = 0, |
| 1499 | \\ f = 4, | 1676 | \\ f = 4, |
| 1500 | \\ g = 5, | 1677 | \\ g = 5, |
| 1501 | \\ _, | 1678 | \\ _, |
| 1502 | \\}; | 1679 | \\}; |
| 1680 | \\pub const e = @enumToInt(enum_unnamed_2.e); | ||
| 1681 | \\pub const f = @enumToInt(enum_unnamed_2.f); | ||
| 1682 | \\pub const g = @enumToInt(enum_unnamed_2.g); | ||
| 1503 | \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e); | 1683 | \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e); |
| 1504 | \\pub const i = @enumToInt(enum_unnamed_3.i); | ||
| 1505 | \\pub const j = @enumToInt(enum_unnamed_3.j); | ||
| 1506 | \\pub const k = @enumToInt(enum_unnamed_3.k); | ||
| 1507 | \\const enum_unnamed_3 = extern enum(c_int) { | 1684 | \\const enum_unnamed_3 = extern enum(c_int) { |
| 1508 | \\ i, | 1685 | \\ i, |
| 1509 | \\ j, | 1686 | \\ j, |
| 1510 | \\ k, | 1687 | \\ k, |
| 1511 | \\ _, | 1688 | \\ _, |
| 1512 | \\}; | 1689 | \\}; |
| 1690 | \\pub const i = @enumToInt(enum_unnamed_3.i); | ||
| 1691 | \\pub const j = @enumToInt(enum_unnamed_3.j); | ||
| 1692 | \\pub const k = @enumToInt(enum_unnamed_3.k); | ||
| 1513 | \\pub const struct_Baz = extern struct { | 1693 | \\pub const struct_Baz = extern struct { |
| 1514 | \\ l: enum_unnamed_3, | 1694 | \\ l: enum_unnamed_3, |
| 1515 | \\ m: d, | 1695 | \\ m: d, |
| 1516 | \\}; | 1696 | \\}; |
| 1517 | \\pub const n = @enumToInt(enum_i.n); | ||
| 1518 | \\pub const o = @enumToInt(enum_i.o); | ||
| 1519 | \\pub const p = @enumToInt(enum_i.p); | ||
| 1520 | \\pub const enum_i = extern enum(c_int) { | 1697 | \\pub const enum_i = extern enum(c_int) { |
| 1521 | \\ n, | 1698 | \\ n, |
| 1522 | \\ o, | 1699 | \\ o, |
| 1523 | \\ p, | 1700 | \\ p, |
| 1524 | \\ _, | 1701 | \\ _, |
| 1525 | \\}; | 1702 | \\}; |
| 1703 | \\pub const n = @enumToInt(enum_i.n); | ||
| 1704 | \\pub const o = @enumToInt(enum_i.o); | ||
| 1705 | \\pub const p = @enumToInt(enum_i.p); | ||
| 1526 | , | 1706 | , |
| 1527 | \\pub const Baz = struct_Baz; | 1707 | \\pub const Baz = struct_Baz; |
| 1528 | }); | 1708 | }); |
| ... | @@ -1639,7 +1819,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1639,7 +1819,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1639 | cases.add("macro pointer cast", | 1819 | cases.add("macro pointer cast", |
| 1640 | \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) | 1820 | \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) |
| 1641 | , &[_][]const u8{ | 1821 | , &[_][]const u8{ |
| 1642 | \\pub const NRF_GPIO = (@import("std").meta.cast([*c]NRF_GPIO_Type, NRF_GPIO_BASE)); | 1822 | \\pub const NRF_GPIO = @import("std").meta.cast([*c]NRF_GPIO_Type, NRF_GPIO_BASE); |
| 1643 | }); | 1823 | }); |
| 1644 | 1824 | ||
| 1645 | cases.add("basic macro function", | 1825 | cases.add("basic macro function", |
| ... | @@ -1701,13 +1881,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1701,13 +1881,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1701 | \\ unsigned d = 440; | 1881 | \\ unsigned d = 440; |
| 1702 | \\} | 1882 | \\} |
| 1703 | , &[_][]const u8{ | 1883 | , &[_][]const u8{ |
| 1704 | \\pub var a: c_long = @bitCast(c_long, @as(c_long, @as(c_int, 2))); | 1884 | \\pub var a: c_long = 2; |
| 1705 | \\pub var b: c_long = @bitCast(c_long, @as(c_long, @as(c_int, 2))); | 1885 | \\pub var b: c_long = 2; |
| 1706 | \\pub var c: c_int = 4; | 1886 | \\pub var c: c_int = 4; |
| 1707 | \\pub export fn foo(arg_c_1: u8) void { | 1887 | \\pub export fn foo(arg_c_1: u8) void { |
| 1708 | \\ var c_1 = arg_c_1; | 1888 | \\ var c_1 = arg_c_1; |
| 1709 | \\ var a_2: c_int = undefined; | 1889 | \\ var a_2: c_int = undefined; |
| 1710 | \\ var b_3: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 123))); | 1890 | \\ var b_3: u8 = 123; |
| 1711 | \\ b_3 = @bitCast(u8, @truncate(i8, a_2)); | 1891 | \\ b_3 = @bitCast(u8, @truncate(i8, a_2)); |
| 1712 | \\ { | 1892 | \\ { |
| 1713 | \\ var d: c_int = 5; | 1893 | \\ var d: c_int = 5; |
| ... | @@ -1723,17 +1903,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1723,17 +1903,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1723 | \\} | 1903 | \\} |
| 1724 | , &[_][]const u8{ | 1904 | , &[_][]const u8{ |
| 1725 | \\pub export fn foo() c_int { | 1905 | \\pub export fn foo() c_int { |
| 1726 | \\ _ = (blk: { | 1906 | \\ _ = blk: { |
| 1727 | \\ _ = @as(c_int, 2); | 1907 | \\ _ = @as(c_int, 2); |
| 1728 | \\ break :blk @as(c_int, 4); | 1908 | \\ break :blk @as(c_int, 4); |
| 1729 | \\ }); | 1909 | \\ }; |
| 1730 | \\ return (blk: { | 1910 | \\ return blk: { |
| 1731 | \\ _ = (blk_1: { | 1911 | \\ _ = blk_1: { |
| 1732 | \\ _ = @as(c_int, 2); | 1912 | \\ _ = @as(c_int, 2); |
| 1733 | \\ break :blk_1 @as(c_int, 4); | 1913 | \\ break :blk_1 @as(c_int, 4); |
| 1734 | \\ }); | 1914 | \\ }; |
| 1735 | \\ break :blk @as(c_int, 6); | 1915 | \\ break :blk @as(c_int, 6); |
| 1736 | \\ }); | 1916 | \\ }; |
| 1737 | \\} | 1917 | \\} |
| 1738 | }); | 1918 | }); |
| 1739 | 1919 | ||
| ... | @@ -1780,20 +1960,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1780,20 +1960,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1780 | \\ while (true) { | 1960 | \\ while (true) { |
| 1781 | \\ var a_1: c_int = 4; | 1961 | \\ var a_1: c_int = 4; |
| 1782 | \\ a_1 = 9; | 1962 | \\ a_1 = 9; |
| 1783 | \\ return (blk: { | 1963 | \\ return blk: { |
| 1784 | \\ _ = @as(c_int, 6); | 1964 | \\ _ = @as(c_int, 6); |
| 1785 | \\ break :blk a_1; | 1965 | \\ break :blk a_1; |
| 1786 | \\ }); | 1966 | \\ }; |
| 1787 | \\ } | 1967 | \\ } |
| 1788 | \\ while (true) { | 1968 | \\ while (true) { |
| 1789 | \\ var a_1: c_int = 2; | 1969 | \\ var a_1: c_int = 2; |
| 1790 | \\ a_1 = 12; | 1970 | \\ a_1 = 12; |
| 1791 | \\ if (!true) break; | ||
| 1792 | \\ } | ||
| 1793 | \\ while (true) { | ||
| 1794 | \\ a = 7; | ||
| 1795 | \\ if (!true) break; | ||
| 1796 | \\ } | 1971 | \\ } |
| 1972 | \\ while (true) a = 7; | ||
| 1797 | \\ return 0; | 1973 | \\ return 0; |
| 1798 | \\} | 1974 | \\} |
| 1799 | }); | 1975 | }); |
| ... | @@ -1813,16 +1989,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1813,16 +1989,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1813 | \\ var b: c_int = 4; | 1989 | \\ var b: c_int = 4; |
| 1814 | \\ while ((i + @as(c_int, 2)) != 0) : (i = 2) { | 1990 | \\ while ((i + @as(c_int, 2)) != 0) : (i = 2) { |
| 1815 | \\ var a: c_int = 2; | 1991 | \\ var a: c_int = 2; |
| 1816 | \\ _ = (blk: { | 1992 | \\ _ = blk: { |
| 1817 | \\ _ = (blk_1: { | 1993 | \\ _ = blk_1: { |
| 1818 | \\ a = 6; | 1994 | \\ a = 6; |
| 1819 | \\ break :blk_1 @as(c_int, 5); | 1995 | \\ break :blk_1 @as(c_int, 5); |
| 1820 | \\ }); | 1996 | \\ }; |
| 1821 | \\ break :blk @as(c_int, 7); | 1997 | \\ break :blk @as(c_int, 7); |
| 1822 | \\ }); | 1998 | \\ }; |
| 1823 | \\ } | 1999 | \\ } |
| 1824 | \\ } | 2000 | \\ } |
| 1825 | \\ var i: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 2))); | 2001 | \\ var i: u8 = 2; |
| 1826 | \\} | 2002 | \\} |
| 1827 | }); | 2003 | }); |
| 1828 | 2004 | ||
| ... | @@ -1830,7 +2006,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1830,7 +2006,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1830 | \\unsigned anyerror = 2; | 2006 | \\unsigned anyerror = 2; |
| 1831 | \\#define noreturn _Noreturn | 2007 | \\#define noreturn _Noreturn |
| 1832 | , &[_][]const u8{ | 2008 | , &[_][]const u8{ |
| 1833 | \\pub export var anyerror_1: c_uint = @bitCast(c_uint, @as(c_int, 2)); | 2009 | \\pub export var anyerror_1: c_uint = 2; |
| 1834 | , | 2010 | , |
| 1835 | \\pub const noreturn_2 = @compileError("unable to translate C expr: unexpected token .Keyword_noreturn"); | 2011 | \\pub const noreturn_2 = @compileError("unable to translate C expr: unexpected token .Keyword_noreturn"); |
| 1836 | }); | 2012 | }); |
| ... | @@ -1844,7 +2020,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1844,7 +2020,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1844 | \\pub export var a: f32 = @floatCast(f32, 3.1415); | 2020 | \\pub export var a: f32 = @floatCast(f32, 3.1415); |
| 1845 | \\pub export var b: f64 = 3.1415; | 2021 | \\pub export var b: f64 = 3.1415; |
| 1846 | \\pub export var c: c_int = @floatToInt(c_int, 3.1415); | 2022 | \\pub export var c: c_int = @floatToInt(c_int, 3.1415); |
| 1847 | \\pub export var d: f64 = @intToFloat(f64, @as(c_int, 3)); | 2023 | \\pub export var d: f64 = 3; |
| 1848 | }); | 2024 | }); |
| 1849 | 2025 | ||
| 1850 | cases.add("conditional operator", | 2026 | cases.add("conditional operator", |
| ... | @@ -1854,7 +2030,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1854,7 +2030,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1854 | \\} | 2030 | \\} |
| 1855 | , &[_][]const u8{ | 2031 | , &[_][]const u8{ |
| 1856 | \\pub export fn bar() c_int { | 2032 | \\pub export fn bar() c_int { |
| 1857 | \\ if ((if (true) @as(c_int, 5) else (if (true) @as(c_int, 4) else @as(c_int, 6))) != 0) _ = @as(c_int, 2); | 2033 | \\ if ((if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6)) != 0) _ = @as(c_int, 2); |
| 1858 | \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6); | 2034 | \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6); |
| 1859 | \\} | 2035 | \\} |
| 1860 | }); | 2036 | }); |
| ... | @@ -1870,34 +2046,64 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1870,34 +2046,64 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1870 | \\ default: | 2046 | \\ default: |
| 1871 | \\ res = 3 * i; | 2047 | \\ res = 3 * i; |
| 1872 | \\ break; | 2048 | \\ break; |
| 2049 | \\ break; | ||
| 2050 | \\ case 7: { | ||
| 2051 | \\ res = 7; | ||
| 2052 | \\ break; | ||
| 2053 | \\ } | ||
| 1873 | \\ case 4: | 2054 | \\ case 4: |
| 2055 | \\		case 5: | ||
| 2056 | \\ res = 69; | ||
| 2057 | \\ { | ||
| 1874 | \\ res = 5; | 2058 | \\ res = 5; |
| 2059 | \\			 return; | ||
| 2060 | \\ } | ||
| 2061 | \\ case 6: | ||
| 2062 | \\ switch (res) { | ||
| 2063 | \\ case 9: break; | ||
| 2064 | \\ } | ||
| 2065 | \\ res = 1; | ||
| 2066 | \\			 return; | ||
| 1875 | \\ } | 2067 | \\ } |
| 1876 | \\} | 2068 | \\} |
| 1877 | , &[_][]const u8{ | 2069 | , &[_][]const u8{ |
| 1878 | \\pub export fn switch_fn(arg_i: c_int) void { | 2070 | \\pub export fn switch_fn(arg_i: c_int) void { |
| 1879 | \\ var i = arg_i; | 2071 | \\ var i = arg_i; |
| 1880 | \\ var res: c_int = 0; | 2072 | \\ var res: c_int = 0; |
| 1881 | \\ @"switch": { | 2073 | \\ switch (i) { |
| 1882 | \\ case_2: { | 2074 | \\ @as(c_int, 0) => { |
| 1883 | \\ default: { | 2075 | \\ res = 1; |
| 1884 | \\ case_1: { | 2076 | \\ res = 2; |
| 1885 | \\ case: { | 2077 | \\ res = @as(c_int, 3) * i; |
| 1886 | \\ switch (i) { | 2078 | \\ }, |
| 1887 | \\ @as(c_int, 0) => break :case, | 2079 | \\ @as(c_int, 1)...@as(c_int, 3) => { |
| 1888 | \\ @as(c_int, 1)...@as(c_int, 3) => break :case_1, | 2080 | \\ res = 2; |
| 1889 | \\ else => break :default, | 2081 | \\ res = @as(c_int, 3) * i; |
| 1890 | \\ @as(c_int, 4) => break :case_2, | 2082 | \\ }, |
| 1891 | \\ } | 2083 | \\ else => { |
| 1892 | \\ } | 2084 | \\ res = @as(c_int, 3) * i; |
| 1893 | \\ res = 1; | 2085 | \\ }, |
| 1894 | \\ } | 2086 | \\ @as(c_int, 7) => { |
| 1895 | \\ res = 2; | 2087 | \\ { |
| 2088 | \\ res = 7; | ||
| 2089 | \\ break; | ||
| 1896 | \\ } | 2090 | \\ } |
| 1897 | \\ res = (@as(c_int, 3) * i); | 2091 | \\ }, |
| 1898 | \\ break :@"switch"; | 2092 | \\ @as(c_int, 4), @as(c_int, 5) => { |
| 1899 | \\ } | 2093 | \\ res = 69; |
| 1900 | \\ res = 5; | 2094 | \\ { |
| 2095 | \\ res = 5; | ||
| 2096 | \\ return; | ||
| 2097 | \\ } | ||
| 2098 | \\ }, | ||
| 2099 | \\ @as(c_int, 6) => { | ||
| 2100 | \\ switch (res) { | ||
| 2101 | \\ @as(c_int, 9) => {}, | ||
| 2102 | \\ else => {}, | ||
| 2103 | \\ } | ||
| 2104 | \\ res = 1; | ||
| 2105 | \\ return; | ||
| 2106 | \\ }, | ||
| 1901 | \\ } | 2107 | \\ } |
| 1902 | \\} | 2108 | \\} |
| 1903 | }); | 2109 | }); |
| ... | @@ -1973,13 +2179,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1973,13 +2179,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1973 | \\ Two, | 2179 | \\ Two, |
| 1974 | \\}; | 2180 | \\}; |
| 1975 | , &[_][]const u8{ | 2181 | , &[_][]const u8{ |
| 1976 | \\pub const One = @enumToInt(enum_unnamed_1.One); | ||
| 1977 | \\pub const Two = @enumToInt(enum_unnamed_1.Two); | ||
| 1978 | \\const enum_unnamed_1 = extern enum(c_int) { | 2182 | \\const enum_unnamed_1 = extern enum(c_int) { |
| 1979 | \\ One, | 2183 | \\ One, |
| 1980 | \\ Two, | 2184 | \\ Two, |
| 1981 | \\ _, | 2185 | \\ _, |
| 1982 | \\}; | 2186 | \\}; |
| 2187 | \\pub const One = @enumToInt(enum_unnamed_1.One); | ||
| 2188 | \\pub const Two = @enumToInt(enum_unnamed_1.Two); | ||
| 1983 | }); | 2189 | }); |
| 1984 | 2190 | ||
| 1985 | cases.add("c style cast", | 2191 | cases.add("c style cast", |
| ... | @@ -1993,7 +2199,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1993,7 +2199,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1993 | \\} | 2199 | \\} |
| 1994 | }); | 2200 | }); |
| 1995 | 2201 | ||
| 1996 | // TODO translate-c should in theory be able to figure out to drop all these casts | ||
| 1997 | cases.add("escape sequences", | 2202 | cases.add("escape sequences", |
| 1998 | \\const char *escapes() { | 2203 | \\const char *escapes() { |
| 1999 | \\char a = '\'', | 2204 | \\char a = '\'', |
| ... | @@ -2012,17 +2217,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2012,17 +2217,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2012 | \\ | 2217 | \\ |
| 2013 | , &[_][]const u8{ | 2218 | , &[_][]const u8{ |
| 2014 | \\pub export fn escapes() [*c]const u8 { | 2219 | \\pub export fn escapes() [*c]const u8 { |
| 2015 | \\ var a: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\''))); | 2220 | \\ var a: u8 = '\''; |
| 2016 | \\ var b: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\\'))); | 2221 | \\ var b: u8 = '\\'; |
| 2017 | \\ var c: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x07'))); | 2222 | \\ var c: u8 = '\x07'; |
| 2018 | \\ var d: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x08'))); | 2223 | \\ var d: u8 = '\x08'; |
| 2019 | \\ var e: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x0c'))); | 2224 | \\ var e: u8 = '\x0c'; |
| 2020 | \\ var f: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\n'))); | 2225 | \\ var f: u8 = '\n'; |
| 2021 | \\ var g: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\r'))); | 2226 | \\ var g: u8 = '\r'; |
| 2022 | \\ var h: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\t'))); | 2227 | \\ var h: u8 = '\t'; |
| 2023 | \\ var i: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x0b'))); | 2228 | \\ var i: u8 = '\x0b'; |
| 2024 | \\ var j: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x00'))); | 2229 | \\ var j: u8 = '\x00'; |
| 2025 | \\ var k: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\"'))); | 2230 | \\ var k: u8 = '\"'; |
| 2026 | \\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\""; | 2231 | \\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\""; |
| 2027 | \\} | 2232 | \\} |
| 2028 | }); | 2233 | }); |
| ... | @@ -2043,12 +2248,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2043,12 +2248,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2043 | \\pub export fn foo() void { | 2248 | \\pub export fn foo() void { |
| 2044 | \\ var a: c_int = 2; | 2249 | \\ var a: c_int = 2; |
| 2045 | \\ while (true) { | 2250 | \\ while (true) { |
| 2046 | \\ a = (a - @as(c_int, 1)); | 2251 | \\ a = a - @as(c_int, 1); |
| 2047 | \\ if (!(a != 0)) break; | 2252 | \\ if (!(a != 0)) break; |
| 2048 | \\ } | 2253 | \\ } |
| 2049 | \\ var b: c_int = 2; | 2254 | \\ var b: c_int = 2; |
| 2050 | \\ while (true) { | 2255 | \\ while (true) { |
| 2051 | \\ b = (b - @as(c_int, 1)); | 2256 | \\ b = b - @as(c_int, 1); |
| 2052 | \\ if (!(b != 0)) break; | 2257 | \\ if (!(b != 0)) break; |
| 2053 | \\ } | 2258 | \\ } |
| 2054 | \\} | 2259 | \\} |
| ... | @@ -2084,25 +2289,28 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2084,25 +2289,28 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2084 | \\ C, | 2289 | \\ C, |
| 2085 | \\ _, | 2290 | \\ _, |
| 2086 | \\}; | 2291 | \\}; |
| 2292 | \\pub const FooA = @enumToInt(enum_Foo.A); | ||
| 2293 | \\pub const FooB = @enumToInt(enum_Foo.B); | ||
| 2294 | \\pub const FooC = @enumToInt(enum_Foo.C); | ||
| 2087 | \\pub const SomeTypedef = c_int; | 2295 | \\pub const SomeTypedef = c_int; |
| 2088 | \\pub export fn and_or_non_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void) c_int { | 2296 | \\pub export fn and_or_non_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void) c_int { |
| 2089 | \\ var a = arg_a; | 2297 | \\ var a = arg_a; |
| 2090 | \\ var b = arg_b; | 2298 | \\ var b = arg_b; |
| 2091 | \\ var c = arg_c; | 2299 | \\ var c = arg_c; |
| 2092 | \\ var d: enum_Foo = @intToEnum(enum_Foo, FooA); | 2300 | \\ var d: enum_Foo = @intToEnum(enum_Foo, FooA); |
| 2093 | \\ var e: c_int = @boolToInt(((a != 0) and (b != 0))); | 2301 | \\ var e: c_int = @boolToInt((a != 0) and (b != 0)); |
| 2094 | \\ var f: c_int = @boolToInt(((b != 0) and (c != null))); | 2302 | \\ var f: c_int = @boolToInt((b != 0) and (c != null)); |
| 2095 | \\ var g: c_int = @boolToInt(((a != 0) and (c != null))); | 2303 | \\ var g: c_int = @boolToInt((a != 0) and (c != null)); |
| 2096 | \\ var h: c_int = @boolToInt(((a != 0) or (b != 0))); | 2304 | \\ var h: c_int = @boolToInt((a != 0) or (b != 0)); |
| 2097 | \\ var i: c_int = @boolToInt(((b != 0) or (c != null))); | 2305 | \\ var i: c_int = @boolToInt((b != 0) or (c != null)); |
| 2098 | \\ var j: c_int = @boolToInt(((a != 0) or (c != null))); | 2306 | \\ var j: c_int = @boolToInt((a != 0) or (c != null)); |
| 2099 | \\ var k: c_int = @boolToInt(((a != 0) or (@bitCast(c_int, @enumToInt(d)) != 0))); | 2307 | \\ var k: c_int = @boolToInt((a != 0) or (@bitCast(c_int, @enumToInt(d)) != 0)); |
| 2100 | \\ var l: c_int = @boolToInt(((@bitCast(c_int, @enumToInt(d)) != 0) and (b != 0))); | 2308 | \\ var l: c_int = @boolToInt((@bitCast(c_int, @enumToInt(d)) != 0) and (b != 0)); |
| 2101 | \\ var m: c_int = @boolToInt(((c != null) or (@bitCast(c_uint, @enumToInt(d)) != 0))); | 2309 | \\ var m: c_int = @boolToInt((c != null) or (@bitCast(c_uint, @enumToInt(d)) != 0)); |
| 2102 | \\ var td: SomeTypedef = 44; | 2310 | \\ var td: SomeTypedef = 44; |
| 2103 | \\ var o: c_int = @boolToInt(((td != 0) or (b != 0))); | 2311 | \\ var o: c_int = @boolToInt((td != 0) or (b != 0)); |
| 2104 | \\ var p: c_int = @boolToInt(((c != null) and (td != 0))); | 2312 | \\ var p: c_int = @boolToInt((c != null) and (td != 0)); |
| 2105 | \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p); | 2313 | \\ return (((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p; |
| 2106 | \\} | 2314 | \\} |
| 2107 | , | 2315 | , |
| 2108 | \\pub const Foo = enum_Foo; | 2316 | \\pub const Foo = enum_Foo; |
| ... | @@ -2129,6 +2337,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2129,6 +2337,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2129 | \\ B, | 2337 | \\ B, |
| 2130 | \\ _, | 2338 | \\ _, |
| 2131 | \\}; | 2339 | \\}; |
| 2340 | \\pub const BarA = @enumToInt(enum_Bar.A); | ||
| 2341 | \\pub const BarB = @enumToInt(enum_Bar.B); | ||
| 2132 | \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void; | 2342 | \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void; |
| 2133 | , | 2343 | , |
| 2134 | \\pub const Foo = struct_Foo; | 2344 | \\pub const Foo = struct_Foo; |
| ... | @@ -2143,7 +2353,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2143,7 +2353,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2143 | \\pub export fn max(arg_a: c_int, arg_b: c_int) c_int { | 2353 | \\pub export fn max(arg_a: c_int, arg_b: c_int) c_int { |
| 2144 | \\ var a = arg_a; | 2354 | \\ var a = arg_a; |
| 2145 | \\ var b = arg_b; | 2355 | \\ var b = arg_b; |
| 2146 | \\ return ((a & b) ^ (a | b)); | 2356 | \\ return (a & b) ^ (a | b); |
| 2147 | \\} | 2357 | \\} |
| 2148 | }); | 2358 | }); |
| 2149 | 2359 | ||
| ... | @@ -2162,13 +2372,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2162,13 +2372,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2162 | \\pub export fn test_comparisons(arg_a: c_int, arg_b: c_int) c_int { | 2372 | \\pub export fn test_comparisons(arg_a: c_int, arg_b: c_int) c_int { |
| 2163 | \\ var a = arg_a; | 2373 | \\ var a = arg_a; |
| 2164 | \\ var b = arg_b; | 2374 | \\ var b = arg_b; |
| 2165 | \\ var c: c_int = @boolToInt((a < b)); | 2375 | \\ var c: c_int = @boolToInt(a < b); |
| 2166 | \\ var d: c_int = @boolToInt((a > b)); | 2376 | \\ var d: c_int = @boolToInt(a > b); |
| 2167 | \\ var e: c_int = @boolToInt((a <= b)); | 2377 | \\ var e: c_int = @boolToInt(a <= b); |
| 2168 | \\ var f: c_int = @boolToInt((a >= b)); | 2378 | \\ var f: c_int = @boolToInt(a >= b); |
| 2169 | \\ var g: c_int = @boolToInt((c < d)); | 2379 | \\ var g: c_int = @boolToInt(c < d); |
| 2170 | \\ var h: c_int = @boolToInt((e < f)); | 2380 | \\ var h: c_int = @boolToInt(e < f); |
| 2171 | \\ var i: c_int = @boolToInt((g < h)); | 2381 | \\ var i: c_int = @boolToInt(g < h); |
| 2172 | \\ return i; | 2382 | \\ return i; |
| 2173 | \\} | 2383 | \\} |
| 2174 | }); | 2384 | }); |
| ... | @@ -2215,11 +2425,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2215,11 +2425,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2215 | \\} | 2425 | \\} |
| 2216 | , &[_][]const u8{ | 2426 | , &[_][]const u8{ |
| 2217 | \\pub export fn foo() c_int { | 2427 | \\pub export fn foo() c_int { |
| 2218 | \\ return (blk: { | 2428 | \\ return blk: { |
| 2219 | \\ var a: c_int = 1; | 2429 | \\ var a: c_int = 1; |
| 2220 | \\ _ = a; | 2430 | \\ _ = a; |
| 2221 | \\ break :blk a; | 2431 | \\ break :blk a; |
| 2222 | \\ }); | 2432 | \\ }; |
| 2223 | \\} | 2433 | \\} |
| 2224 | }); | 2434 | }); |
| 2225 | 2435 | ||
| ... | @@ -2289,8 +2499,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2289,8 +2499,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2289 | , &[_][]const u8{ | 2499 | , &[_][]const u8{ |
| 2290 | \\pub export fn foo() void { | 2500 | \\pub export fn foo() void { |
| 2291 | \\ var a: [10]c_longlong = undefined; | 2501 | \\ var a: [10]c_longlong = undefined; |
| 2292 | \\ var i: c_longlong = @bitCast(c_longlong, @as(c_longlong, @as(c_int, 0))); | 2502 | \\ var i: c_longlong = 0; |
| 2293 | \\ a[@intCast(usize, i)] = @bitCast(c_longlong, @as(c_longlong, @as(c_int, 0))); | 2503 | \\ a[@intCast(usize, i)] = 0; |
| 2294 | \\} | 2504 | \\} |
| 2295 | }); | 2505 | }); |
| 2296 | 2506 | ||
| ... | @@ -2302,8 +2512,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2302,8 +2512,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2302 | , &[_][]const u8{ | 2512 | , &[_][]const u8{ |
| 2303 | \\pub export fn foo() void { | 2513 | \\pub export fn foo() void { |
| 2304 | \\ var a: [10]c_uint = undefined; | 2514 | \\ var a: [10]c_uint = undefined; |
| 2305 | \\ var i: c_uint = @bitCast(c_uint, @as(c_int, 0)); | 2515 | \\ var i: c_uint = 0; |
| 2306 | \\ a[i] = @bitCast(c_uint, @as(c_int, 0)); | 2516 | \\ a[i] = 0; |
| 2307 | \\} | 2517 | \\} |
| 2308 | }); | 2518 | }); |
| 2309 | 2519 | ||
| ... | @@ -2395,6 +2605,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2395,6 +2605,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2395 | \\ C, | 2605 | \\ C, |
| 2396 | \\ _, | 2606 | \\ _, |
| 2397 | \\}; | 2607 | \\}; |
| 2608 | \\pub const A = @enumToInt(enum_SomeEnum.A); | ||
| 2609 | \\pub const B = @enumToInt(enum_SomeEnum.B); | ||
| 2610 | \\pub const C = @enumToInt(enum_SomeEnum.C); | ||
| 2398 | \\pub export fn if_none_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void, arg_d: enum_SomeEnum) c_int { | 2611 | \\pub export fn if_none_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void, arg_d: enum_SomeEnum) c_int { |
| 2399 | \\ var a = arg_a; | 2612 | \\ var a = arg_a; |
| 2400 | \\ var b = arg_b; | 2613 | \\ var b = arg_b; |
| ... | @@ -2484,10 +2697,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2484,10 +2697,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2484 | \\ var f: ?fn () callconv(.C) void = foo; | 2697 | \\ var f: ?fn () callconv(.C) void = foo; |
| 2485 | \\ var b: ?fn () callconv(.C) c_int = baz; | 2698 | \\ var b: ?fn () callconv(.C) c_int = baz; |
| 2486 | \\ f.?(); | 2699 | \\ f.?(); |
| 2487 | \\ (f).?(); | 2700 | \\ f.?(); |
| 2488 | \\ foo(); | 2701 | \\ foo(); |
| 2489 | \\ _ = b.?(); | 2702 | \\ _ = b.?(); |
| 2490 | \\ _ = (b).?(); | 2703 | \\ _ = b.?(); |
| 2491 | \\ _ = baz(); | 2704 | \\ _ = baz(); |
| 2492 | \\} | 2705 | \\} |
| 2493 | }); | 2706 | }); |
| ... | @@ -2508,31 +2721,31 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2508,31 +2721,31 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2508 | , &[_][]const u8{ | 2721 | , &[_][]const u8{ |
| 2509 | \\pub export fn foo() void { | 2722 | \\pub export fn foo() void { |
| 2510 | \\ var i: c_int = 0; | 2723 | \\ var i: c_int = 0; |
| 2511 | \\ var u: c_uint = @bitCast(c_uint, @as(c_int, 0)); | 2724 | \\ var u: c_uint = 0; |
| 2512 | \\ i += 1; | 2725 | \\ i += 1; |
| 2513 | \\ i -= 1; | 2726 | \\ i -= 1; |
| 2514 | \\ u +%= 1; | 2727 | \\ u +%= 1; |
| 2515 | \\ u -%= 1; | 2728 | \\ u -%= 1; |
| 2516 | \\ i = (blk: { | 2729 | \\ i = blk: { |
| 2517 | \\ const ref = &i; | 2730 | \\ const ref = &i; |
| 2518 | \\ ref.* += 1; | 2731 | \\ ref.* += 1; |
| 2519 | \\ break :blk ref.*; | 2732 | \\ break :blk ref.*; |
| 2520 | \\ }); | 2733 | \\ }; |
| 2521 | \\ i = (blk: { | 2734 | \\ i = blk: { |
| 2522 | \\ const ref = &i; | 2735 | \\ const ref = &i; |
| 2523 | \\ ref.* -= 1; | 2736 | \\ ref.* -= 1; |
| 2524 | \\ break :blk ref.*; | 2737 | \\ break :blk ref.*; |
| 2525 | \\ }); | 2738 | \\ }; |
| 2526 | \\ u = (blk: { | 2739 | \\ u = blk: { |
| 2527 | \\ const ref = &u; | 2740 | \\ const ref = &u; |
| 2528 | \\ ref.* +%= 1; | 2741 | \\ ref.* +%= 1; |
| 2529 | \\ break :blk ref.*; | 2742 | \\ break :blk ref.*; |
| 2530 | \\ }); | 2743 | \\ }; |
| 2531 | \\ u = (blk: { | 2744 | \\ u = blk: { |
| 2532 | \\ const ref = &u; | 2745 | \\ const ref = &u; |
| 2533 | \\ ref.* -%= 1; | 2746 | \\ ref.* -%= 1; |
| 2534 | \\ break :blk ref.*; | 2747 | \\ break :blk ref.*; |
| 2535 | \\ }); | 2748 | \\ }; |
| 2536 | \\} | 2749 | \\} |
| 2537 | }); | 2750 | }); |
| 2538 | 2751 | ||
| ... | @@ -2595,67 +2808,67 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2595,67 +2808,67 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2595 | , &[_][]const u8{ | 2808 | , &[_][]const u8{ |
| 2596 | \\pub export fn foo() void { | 2809 | \\pub export fn foo() void { |
| 2597 | \\ var a: c_int = 0; | 2810 | \\ var a: c_int = 0; |
| 2598 | \\ var b: c_uint = @bitCast(c_uint, @as(c_int, 0)); | 2811 | \\ var b: c_uint = 0; |
| 2599 | \\ a += (blk: { | 2812 | \\ a += blk: { |
| 2600 | \\ const ref = &a; | 2813 | \\ const ref = &a; |
| 2601 | \\ ref.* = ref.* + @as(c_int, 1); | 2814 | \\ ref.* += @as(c_int, 1); |
| 2602 | \\ break :blk ref.*; | 2815 | \\ break :blk ref.*; |
| 2603 | \\ }); | 2816 | \\ }; |
| 2604 | \\ a -= (blk: { | 2817 | \\ a -= blk: { |
| 2605 | \\ const ref = &a; | 2818 | \\ const ref = &a; |
| 2606 | \\ ref.* = ref.* - @as(c_int, 1); | 2819 | \\ ref.* -= @as(c_int, 1); |
| 2607 | \\ break :blk ref.*; | 2820 | \\ break :blk ref.*; |
| 2608 | \\ }); | 2821 | \\ }; |
| 2609 | \\ a *= (blk: { | 2822 | \\ a *= blk: { |
| 2610 | \\ const ref = &a; | 2823 | \\ const ref = &a; |
| 2611 | \\ ref.* = ref.* * @as(c_int, 1); | 2824 | \\ ref.* *= @as(c_int, 1); |
| 2612 | \\ break :blk ref.*; | 2825 | \\ break :blk ref.*; |
| 2613 | \\ }); | 2826 | \\ }; |
| 2614 | \\ a &= (blk: { | 2827 | \\ a &= blk: { |
| 2615 | \\ const ref = &a; | 2828 | \\ const ref = &a; |
| 2616 | \\ ref.* = ref.* & @as(c_int, 1); | 2829 | \\ ref.* &= @as(c_int, 1); |
| 2617 | \\ break :blk ref.*; | 2830 | \\ break :blk ref.*; |
| 2618 | \\ }); | 2831 | \\ }; |
| 2619 | \\ a |= (blk: { | 2832 | \\ a |= blk: { |
| 2620 | \\ const ref = &a; | 2833 | \\ const ref = &a; |
| 2621 | \\ ref.* = ref.* | @as(c_int, 1); | 2834 | \\ ref.* |= @as(c_int, 1); |
| 2622 | \\ break :blk ref.*; | 2835 | \\ break :blk ref.*; |
| 2623 | \\ }); | 2836 | \\ }; |
| 2624 | \\ a ^= (blk: { | 2837 | \\ a ^= blk: { |
| 2625 | \\ const ref = &a; | 2838 | \\ const ref = &a; |
| 2626 | \\ ref.* = ref.* ^ @as(c_int, 1); | 2839 | \\ ref.* ^= @as(c_int, 1); |
| 2627 | \\ break :blk ref.*; | 2840 | \\ break :blk ref.*; |
| 2628 | \\ }); | 2841 | \\ }; |
| 2629 | \\ a >>= @intCast(@import("std").math.Log2Int(c_int), (blk: { | 2842 | \\ a >>= @intCast(@import("std").math.Log2Int(c_int), blk: { |
| 2630 | \\ const ref = &a; | 2843 | \\ const ref = &a; |
| 2631 | \\ ref.* = ref.* >> @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); | 2844 | \\ ref.* >>= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); |
| 2632 | \\ break :blk ref.*; | 2845 | \\ break :blk ref.*; |
| 2633 | \\ })); | 2846 | \\ }); |
| 2634 | \\ a <<= @intCast(@import("std").math.Log2Int(c_int), (blk: { | 2847 | \\ a <<= @intCast(@import("std").math.Log2Int(c_int), blk: { |
| 2635 | \\ const ref = &a; | 2848 | \\ const ref = &a; |
| 2636 | \\ ref.* = ref.* << @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); | 2849 | \\ ref.* <<= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); |
| 2637 | \\ break :blk ref.*; | 2850 | \\ break :blk ref.*; |
| 2638 | \\ })); | 2851 | \\ }); |
| 2639 | \\ a = @divTrunc(a, (blk: { | 2852 | \\ a = @divTrunc(a, blk: { |
| 2640 | \\ const ref = &a; | 2853 | \\ const ref = &a; |
| 2641 | \\ ref.* = @divTrunc(ref.*, @as(c_int, 1)); | 2854 | \\ ref.* = @divTrunc(ref.*, @as(c_int, 1)); |
| 2642 | \\ break :blk ref.*; | 2855 | \\ break :blk ref.*; |
| 2643 | \\ })); | 2856 | \\ }); |
| 2644 | \\ a = @rem(a, (blk: { | 2857 | \\ a = @rem(a, blk: { |
| 2645 | \\ const ref = &a; | 2858 | \\ const ref = &a; |
| 2646 | \\ ref.* = @rem(ref.*, @as(c_int, 1)); | 2859 | \\ ref.* = @rem(ref.*, @as(c_int, 1)); |
| 2647 | \\ break :blk ref.*; | 2860 | \\ break :blk ref.*; |
| 2648 | \\ })); | 2861 | \\ }); |
| 2649 | \\ b /= (blk: { | 2862 | \\ b /= blk: { |
| 2650 | \\ const ref = &b; | 2863 | \\ const ref = &b; |
| 2651 | \\ ref.* = ref.* / @bitCast(c_uint, @as(c_int, 1)); | 2864 | \\ ref.* /= @bitCast(c_uint, @as(c_int, 1)); |
| 2652 | \\ break :blk ref.*; | 2865 | \\ break :blk ref.*; |
| 2653 | \\ }); | 2866 | \\ }; |
| 2654 | \\ b %= (blk: { | 2867 | \\ b %= blk: { |
| 2655 | \\ const ref = &b; | 2868 | \\ const ref = &b; |
| 2656 | \\ ref.* = ref.* % @bitCast(c_uint, @as(c_int, 1)); | 2869 | \\ ref.* %= @bitCast(c_uint, @as(c_int, 1)); |
| 2657 | \\ break :blk ref.*; | 2870 | \\ break :blk ref.*; |
| 2658 | \\ }); | 2871 | \\ }; |
| 2659 | \\} | 2872 | \\} |
| 2660 | }); | 2873 | }); |
| 2661 | 2874 | ||
| ... | @@ -2673,47 +2886,47 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2673,47 +2886,47 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2673 | \\} | 2886 | \\} |
| 2674 | , &[_][]const u8{ | 2887 | , &[_][]const u8{ |
| 2675 | \\pub export fn foo() void { | 2888 | \\pub export fn foo() void { |
| 2676 | \\ var a: c_uint = @bitCast(c_uint, @as(c_int, 0)); | 2889 | \\ var a: c_uint = 0; |
| 2677 | \\ a +%= (blk: { | 2890 | \\ a +%= blk: { |
| 2678 | \\ const ref = &a; | 2891 | \\ const ref = &a; |
| 2679 | \\ ref.* = ref.* +% @bitCast(c_uint, @as(c_int, 1)); | 2892 | \\ ref.* +%= @bitCast(c_uint, @as(c_int, 1)); |
| 2680 | \\ break :blk ref.*; | 2893 | \\ break :blk ref.*; |
| 2681 | \\ }); | 2894 | \\ }; |
| 2682 | \\ a -%= (blk: { | 2895 | \\ a -%= blk: { |
| 2683 | \\ const ref = &a; | 2896 | \\ const ref = &a; |
| 2684 | \\ ref.* = ref.* -% @bitCast(c_uint, @as(c_int, 1)); | 2897 | \\ ref.* -%= @bitCast(c_uint, @as(c_int, 1)); |
| 2685 | \\ break :blk ref.*; | 2898 | \\ break :blk ref.*; |
| 2686 | \\ }); | 2899 | \\ }; |
| 2687 | \\ a *%= (blk: { | 2900 | \\ a *%= blk: { |
| 2688 | \\ const ref = &a; | 2901 | \\ const ref = &a; |
| 2689 | \\ ref.* = ref.* *% @bitCast(c_uint, @as(c_int, 1)); | 2902 | \\ ref.* *%= @bitCast(c_uint, @as(c_int, 1)); |
| 2690 | \\ break :blk ref.*; | 2903 | \\ break :blk ref.*; |
| 2691 | \\ }); | 2904 | \\ }; |
| 2692 | \\ a &= (blk: { | 2905 | \\ a &= blk: { |
| 2693 | \\ const ref = &a; | 2906 | \\ const ref = &a; |
| 2694 | \\ ref.* = ref.* & @bitCast(c_uint, @as(c_int, 1)); | 2907 | \\ ref.* &= @bitCast(c_uint, @as(c_int, 1)); |
| 2695 | \\ break :blk ref.*; | 2908 | \\ break :blk ref.*; |
| 2696 | \\ }); | 2909 | \\ }; |
| 2697 | \\ a |= (blk: { | 2910 | \\ a |= blk: { |
| 2698 | \\ const ref = &a; | 2911 | \\ const ref = &a; |
| 2699 | \\ ref.* = ref.* | @bitCast(c_uint, @as(c_int, 1)); | 2912 | \\ ref.* |= @bitCast(c_uint, @as(c_int, 1)); |
| 2700 | \\ break :blk ref.*; | 2913 | \\ break :blk ref.*; |
| 2701 | \\ }); | 2914 | \\ }; |
| 2702 | \\ a ^= (blk: { | 2915 | \\ a ^= blk: { |
| 2703 | \\ const ref = &a; | 2916 | \\ const ref = &a; |
| 2704 | \\ ref.* = ref.* ^ @bitCast(c_uint, @as(c_int, 1)); | 2917 | \\ ref.* ^= @bitCast(c_uint, @as(c_int, 1)); |
| 2705 | \\ break :blk ref.*; | 2918 | \\ break :blk ref.*; |
| 2706 | \\ }); | 2919 | \\ }; |
| 2707 | \\ a >>= @intCast(@import("std").math.Log2Int(c_uint), (blk: { | 2920 | \\ a >>= @intCast(@import("std").math.Log2Int(c_uint), blk: { |
| 2708 | \\ const ref = &a; | 2921 | \\ const ref = &a; |
| 2709 | \\ ref.* = ref.* >> @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); | 2922 | \\ ref.* >>= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); |
| 2710 | \\ break :blk ref.*; | 2923 | \\ break :blk ref.*; |
| 2711 | \\ })); | 2924 | \\ }); |
| 2712 | \\ a <<= @intCast(@import("std").math.Log2Int(c_uint), (blk: { | 2925 | \\ a <<= @intCast(@import("std").math.Log2Int(c_uint), blk: { |
| 2713 | \\ const ref = &a; | 2926 | \\ const ref = &a; |
| 2714 | \\ ref.* = ref.* << @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); | 2927 | \\ ref.* <<= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); |
| 2715 | \\ break :blk ref.*; | 2928 | \\ break :blk ref.*; |
| 2716 | \\ })); | 2929 | \\ }); |
| 2717 | \\} | 2930 | \\} |
| 2718 | }); | 2931 | }); |
| 2719 | 2932 | ||
| ... | @@ -2733,35 +2946,35 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2733,35 +2946,35 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2733 | , &[_][]const u8{ | 2946 | , &[_][]const u8{ |
| 2734 | \\pub export fn foo() void { | 2947 | \\pub export fn foo() void { |
| 2735 | \\ var i: c_int = 0; | 2948 | \\ var i: c_int = 0; |
| 2736 | \\ var u: c_uint = @bitCast(c_uint, @as(c_int, 0)); | 2949 | \\ var u: c_uint = 0; |
| 2737 | \\ i += 1; | 2950 | \\ i += 1; |
| 2738 | \\ i -= 1; | 2951 | \\ i -= 1; |
| 2739 | \\ u +%= 1; | 2952 | \\ u +%= 1; |
| 2740 | \\ u -%= 1; | 2953 | \\ u -%= 1; |
| 2741 | \\ i = (blk: { | 2954 | \\ i = blk: { |
| 2742 | \\ const ref = &i; | 2955 | \\ const ref = &i; |
| 2743 | \\ const tmp = ref.*; | 2956 | \\ const tmp = ref.*; |
| 2744 | \\ ref.* += 1; | 2957 | \\ ref.* += 1; |
| 2745 | \\ break :blk tmp; | 2958 | \\ break :blk tmp; |
| 2746 | \\ }); | 2959 | \\ }; |
| 2747 | \\ i = (blk: { | 2960 | \\ i = blk: { |
| 2748 | \\ const ref = &i; | 2961 | \\ const ref = &i; |
| 2749 | \\ const tmp = ref.*; | 2962 | \\ const tmp = ref.*; |
| 2750 | \\ ref.* -= 1; | 2963 | \\ ref.* -= 1; |
| 2751 | \\ break :blk tmp; | 2964 | \\ break :blk tmp; |
| 2752 | \\ }); | 2965 | \\ }; |
| 2753 | \\ u = (blk: { | 2966 | \\ u = blk: { |
| 2754 | \\ const ref = &u; | 2967 | \\ const ref = &u; |
| 2755 | \\ const tmp = ref.*; | 2968 | \\ const tmp = ref.*; |
| 2756 | \\ ref.* +%= 1; | 2969 | \\ ref.* +%= 1; |
| 2757 | \\ break :blk tmp; | 2970 | \\ break :blk tmp; |
| 2758 | \\ }); | 2971 | \\ }; |
| 2759 | \\ u = (blk: { | 2972 | \\ u = blk: { |
| 2760 | \\ const ref = &u; | 2973 | \\ const ref = &u; |
| 2761 | \\ const tmp = ref.*; | 2974 | \\ const tmp = ref.*; |
| 2762 | \\ ref.* -%= 1; | 2975 | \\ ref.* -%= 1; |
| 2763 | \\ break :blk tmp; | 2976 | \\ break :blk tmp; |
| 2764 | \\ }); | 2977 | \\ }; |
| 2765 | \\} | 2978 | \\} |
| 2766 | }); | 2979 | }); |
| 2767 | 2980 | ||
| ... | @@ -2854,15 +3067,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2854,15 +3067,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2854 | \\ Foo1, | 3067 | \\ Foo1, |
| 2855 | \\}; | 3068 | \\}; |
| 2856 | , &[_][]const u8{ | 3069 | , &[_][]const u8{ |
| 2857 | \\pub const FooA = @enumToInt(enum_Foo.A); | ||
| 2858 | \\pub const FooB = @enumToInt(enum_Foo.B); | ||
| 2859 | \\pub const Foo1 = @enumToInt(enum_Foo.@"1"); | ||
| 2860 | \\pub const enum_Foo = extern enum(c_int) { | 3070 | \\pub const enum_Foo = extern enum(c_int) { |
| 2861 | \\ A = 2, | 3071 | \\ A = 2, |
| 2862 | \\ B = 5, | 3072 | \\ B = 5, |
| 2863 | \\ @"1" = 6, | 3073 | \\ @"1" = 6, |
| 2864 | \\ _, | 3074 | \\ _, |
| 2865 | \\}; | 3075 | \\}; |
| 3076 | \\pub const FooA = @enumToInt(enum_Foo.A); | ||
| 3077 | \\pub const FooB = @enumToInt(enum_Foo.B); | ||
| 3078 | \\pub const Foo1 = @enumToInt(enum_Foo.@"1"); | ||
| 2866 | , | 3079 | , |
| 2867 | \\pub const Foo = enum_Foo; | 3080 | \\pub const Foo = enum_Foo; |
| 2868 | }); | 3081 | }); |
| ... | @@ -2872,13 +3085,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2872,13 +3085,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2872 | \\#define BAR (void*) a | 3085 | \\#define BAR (void*) a |
| 2873 | \\#define BAZ (uint32_t)(2) | 3086 | \\#define BAZ (uint32_t)(2) |
| 2874 | , &[_][]const u8{ | 3087 | , &[_][]const u8{ |
| 2875 | \\pub fn FOO(bar: anytype) callconv(.Inline) @TypeOf(baz((@import("std").meta.cast(?*c_void, baz)))) { | 3088 | \\pub fn FOO(bar: anytype) callconv(.Inline) @TypeOf(baz(@import("std").meta.cast(?*c_void, baz))) { |
| 2876 | \\ return baz((@import("std").meta.cast(?*c_void, baz))); | 3089 | \\ return baz(@import("std").meta.cast(?*c_void, baz)); |
| 2877 | \\} | 3090 | \\} |
| 2878 | , | 3091 | , |
| 2879 | \\pub const BAR = (@import("std").meta.cast(?*c_void, a)); | 3092 | \\pub const BAR = @import("std").meta.cast(?*c_void, a); |
| 2880 | , | 3093 | , |
| 2881 | \\pub const BAZ = (@import("std").meta.cast(u32, 2)); | 3094 | \\pub const BAZ = @import("std").meta.cast(u32, 2); |
| 2882 | }); | 3095 | }); |
| 2883 | 3096 | ||
| 2884 | cases.add("macro with cast to unsigned short, long, and long long", | 3097 | cases.add("macro with cast to unsigned short, long, and long long", |
| ... | @@ -2886,9 +3099,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2886,9 +3099,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2886 | \\#define CURLAUTH_BASIC ((unsigned long) 1) | 3099 | \\#define CURLAUTH_BASIC ((unsigned long) 1) |
| 2887 | \\#define CURLAUTH_BASIC_BUT_ULONGLONG ((unsigned long long) 1) | 3100 | \\#define CURLAUTH_BASIC_BUT_ULONGLONG ((unsigned long long) 1) |
| 2888 | , &[_][]const u8{ | 3101 | , &[_][]const u8{ |
| 2889 | \\pub const CURLAUTH_BASIC_BUT_USHORT = (@import("std").meta.cast(c_ushort, 1)); | 3102 | \\pub const CURLAUTH_BASIC_BUT_USHORT = @import("std").meta.cast(c_ushort, 1); |
| 2890 | \\pub const CURLAUTH_BASIC = (@import("std").meta.cast(c_ulong, 1)); | 3103 | \\pub const CURLAUTH_BASIC = @import("std").meta.cast(c_ulong, 1); |
| 2891 | \\pub const CURLAUTH_BASIC_BUT_ULONGLONG = (@import("std").meta.cast(c_ulonglong, 1)); | 3104 | \\pub const CURLAUTH_BASIC_BUT_ULONGLONG = @import("std").meta.cast(c_ulonglong, 1); |
| 2892 | }); | 3105 | }); |
| 2893 | 3106 | ||
| 2894 | cases.add("macro conditional operator", | 3107 | cases.add("macro conditional operator", |
| ... | @@ -2904,9 +3117,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2904,9 +3117,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2904 | \\} | 3117 | \\} |
| 2905 | , &[_][]const u8{ | 3118 | , &[_][]const u8{ |
| 2906 | \\pub fn foo() callconv(.C) void { | 3119 | \\pub fn foo() callconv(.C) void { |
| 2907 | \\ if (true) while (true) { | 3120 | \\ if (true) {} |
| 2908 | \\ if (!false) break; | ||
| 2909 | \\ }; | ||
| 2910 | \\} | 3121 | \\} |
| 2911 | }); | 3122 | }); |
| 2912 | 3123 | ||
| ... | @@ -2923,7 +3134,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2923,7 +3134,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2923 | \\} | 3134 | \\} |
| 2924 | }); | 3135 | }); |
| 2925 | 3136 | ||
| 2926 | // TODO: detect to use different block labels here | ||
| 2927 | cases.add("nested assignment", | 3137 | cases.add("nested assignment", |
| 2928 | \\int foo(int *p, int x) { | 3138 | \\int foo(int *p, int x) { |
| 2929 | \\ return *p++ = x; | 3139 | \\ return *p++ = x; |
| ... | @@ -3033,10 +3243,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -3033,10 +3243,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3033 | , &[_][]const u8{ | 3243 | , &[_][]const u8{ |
| 3034 | \\pub export fn foo(arg_x: bool) bool { | 3244 | \\pub export fn foo(arg_x: bool) bool { |
| 3035 | \\ var x = arg_x; | 3245 | \\ var x = arg_x; |
| 3036 | \\ var a: bool = (@as(c_int, @boolToInt(x)) != @as(c_int, 1)); | 3246 | \\ var a: bool = @as(c_int, @boolToInt(x)) != @as(c_int, 1); |
| 3037 | \\ var b: bool = (@as(c_int, @boolToInt(a)) != @as(c_int, 0)); | 3247 | \\ var b: bool = @as(c_int, @boolToInt(a)) != @as(c_int, 0); |
| 3038 | \\ var c: bool = @ptrToInt(foo) != 0; | 3248 | \\ var c: bool = @ptrToInt(foo) != 0; |
| 3039 | \\ return foo((@as(c_int, @boolToInt(c)) != @as(c_int, @boolToInt(b)))); | 3249 | \\ return foo(@as(c_int, @boolToInt(c)) != @as(c_int, @boolToInt(b))); |
| 3040 | \\} | 3250 | \\} |
| 3041 | }); | 3251 | }); |
| 3042 | 3252 | ||
| ... | @@ -3106,8 +3316,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -3106,8 +3316,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3106 | \\#define DefaultScreen(dpy) (((_XPrivDisplay)(dpy))->default_screen) | 3316 | \\#define DefaultScreen(dpy) (((_XPrivDisplay)(dpy))->default_screen) |
| 3107 | \\ | 3317 | \\ |
| 3108 | , &[_][]const u8{ | 3318 | , &[_][]const u8{ |
| 3109 | \\pub fn DefaultScreen(dpy: anytype) callconv(.Inline) @TypeOf((@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen) { | 3319 | \\pub fn DefaultScreen(dpy: anytype) callconv(.Inline) @TypeOf(@import("std").meta.cast(_XPrivDisplay, dpy).*.default_screen) { |
| 3110 | \\ return (@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen; | 3320 | \\ return @import("std").meta.cast(_XPrivDisplay, dpy).*.default_screen; |
| 3111 | \\} | 3321 | \\} |
| 3112 | }); | 3322 | }); |
| 3113 | 3323 | ||
| ... | @@ -3115,9 +3325,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -3115,9 +3325,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3115 | \\#define NULL ((void*)0) | 3325 | \\#define NULL ((void*)0) |
| 3116 | \\#define FOO ((int)0x8000) | 3326 | \\#define FOO ((int)0x8000) |
| 3117 | , &[_][]const u8{ | 3327 | , &[_][]const u8{ |
| 3118 | \\pub const NULL = (@import("std").meta.cast(?*c_void, 0)); | 3328 | \\pub const NULL = @import("std").meta.cast(?*c_void, 0); |
| 3119 | , | 3329 | , |
| 3120 | \\pub const FOO = (@import("std").meta.cast(c_int, 0x8000)); | 3330 | \\pub const FOO = @import("std").meta.cast(c_int, 0x8000); |
| 3121 | }); | 3331 | }); |
| 3122 | 3332 | ||
| 3123 | if (std.Target.current.abi == .msvc) { | 3333 | if (std.Target.current.abi == .msvc) { |