| ... | ... | @@ -240,6 +240,9 @@ pub const Tokenizer = struct { |
| 240 | 240 | CharLiteral, |
| 241 | 241 | CharLiteralBackslash, |
| 242 | 242 | CharLiteralHexEscape, |
| 243 | CharLiteralUnicodeEscapeSawU, |
| 244 | CharLiteralUnicodeEscape, |
| 245 | CharLiteralUnicodeInvalid, |
| 243 | 246 | CharLiteralEnd, |
| 244 | 247 | Backslash, |
| 245 | 248 | Equal, |
| ... | ... | @@ -296,7 +299,6 @@ pub const Tokenizer = struct { |
| 296 | 299 | .end = undefined, |
| 297 | 300 | }; |
| 298 | 301 | var seen_escape_digits: usize = undefined; |
| 299 | | var expected_escape_digits: usize = undefined; |
| 300 | 302 | while (self.index < self.buffer.len) : (self.index += 1) { |
| 301 | 303 | const c = self.buffer[self.index]; |
| 302 | 304 | switch (state) { |
| ... | ... | @@ -664,17 +666,9 @@ pub const Tokenizer = struct { |
| 664 | 666 | 'x' => { |
| 665 | 667 | state = State.CharLiteralHexEscape; |
| 666 | 668 | seen_escape_digits = 0; |
| 667 | | expected_escape_digits = 2; |
| 668 | 669 | }, |
| 669 | 670 | 'u' => { |
| 670 | | state = State.CharLiteralHexEscape; |
| 671 | | seen_escape_digits = 0; |
| 672 | | expected_escape_digits = 4; |
| 673 | | }, |
| 674 | | 'U' => { |
| 675 | | state = State.CharLiteralHexEscape; |
| 676 | | seen_escape_digits = 0; |
| 677 | | expected_escape_digits = 6; |
| 671 | state = State.CharLiteralUnicodeEscapeSawU; |
| 678 | 672 | }, |
| 679 | 673 | else => { |
| 680 | 674 | state = State.CharLiteralEnd; |
| ... | ... | @@ -682,9 +676,9 @@ pub const Tokenizer = struct { |
| 682 | 676 | }, |
| 683 | 677 | |
| 684 | 678 | State.CharLiteralHexEscape => switch (c) { |
| 685 | | '0'...'9', 'a'...'z', 'A'...'F' => { |
| 679 | '0'...'9', 'a'...'f', 'A'...'F' => { |
| 686 | 680 | seen_escape_digits += 1; |
| 687 | | if (seen_escape_digits == expected_escape_digits) { |
| 681 | if (seen_escape_digits == 2) { |
| 688 | 682 | state = State.CharLiteralEnd; |
| 689 | 683 | } |
| 690 | 684 | }, |
| ... | ... | @@ -694,6 +688,43 @@ pub const Tokenizer = struct { |
| 694 | 688 | }, |
| 695 | 689 | }, |
| 696 | 690 | |
| 691 | State.CharLiteralUnicodeEscapeSawU => switch (c) { |
| 692 | '{' => { |
| 693 | state = State.CharLiteralUnicodeEscape; |
| 694 | seen_escape_digits = 0; |
| 695 | }, |
| 696 | else => { |
| 697 | result.id = Token.Id.Invalid; |
| 698 | state = State.CharLiteralUnicodeInvalid; |
| 699 | }, |
| 700 | }, |
| 701 | |
| 702 | State.CharLiteralUnicodeEscape => switch (c) { |
| 703 | '0'...'9', 'a'...'f', 'A'...'F' => { |
| 704 | seen_escape_digits += 1; |
| 705 | }, |
| 706 | '}' => { |
| 707 | if (seen_escape_digits == 0) { |
| 708 | result.id = Token.Id.Invalid; |
| 709 | state = State.CharLiteralUnicodeInvalid; |
| 710 | } else { |
| 711 | state = State.CharLiteralEnd; |
| 712 | } |
| 713 | }, |
| 714 | else => { |
| 715 | result.id = Token.Id.Invalid; |
| 716 | state = State.CharLiteralUnicodeInvalid; |
| 717 | }, |
| 718 | }, |
| 719 | |
| 720 | State.CharLiteralUnicodeInvalid => switch (c) { |
| 721 | // Keep consuming characters until an obvious stopping point. |
| 722 | // This consolidates e.g. `u{0ab1Q}` into a single invalid token |
| 723 | // instead of creating the tokens `u{0ab1`, `Q`, `}` |
| 724 | '0'...'9', 'a'...'z', 'A'...'Z', '}' => {}, |
| 725 | else => break, |
| 726 | }, |
| 727 | |
| 697 | 728 | State.CharLiteralEnd => switch (c) { |
| 698 | 729 | '\'' => { |
| 699 | 730 | result.id = Token.Id.CharLiteral; |
| ... | ... | @@ -1055,6 +1086,9 @@ pub const Tokenizer = struct { |
| 1055 | 1086 | State.CharLiteral, |
| 1056 | 1087 | State.CharLiteralBackslash, |
| 1057 | 1088 | State.CharLiteralHexEscape, |
| 1089 | State.CharLiteralUnicodeEscapeSawU, |
| 1090 | State.CharLiteralUnicodeEscape, |
| 1091 | State.CharLiteralUnicodeInvalid, |
| 1058 | 1092 | State.CharLiteralEnd, |
| 1059 | 1093 | State.StringLiteralBackslash, |
| 1060 | 1094 | State.LBracketStar, |
| ... | ... | @@ -1208,7 +1242,60 @@ test "tokenizer - unknown length pointer and then c pointer" { |
| 1208 | 1242 | test "tokenizer - char literal with hex escape" { |
| 1209 | 1243 | testTokenize( |
| 1210 | 1244 | \\'\x1b' |
| 1211 | | , [_]Token.Id{Token.Id.CharLiteral}); |
| 1245 | , [_]Token.Id{.CharLiteral}); |
| 1246 | testTokenize( |
| 1247 | \\'\x1' |
| 1248 | , [_]Token.Id{ .Invalid, .Invalid }); |
| 1249 | } |
| 1250 | |
| 1251 | test "tokenizer - char literal with unicode escapes" { |
| 1252 | // Valid unicode escapes |
| 1253 | testTokenize( |
| 1254 | \\'\u{3}' |
| 1255 | , [_]Token.Id{.CharLiteral}); |
| 1256 | testTokenize( |
| 1257 | \\'\u{01}' |
| 1258 | , [_]Token.Id{.CharLiteral}); |
| 1259 | testTokenize( |
| 1260 | \\'\u{2a}' |
| 1261 | , [_]Token.Id{.CharLiteral}); |
| 1262 | testTokenize( |
| 1263 | \\'\u{3f9}' |
| 1264 | , [_]Token.Id{.CharLiteral}); |
| 1265 | testTokenize( |
| 1266 | \\'\u{6E09aBc1523}' |
| 1267 | , [_]Token.Id{.CharLiteral}); |
| 1268 | testTokenize( |
| 1269 | \\"\u{440}" |
| 1270 | , [_]Token.Id{.StringLiteral}); |
| 1271 | |
| 1272 | // Invalid unicode escapes |
| 1273 | testTokenize( |
| 1274 | \\'\u' |
| 1275 | , [_]Token.Id{.Invalid}); |
| 1276 | testTokenize( |
| 1277 | \\'\u{{' |
| 1278 | , [_]Token.Id{ .Invalid, .Invalid }); |
| 1279 | testTokenize( |
| 1280 | \\'\u{}' |
| 1281 | , [_]Token.Id{ .Invalid, .Invalid }); |
| 1282 | testTokenize( |
| 1283 | \\'\u{s}' |
| 1284 | , [_]Token.Id{ .Invalid, .Invalid }); |
| 1285 | testTokenize( |
| 1286 | \\'\u{2z}' |
| 1287 | , [_]Token.Id{ .Invalid, .Invalid }); |
| 1288 | testTokenize( |
| 1289 | \\'\u{4a' |
| 1290 | , [_]Token.Id{.Invalid}); |
| 1291 | |
| 1292 | // Test old-style unicode literals |
| 1293 | testTokenize( |
| 1294 | \\'\u0333' |
| 1295 | , [_]Token.Id{ .Invalid, .Invalid }); |
| 1296 | testTokenize( |
| 1297 | \\'\U0333' |
| 1298 | , [_]Token.Id{ .Invalid, .IntegerLiteral, .Invalid }); |
| 1212 | 1299 | } |
| 1213 | 1300 | |
| 1214 | 1301 | test "tokenizer - float literal e exponent" { |