| ... | ... | @@ -260,6 +260,7 @@ pub const Tokenizer = struct { |
| 260 | 260 | Slash, |
| 261 | 261 | LineCommentStart, |
| 262 | 262 | LineComment, |
| 263 | DocComment, |
| 263 | 264 | Zero, |
| 264 | 265 | IntegerLiteral, |
| 265 | 266 | IntegerLiteralWithRadix, |
| ... | ... | @@ -825,6 +826,7 @@ pub const Tokenizer = struct { |
| 825 | 826 | State.Slash => switch (c) { |
| 826 | 827 | '/' => { |
| 827 | 828 | state = State.LineCommentStart; |
| 829 | result.id = Token.Id.LineComment; |
| 828 | 830 | }, |
| 829 | 831 | '=' => { |
| 830 | 832 | result.id = Token.Id.SlashEqual; |
| ... | ... | @@ -839,15 +841,15 @@ pub const Tokenizer = struct { |
| 839 | 841 | State.LineCommentStart => switch (c) { |
| 840 | 842 | '/' => { |
| 841 | 843 | result.id = Token.Id.DocComment; |
| 842 | | state = State.LineComment; |
| 844 | state = State.DocComment; |
| 843 | 845 | }, |
| 844 | | '\n' => { |
| 845 | | result.id = Token.Id.LineComment; |
| 846 | | break; |
| 846 | '\n' => break, |
| 847 | else => { |
| 848 | state = State.LineComment; |
| 849 | self.checkLiteralCharacter(); |
| 847 | 850 | }, |
| 848 | | else => self.checkLiteralCharacter(), |
| 849 | 851 | }, |
| 850 | | State.LineComment => switch (c) { |
| 852 | State.LineComment, State.DocComment => switch (c) { |
| 851 | 853 | '\n' => break, |
| 852 | 854 | else => self.checkLiteralCharacter(), |
| 853 | 855 | }, |
| ... | ... | @@ -934,7 +936,10 @@ pub const Tokenizer = struct { |
| 934 | 936 | }, |
| 935 | 937 | State.LineCommentStart, |
| 936 | 938 | State.LineComment => { |
| 937 | | result.id = Token.Id.Eof; |
| 939 | result.id = Token.Id.LineComment; |
| 940 | }, |
| 941 | State.DocComment => { |
| 942 | result.id = Token.Id.DocComment; |
| 938 | 943 | }, |
| 939 | 944 | |
| 940 | 945 | State.NumberDot, |
| ... | ... | @@ -1105,41 +1110,77 @@ test "tokenizer - invalid literal/comment characters" { |
| 1105 | 1110 | Token.Id.Invalid, |
| 1106 | 1111 | }); |
| 1107 | 1112 | testTokenize("//\x00", []Token.Id { |
| 1113 | Token.Id.LineComment, |
| 1108 | 1114 | Token.Id.Invalid, |
| 1109 | 1115 | }); |
| 1110 | 1116 | testTokenize("//\x1f", []Token.Id { |
| 1117 | Token.Id.LineComment, |
| 1111 | 1118 | Token.Id.Invalid, |
| 1112 | 1119 | }); |
| 1113 | 1120 | testTokenize("//\x7f", []Token.Id { |
| 1121 | Token.Id.LineComment, |
| 1114 | 1122 | Token.Id.Invalid, |
| 1115 | 1123 | }); |
| 1116 | 1124 | } |
| 1117 | 1125 | |
| 1118 | 1126 | test "tokenizer - utf8" { |
| 1119 | | testTokenize("//\xc2\x80", []Token.Id{}); |
| 1120 | | testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{}); |
| 1127 | testTokenize("//\xc2\x80", []Token.Id{Token.Id.LineComment}); |
| 1128 | testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{Token.Id.LineComment}); |
| 1121 | 1129 | } |
| 1122 | 1130 | |
| 1123 | 1131 | test "tokenizer - invalid utf8" { |
| 1124 | | testTokenize("//\x80", []Token.Id{Token.Id.Invalid}); |
| 1125 | | testTokenize("//\xbf", []Token.Id{Token.Id.Invalid}); |
| 1126 | | testTokenize("//\xf8", []Token.Id{Token.Id.Invalid}); |
| 1127 | | testTokenize("//\xff", []Token.Id{Token.Id.Invalid}); |
| 1128 | | testTokenize("//\xc2\xc0", []Token.Id{Token.Id.Invalid}); |
| 1129 | | testTokenize("//\xe0", []Token.Id{Token.Id.Invalid}); |
| 1130 | | testTokenize("//\xf0", []Token.Id{Token.Id.Invalid}); |
| 1131 | | testTokenize("//\xf0\x90\x80\xc0", []Token.Id{Token.Id.Invalid}); |
| 1132 | testTokenize("//\x80", []Token.Id{ |
| 1133 | Token.Id.LineComment, |
| 1134 | Token.Id.Invalid, |
| 1135 | }); |
| 1136 | testTokenize("//\xbf", []Token.Id{ |
| 1137 | Token.Id.LineComment, |
| 1138 | Token.Id.Invalid, |
| 1139 | }); |
| 1140 | testTokenize("//\xf8", []Token.Id{ |
| 1141 | Token.Id.LineComment, |
| 1142 | Token.Id.Invalid, |
| 1143 | }); |
| 1144 | testTokenize("//\xff", []Token.Id{ |
| 1145 | Token.Id.LineComment, |
| 1146 | Token.Id.Invalid, |
| 1147 | }); |
| 1148 | testTokenize("//\xc2\xc0", []Token.Id{ |
| 1149 | Token.Id.LineComment, |
| 1150 | Token.Id.Invalid, |
| 1151 | }); |
| 1152 | testTokenize("//\xe0", []Token.Id{ |
| 1153 | Token.Id.LineComment, |
| 1154 | Token.Id.Invalid, |
| 1155 | }); |
| 1156 | testTokenize("//\xf0", []Token.Id{ |
| 1157 | Token.Id.LineComment, |
| 1158 | Token.Id.Invalid, |
| 1159 | }); |
| 1160 | testTokenize("//\xf0\x90\x80\xc0", []Token.Id{ |
| 1161 | Token.Id.LineComment, |
| 1162 | Token.Id.Invalid, |
| 1163 | }); |
| 1132 | 1164 | } |
| 1133 | 1165 | |
| 1134 | 1166 | test "tokenizer - illegal unicode codepoints" { |
| 1135 | 1167 | // unicode newline characters.U+0085, U+2028, U+2029 |
| 1136 | | testTokenize("//\xc2\x84", []Token.Id{}); |
| 1137 | | testTokenize("//\xc2\x85", []Token.Id{Token.Id.Invalid}); |
| 1138 | | testTokenize("//\xc2\x86", []Token.Id{}); |
| 1139 | | testTokenize("//\xe2\x80\xa7", []Token.Id{}); |
| 1140 | | testTokenize("//\xe2\x80\xa8", []Token.Id{Token.Id.Invalid}); |
| 1141 | | testTokenize("//\xe2\x80\xa9", []Token.Id{Token.Id.Invalid}); |
| 1142 | | testTokenize("//\xe2\x80\xaa", []Token.Id{}); |
| 1168 | testTokenize("//\xc2\x84", []Token.Id{Token.Id.LineComment}); |
| 1169 | testTokenize("//\xc2\x85", []Token.Id{ |
| 1170 | Token.Id.LineComment, |
| 1171 | Token.Id.Invalid, |
| 1172 | }); |
| 1173 | testTokenize("//\xc2\x86", []Token.Id{Token.Id.LineComment}); |
| 1174 | testTokenize("//\xe2\x80\xa7", []Token.Id{Token.Id.LineComment}); |
| 1175 | testTokenize("//\xe2\x80\xa8", []Token.Id{ |
| 1176 | Token.Id.LineComment, |
| 1177 | Token.Id.Invalid, |
| 1178 | }); |
| 1179 | testTokenize("//\xe2\x80\xa9", []Token.Id{ |
| 1180 | Token.Id.LineComment, |
| 1181 | Token.Id.Invalid, |
| 1182 | }); |
| 1183 | testTokenize("//\xe2\x80\xaa", []Token.Id{Token.Id.LineComment}); |
| 1143 | 1184 | } |
| 1144 | 1185 | |
| 1145 | 1186 | test "tokenizer - string identifier and builtin fns" { |
| ... | ... | @@ -1166,11 +1207,35 @@ test "tokenizer - pipe and then invalid" { |
| 1166 | 1207 | }); |
| 1167 | 1208 | } |
| 1168 | 1209 | |
| 1210 | test "tokenizer - line comment and doc comment" { |
| 1211 | testTokenize("//", []Token.Id{Token.Id.LineComment}); |
| 1212 | testTokenize("// a / b", []Token.Id{Token.Id.LineComment}); |
| 1213 | testTokenize("// /", []Token.Id{Token.Id.LineComment}); |
| 1214 | testTokenize("/// a", []Token.Id{Token.Id.DocComment}); |
| 1215 | testTokenize("///", []Token.Id{Token.Id.DocComment}); |
| 1216 | } |
| 1217 | |
| 1218 | test "tokenizer - line comment followed by identifier" { |
| 1219 | testTokenize( |
| 1220 | \\ Unexpected, |
| 1221 | \\ // another |
| 1222 | \\ Another, |
| 1223 | , []Token.Id{ |
| 1224 | Token.Id.Identifier, |
| 1225 | Token.Id.Comma, |
| 1226 | Token.Id.LineComment, |
| 1227 | Token.Id.Identifier, |
| 1228 | Token.Id.Comma, |
| 1229 | }); |
| 1230 | } |
| 1231 | |
| 1169 | 1232 | fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void { |
| 1170 | 1233 | var tokenizer = Tokenizer.init(source); |
| 1171 | 1234 | for (expected_tokens) |expected_token_id| { |
| 1172 | 1235 | const token = tokenizer.next(); |
| 1173 | | std.debug.assert(@TagType(Token.Id)(token.id) == @TagType(Token.Id)(expected_token_id)); |
| 1236 | if (@TagType(Token.Id)(token.id) != @TagType(Token.Id)(expected_token_id)) { |
| 1237 | std.debug.panic("expected {}, found {}\n", @tagName(@TagType(Token.Id)(expected_token_id)), @tagName(@TagType(Token.Id)(token.id))); |
| 1238 | } |
| 1174 | 1239 | switch (expected_token_id) { |
| 1175 | 1240 | Token.Id.StringLiteral => |expected_kind| { |
| 1176 | 1241 | std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable }); |