| ... | @@ -6,7 +6,7 @@ | ... | @@ -6,7 +6,7 @@ |
| 6 | | 6 | |
| 7 | const std = @import("std"); | 7 | const std = @import("std"); |
| 8 | const ErrorDetails = @import("errors.zig").ErrorDetails; | 8 | const ErrorDetails = @import("errors.zig").ErrorDetails; |
| 9 | const columnsUntilTabStop = @import("literals.zig").columnsUntilTabStop; | 9 | const columnWidth = @import("literals.zig").columnWidth; |
| 10 | const code_pages = @import("code_pages.zig"); | 10 | const code_pages = @import("code_pages.zig"); |
| 11 | const CodePage = code_pages.CodePage; | 11 | const CodePage = code_pages.CodePage; |
| 12 | const SourceMappings = @import("source_mapping.zig").SourceMappings; | 12 | const SourceMappings = @import("source_mapping.zig").SourceMappings; |
| ... | @@ -69,17 +69,14 @@ pub const Token = struct { | ... | @@ -69,17 +69,14 @@ pub const Token = struct { |
| 69 | }; | 69 | }; |
| 70 | } | 70 | } |
| 71 | | 71 | |
| | 72 | /// Returns 0-based column |
| 72 | pub fn calculateColumn(token: Token, source: []const u8, tab_columns: usize, maybe_line_start: ?usize) usize { | 73 | pub fn calculateColumn(token: Token, source: []const u8, tab_columns: usize, maybe_line_start: ?usize) usize { |
| 73 | const line_start = maybe_line_start orelse token.getLineStart(source); | 74 | const line_start = maybe_line_start orelse token.getLineStart(source); |
| 74 | | 75 | |
| 75 | var i: usize = line_start; | 76 | var i: usize = line_start; |
| 76 | var column: usize = 0; | 77 | var column: usize = 0; |
| 77 | while (i < token.start) : (i += 1) { | 78 | while (i < token.start) : (i += 1) { |
| 78 | const c = source[i]; | 79 | column += columnWidth(column, source[i], tab_columns); |
| 79 | switch (c) { | | |
| 80 | '\t' => column += columnsUntilTabStop(column, tab_columns), | | |
| 81 | else => column += 1, | | |
| 82 | } | | |
| 83 | } | 80 | } |
| 84 | return column; | 81 | return column; |
| 85 | } | 82 | } |
| ... | @@ -109,6 +106,7 @@ pub const Token = struct { | ... | @@ -109,6 +106,7 @@ pub const Token = struct { |
| 109 | const line_start = maybe_line_start orelse token.getLineStart(source); | 106 | const line_start = maybe_line_start orelse token.getLineStart(source); |
| 110 | | 107 | |
| 111 | var line_end = line_start + 1; | 108 | var line_end = line_start + 1; |
| | 109 | if (line_end >= source.len or source[line_end] == '\n') return source[line_start..line_start]; |
| 112 | while (line_end < source.len and source[line_end] != '\n') : (line_end += 1) {} | 110 | while (line_end < source.len and source[line_end] != '\n') : (line_end += 1) {} |
| 113 | while (line_end > 0 and source[line_end - 1] == '\r') : (line_end -= 1) {} | 111 | while (line_end > 0 and source[line_end - 1] == '\r') : (line_end -= 1) {} |
| 114 | | 112 | |
| ... | @@ -404,6 +402,9 @@ pub const Lexer = struct { | ... | @@ -404,6 +402,9 @@ pub const Lexer = struct { |
| 404 | // TODO: Understand this more, bring it more in line with how the Win32 limits work. | 402 | // TODO: Understand this more, bring it more in line with how the Win32 limits work. |
| 405 | // Alternatively, do something that makes more sense but may be more permissive. | 403 | // Alternatively, do something that makes more sense but may be more permissive. |
| 406 | var string_literal_length: usize = 0; | 404 | var string_literal_length: usize = 0; |
| | 405 | // Keeping track of the string literal column prevents pathological edge cases when |
| | 406 | // there are tons of tab stop characters within a string literal. |
| | 407 | var string_literal_column: usize = 0; |
| 407 | var string_literal_collapsing_whitespace: bool = false; | 408 | var string_literal_collapsing_whitespace: bool = false; |
| 408 | var still_could_have_exponent: bool = true; | 409 | var still_could_have_exponent: bool = true; |
| 409 | var exponent_index: ?usize = null; | 410 | var exponent_index: ?usize = null; |
| ... | @@ -471,6 +472,14 @@ pub const Lexer = struct { | ... | @@ -471,6 +472,14 @@ pub const Lexer = struct { |
| 471 | self.at_start_of_line = false; | 472 | self.at_start_of_line = false; |
| 472 | string_literal_collapsing_whitespace = false; | 473 | string_literal_collapsing_whitespace = false; |
| 473 | string_literal_length = 0; | 474 | string_literal_length = 0; |
| | 475 | |
| | 476 | var dummy_token = Token{ |
| | 477 | .start = self.index, |
| | 478 | .end = self.index, |
| | 479 | .line_number = self.line_handler.line_number, |
| | 480 | .id = .invalid, |
| | 481 | }; |
| | 482 | string_literal_column = dummy_token.calculateColumn(self.buffer, 8, null); |
| 474 | }, | 483 | }, |
| 475 | '+', '&', '|' => { | 484 | '+', '&', '|' => { |
| 476 | self.index += 1; | 485 | self.index += 1; |
| ... | @@ -618,6 +627,14 @@ pub const Lexer = struct { | ... | @@ -618,6 +627,14 @@ pub const Lexer = struct { |
| 618 | state = .quoted_wide_string; | 627 | state = .quoted_wide_string; |
| 619 | string_literal_collapsing_whitespace = false; | 628 | string_literal_collapsing_whitespace = false; |
| 620 | string_literal_length = 0; | 629 | string_literal_length = 0; |
| | 630 | |
| | 631 | var dummy_token = Token{ |
| | 632 | .start = self.index, |
| | 633 | .end = self.index, |
| | 634 | .line_number = self.line_handler.line_number, |
| | 635 | .id = .invalid, |
| | 636 | }; |
| | 637 | string_literal_column = dummy_token.calculateColumn(self.buffer, 8, null); |
| 621 | }, | 638 | }, |
| 622 | else => { | 639 | else => { |
| 623 | state = .literal; | 640 | state = .literal; |
| ... | @@ -695,18 +712,23 @@ pub const Lexer = struct { | ... | @@ -695,18 +712,23 @@ pub const Lexer = struct { |
| 695 | }, | 712 | }, |
| 696 | .quoted_ascii_string, .quoted_wide_string => switch (c) { | 713 | .quoted_ascii_string, .quoted_wide_string => switch (c) { |
| 697 | '"' => { | 714 | '"' => { |
| | 715 | string_literal_column += 1; |
| 698 | state = if (state == .quoted_ascii_string) .quoted_ascii_string_maybe_end else .quoted_wide_string_maybe_end; | 716 | state = if (state == .quoted_ascii_string) .quoted_ascii_string_maybe_end else .quoted_wide_string_maybe_end; |
| 699 | }, | 717 | }, |
| 700 | '\\' => { | 718 | '\\' => { |
| | 719 | string_literal_length += 1; |
| | 720 | string_literal_column += 1; |
| 701 | state = if (state == .quoted_ascii_string) .quoted_ascii_string_escape else .quoted_wide_string_escape; | 721 | state = if (state == .quoted_ascii_string) .quoted_ascii_string_escape else .quoted_wide_string_escape; |
| 702 | }, | 722 | }, |
| 703 | '\r' => { | 723 | '\r' => { |
| | 724 | string_literal_column = 0; |
| 704 | // \r doesn't count towards string literal length | 725 | // \r doesn't count towards string literal length |
| 705 | | 726 | |
| 706 | // Increment line number but don't affect the result token's line number | 727 | // Increment line number but don't affect the result token's line number |
| 707 | _ = self.incrementLineNumber(); | 728 | _ = self.incrementLineNumber(); |
| 708 | }, | 729 | }, |
| 709 | '\n' => { | 730 | '\n' => { |
| | 731 | string_literal_column = 0; |
| 710 | // first \n expands to <space><\n> | 732 | // first \n expands to <space><\n> |
| 711 | if (!string_literal_collapsing_whitespace) { | 733 | if (!string_literal_collapsing_whitespace) { |
| 712 | string_literal_length += 2; | 734 | string_literal_length += 2; |
| ... | @@ -720,33 +742,17 @@ pub const Lexer = struct { | ... | @@ -720,33 +742,17 @@ pub const Lexer = struct { |
| 720 | // only \t, space, Vertical Tab, and Form Feed count as whitespace when collapsing | 742 | // only \t, space, Vertical Tab, and Form Feed count as whitespace when collapsing |
| 721 | '\t', ' ', '\x0b', '\x0c' => { | 743 | '\t', ' ', '\x0b', '\x0c' => { |
| 722 | if (!string_literal_collapsing_whitespace) { | 744 | if (!string_literal_collapsing_whitespace) { |
| 723 | if (c == '\t') { | 745 | // Literal tab characters are counted as the number of space characters |
| 724 | // Literal tab characters are counted as the number of space characters | 746 | // needed to reach the next 8-column tab stop. |
| 725 | // needed to reach the next 8-column tab stop. | 747 | const width = columnWidth(string_literal_column, @intCast(c), 8); |
| 726 | // | 748 | string_literal_length += width; |
| 727 | // This implemention is ineffecient but hopefully it's enough of an | 749 | string_literal_column += width; |
| 728 | // edge case that it doesn't matter too much. Literal tab characters in | | |
| 729 | // string literals being replaced by a variable number of spaces depending | | |
| 730 | // on which column the tab character is located in the source .rc file seems | | |
| 731 | // like it has extremely limited use-cases, so it seems unlikely that it's used | | |
| 732 | // in real .rc files. | | |
| 733 | var dummy_token = Token{ | | |
| 734 | .start = self.index, | | |
| 735 | .end = self.index, | | |
| 736 | .line_number = self.line_handler.line_number, | | |
| 737 | .id = .invalid, | | |
| 738 | }; | | |
| 739 | dummy_token.start = self.index; | | |
| 740 | const current_column = dummy_token.calculateColumn(self.buffer, 8, null); | | |
| 741 | string_literal_length += columnsUntilTabStop(current_column, 8); | | |
| 742 | } else { | | |
| 743 | string_literal_length += 1; | | |
| 744 | } | | |
| 745 | } | 750 | } |
| 746 | }, | 751 | }, |
| 747 | else => { | 752 | else => { |
| 748 | string_literal_collapsing_whitespace = false; | 753 | string_literal_collapsing_whitespace = false; |
| 749 | string_literal_length += 1; | 754 | string_literal_length += 1; |
| | 755 | string_literal_column += 1; |
| 750 | }, | 756 | }, |
| 751 | }, | 757 | }, |
| 752 | .quoted_ascii_string_escape, .quoted_wide_string_escape => switch (c) { | 758 | .quoted_ascii_string_escape, .quoted_wide_string_escape => switch (c) { |
| ... | @@ -760,14 +766,19 @@ pub const Lexer = struct { | ... | @@ -760,14 +766,19 @@ pub const Lexer = struct { |
| 760 | return error.FoundCStyleEscapedQuote; | 766 | return error.FoundCStyleEscapedQuote; |
| 761 | }, | 767 | }, |
| 762 | else => { | 768 | else => { |
| | 769 | string_literal_length += 1; |
| | 770 | string_literal_column += 1; |
| 763 | state = if (state == .quoted_ascii_string_escape) .quoted_ascii_string else .quoted_wide_string; | 771 | state = if (state == .quoted_ascii_string_escape) .quoted_ascii_string else .quoted_wide_string; |
| 764 | }, | 772 | }, |
| 765 | }, | 773 | }, |
| 766 | .quoted_ascii_string_maybe_end, .quoted_wide_string_maybe_end => switch (c) { | 774 | .quoted_ascii_string_maybe_end, .quoted_wide_string_maybe_end => switch (c) { |
| 767 | '"' => { | 775 | '"' => { |
| 768 | state = if (state == .quoted_ascii_string_maybe_end) .quoted_ascii_string else .quoted_wide_string; | 776 | state = if (state == .quoted_ascii_string_maybe_end) .quoted_ascii_string else .quoted_wide_string; |
| 769 | // Escaped quotes only count as 1 char for string literal length checks, | 777 | // Escaped quotes count as 1 char for string literal length checks. |
| 770 | // so we don't increment string_literal_length here. | 778 | // Since we did not increment on the first " (because it could have been |
| | 779 | // the end of the quoted string), we increment here |
| | 780 | string_literal_length += 1; |
| | 781 | string_literal_column += 1; |
| 771 | }, | 782 | }, |
| 772 | else => { | 783 | else => { |
| 773 | result.id = if (state == .quoted_ascii_string_maybe_end) .quoted_ascii_string else .quoted_wide_string; | 784 | result.id = if (state == .quoted_ascii_string_maybe_end) .quoted_ascii_string else .quoted_wide_string; |
| ... | @@ -807,6 +818,8 @@ pub const Lexer = struct { | ... | @@ -807,6 +818,8 @@ pub const Lexer = struct { |
| 807 | } | 818 | } |
| 808 | } | 819 | } |
| 809 | | 820 | |
| | 821 | result.end = self.index; |
| | 822 | |
| 810 | if (result.id == .quoted_ascii_string or result.id == .quoted_wide_string) { | 823 | if (result.id == .quoted_ascii_string or result.id == .quoted_wide_string) { |
| 811 | if (string_literal_length > self.max_string_literal_codepoints) { | 824 | if (string_literal_length > self.max_string_literal_codepoints) { |
| 812 | self.error_context_token = result; | 825 | self.error_context_token = result; |
| ... | @@ -814,7 +827,6 @@ pub const Lexer = struct { | ... | @@ -814,7 +827,6 @@ pub const Lexer = struct { |
| 814 | } | 827 | } |
| 815 | } | 828 | } |
| 816 | | 829 | |
| 817 | result.end = self.index; | | |
| 818 | return result; | 830 | return result; |
| 819 | } | 831 | } |
| 820 | | 832 | |
| ... | @@ -877,6 +889,7 @@ pub const Lexer = struct { | ... | @@ -877,6 +889,7 @@ pub const Lexer = struct { |
| 877 | .end = end, | 889 | .end = end, |
| 878 | .line_number = self.line_handler.line_number, | 890 | .line_number = self.line_handler.line_number, |
| 879 | }; | 891 | }; |
| | 892 | errdefer self.error_context_token = token; |
| 880 | const full_command = self.buffer[start..end]; | 893 | const full_command = self.buffer[start..end]; |
| 881 | var command = full_command; | 894 | var command = full_command; |
| 882 | | 895 | |
| ... | @@ -901,7 +914,6 @@ pub const Lexer = struct { | ... | @@ -901,7 +914,6 @@ pub const Lexer = struct { |
| 901 | } | 914 | } |
| 902 | | 915 | |
| 903 | if (command.len == 0 or command[0] != '(') { | 916 | if (command.len == 0 or command[0] != '(') { |
| 904 | self.error_context_token = token; | | |
| 905 | return error.CodePagePragmaMissingLeftParen; | 917 | return error.CodePagePragmaMissingLeftParen; |
| 906 | } | 918 | } |
| 907 | command = command[1..]; | 919 | command = command[1..]; |
| ... | @@ -917,7 +929,6 @@ pub const Lexer = struct { | ... | @@ -917,7 +929,6 @@ pub const Lexer = struct { |
| 917 | } | 929 | } |
| 918 | | 930 | |
| 919 | if (num_str.len == 0) { | 931 | if (num_str.len == 0) { |
| 920 | self.error_context_token = token; | | |
| 921 | return error.CodePagePragmaNotInteger; | 932 | return error.CodePagePragmaNotInteger; |
| 922 | } | 933 | } |
| 923 | | 934 | |
| ... | @@ -926,7 +937,6 @@ pub const Lexer = struct { | ... | @@ -926,7 +937,6 @@ pub const Lexer = struct { |
| 926 | } | 937 | } |
| 927 | | 938 | |
| 928 | if (command.len == 0 or command[0] != ')') { | 939 | if (command.len == 0 or command[0] != ')') { |
| 929 | self.error_context_token = token; | | |
| 930 | return error.CodePagePragmaMissingRightParen; | 940 | return error.CodePagePragmaMissingRightParen; |
| 931 | } | 941 | } |
| 932 | | 942 | |
| ... | @@ -943,41 +953,26 @@ pub const Lexer = struct { | ... | @@ -943,41 +953,26 @@ pub const Lexer = struct { |
| 943 | // | 953 | // |
| 944 | // Instead of that, we just have a separate error specifically for overflow. | 954 | // Instead of that, we just have a separate error specifically for overflow. |
| 945 | const num = parseCodePageNum(num_str) catch |err| switch (err) { | 955 | const num = parseCodePageNum(num_str) catch |err| switch (err) { |
| 946 | error.InvalidCharacter => { | 956 | error.InvalidCharacter => return error.CodePagePragmaNotInteger, |
| 947 | self.error_context_token = token; | 957 | error.Overflow => return error.CodePagePragmaOverflow, |
| 948 | return error.CodePagePragmaNotInteger; | | |
| 949 | }, | | |
| 950 | error.Overflow => { | | |
| 951 | self.error_context_token = token; | | |
| 952 | return error.CodePagePragmaOverflow; | | |
| 953 | }, | | |
| 954 | }; | 958 | }; |
| 955 | | 959 | |
| 956 | // Anything that starts with 0 but does not resolve to 0 is treated as invalid, e.g. 01252 | 960 | // Anything that starts with 0 but does not resolve to 0 is treated as invalid, e.g. 01252 |
| 957 | if (num_str[0] == '0' and num != 0) { | 961 | if (num_str[0] == '0' and num != 0) { |
| 958 | self.error_context_token = token; | | |
| 959 | return error.CodePagePragmaInvalidCodePage; | 962 | return error.CodePagePragmaInvalidCodePage; |
| 960 | } | 963 | } |
| 961 | // Anything that resolves to 0 is treated as 'not an integer' by the Win32 implementation. | 964 | // Anything that resolves to 0 is treated as 'not an integer' by the Win32 implementation. |
| 962 | else if (num == 0) { | 965 | else if (num == 0) { |
| 963 | self.error_context_token = token; | | |
| 964 | return error.CodePagePragmaNotInteger; | 966 | return error.CodePagePragmaNotInteger; |
| 965 | } | 967 | } |
| 966 | // Anything above u16 max is not going to be found since our CodePage enum is backed by a u16. | 968 | // Anything above u16 max is not going to be found since our CodePage enum is backed by a u16. |
| 967 | if (num > std.math.maxInt(u16)) { | 969 | if (num > std.math.maxInt(u16)) { |
| 968 | self.error_context_token = token; | | |
| 969 | return error.CodePagePragmaInvalidCodePage; | 970 | return error.CodePagePragmaInvalidCodePage; |
| 970 | } | 971 | } |
| 971 | | 972 | |
| 972 | break :code_page code_pages.CodePage.getByIdentifierEnsureSupported(@intCast(num)) catch |err| switch (err) { | 973 | break :code_page code_pages.CodePage.getByIdentifierEnsureSupported(@intCast(num)) catch |err| switch (err) { |
| 973 | error.InvalidCodePage => { | 974 | error.InvalidCodePage => return error.CodePagePragmaInvalidCodePage, |
| 974 | self.error_context_token = token; | 975 | error.UnsupportedCodePage => return error.CodePagePragmaUnsupportedCodePage, |
| 975 | return error.CodePagePragmaInvalidCodePage; | | |
| 976 | }, | | |
| 977 | error.UnsupportedCodePage => { | | |
| 978 | self.error_context_token = token; | | |
| 979 | return error.CodePagePragmaUnsupportedCodePage; | | |
| 980 | }, | | |
| 981 | }; | 976 | }; |
| 982 | }; | 977 | }; |
| 983 | | 978 | |
| ... | @@ -990,7 +985,6 @@ pub const Lexer = struct { | ... | @@ -990,7 +985,6 @@ pub const Lexer = struct { |
| 990 | // to still be able to work correctly after this error is returned. | 985 | // to still be able to work correctly after this error is returned. |
| 991 | if (self.source_mappings) |source_mappings| { | 986 | if (self.source_mappings) |source_mappings| { |
| 992 | if (!source_mappings.isRootFile(token.line_number)) { | 987 | if (!source_mappings.isRootFile(token.line_number)) { |
| 993 | self.error_context_token = token; | | |
| 994 | return error.CodePagePragmaInIncludedFile; | 988 | return error.CodePagePragmaInIncludedFile; |
| 995 | } | 989 | } |
| 996 | } | 990 | } |