| ... | @@ -265,13 +265,17 @@ pub const Tokenizer = struct { | ... | @@ -265,13 +265,17 @@ pub const Tokenizer = struct { |
| 265 | var state: enum { | 265 | var state: enum { |
| 266 | Start, | 266 | Start, |
| 267 | Cr, | 267 | Cr, |
| | 268 | BackSlash, |
| | 269 | BackSlashCr, |
| 268 | u, | 270 | u, |
| 269 | u8, | 271 | u8, |
| 270 | U, | 272 | U, |
| 271 | L, | 273 | L, |
| 272 | StringLiteral, | 274 | StringLiteral, |
| | 275 | CharLiteralStart, |
| 273 | CharLiteral, | 276 | CharLiteral, |
| 274 | EscapeSequence, | 277 | EscapeSequence, |
| | 278 | CrEscape, |
| 275 | OctalEscape, | 279 | OctalEscape, |
| 276 | HexEscape, | 280 | HexEscape, |
| 277 | UnicodeEscape, | 281 | UnicodeEscape, |
| ... | @@ -344,7 +348,7 @@ pub const Tokenizer = struct { | ... | @@ -344,7 +348,7 @@ pub const Tokenizer = struct { |
| 344 | }, | 348 | }, |
| 345 | '\'' => { | 349 | '\'' => { |
| 346 | result.id = .{ .CharLiteral = .None }; | 350 | result.id = .{ .CharLiteral = .None }; |
| 347 | state = .CharLiteral; | 351 | state = .CharLiteralStart; |
| 348 | }, | 352 | }, |
| 349 | 'u' => { | 353 | 'u' => { |
| 350 | state = .u; | 354 | state = .u; |
| ... | @@ -464,6 +468,9 @@ pub const Tokenizer = struct { | ... | @@ -464,6 +468,9 @@ pub const Tokenizer = struct { |
| 464 | '1'...'9' => { | 468 | '1'...'9' => { |
| 465 | state = .IntegerLiteral; | 469 | state = .IntegerLiteral; |
| 466 | }, | 470 | }, |
| | 471 | '\\' => { |
| | 472 | state = .BackSlash; |
| | 473 | }, |
| 467 | else => { | 474 | else => { |
| 468 | result.start = self.index + 1; | 475 | result.start = self.index + 1; |
| 469 | }, | 476 | }, |
| ... | @@ -480,13 +487,34 @@ pub const Tokenizer = struct { | ... | @@ -480,13 +487,34 @@ pub const Tokenizer = struct { |
| 480 | break; | 487 | break; |
| 481 | }, | 488 | }, |
| 482 | }, | 489 | }, |
| | 490 | .BackSlash => switch (c) { |
| | 491 | '\n' => { |
| | 492 | state = .Start; |
| | 493 | }, |
| | 494 | '\r' => { |
| | 495 | state = .BackSlashCr; |
| | 496 | }, |
| | 497 | else => { |
| | 498 | result.id = .Invalid; |
| | 499 | break; |
| | 500 | }, |
| | 501 | }, |
| | 502 | .BackSlashCr => switch (c) { |
| | 503 | '\n' => { |
| | 504 | state = .Start; |
| | 505 | }, |
| | 506 | else => { |
| | 507 | result.id = .Invalid; |
| | 508 | break; |
| | 509 | }, |
| | 510 | }, |
| 483 | .u => switch (c) { | 511 | .u => switch (c) { |
| 484 | '8' => { | 512 | '8' => { |
| 485 | state = .u8; | 513 | state = .u8; |
| 486 | }, | 514 | }, |
| 487 | '\'' => { | 515 | '\'' => { |
| 488 | result.id = .{ .CharLiteral = .Utf16 }; | 516 | result.id = .{ .CharLiteral = .Utf16 }; |
| 489 | state = .CharLiteral; | 517 | state = .CharLiteralStart; |
| 490 | }, | 518 | }, |
| 491 | '\"' => { | 519 | '\"' => { |
| 492 | result.id = .{ .StringLiteral = .Utf16 }; | 520 | result.id = .{ .StringLiteral = .Utf16 }; |
| ... | @@ -508,7 +536,7 @@ pub const Tokenizer = struct { | ... | @@ -508,7 +536,7 @@ pub const Tokenizer = struct { |
| 508 | .U => switch (c) { | 536 | .U => switch (c) { |
| 509 | '\'' => { | 537 | '\'' => { |
| 510 | result.id = .{ .CharLiteral = .Utf32 }; | 538 | result.id = .{ .CharLiteral = .Utf32 }; |
| 511 | state = .CharLiteral; | 539 | state = .CharLiteralStart; |
| 512 | }, | 540 | }, |
| 513 | '\"' => { | 541 | '\"' => { |
| 514 | result.id = .{ .StringLiteral = .Utf32 }; | 542 | result.id = .{ .StringLiteral = .Utf32 }; |
| ... | @@ -521,7 +549,7 @@ pub const Tokenizer = struct { | ... | @@ -521,7 +549,7 @@ pub const Tokenizer = struct { |
| 521 | .L => switch (c) { | 549 | .L => switch (c) { |
| 522 | '\'' => { | 550 | '\'' => { |
| 523 | result.id = .{ .CharLiteral = .Wide }; | 551 | result.id = .{ .CharLiteral = .Wide }; |
| 524 | state = .CharLiteral; | 552 | state = .CharLiteralStart; |
| 525 | }, | 553 | }, |
| 526 | '\"' => { | 554 | '\"' => { |
| 527 | result.id = .{ .StringLiteral = .Wide }; | 555 | result.id = .{ .StringLiteral = .Wide }; |
| ... | @@ -546,7 +574,7 @@ pub const Tokenizer = struct { | ... | @@ -546,7 +574,7 @@ pub const Tokenizer = struct { |
| 546 | }, | 574 | }, |
| 547 | else => {}, | 575 | else => {}, |
| 548 | }, | 576 | }, |
| 549 | .CharLiteral => switch (c) { | 577 | .CharLiteralStart => switch (c) { |
| 550 | '\\' => { | 578 | '\\' => { |
| 551 | string = false; | 579 | string = false; |
| 552 | state = .EscapeSequence; | 580 | state = .EscapeSequence; |
| ... | @@ -555,10 +583,32 @@ pub const Tokenizer = struct { | ... | @@ -555,10 +583,32 @@ pub const Tokenizer = struct { |
| 555 | result.id = .Invalid; | 583 | result.id = .Invalid; |
| 556 | break; | 584 | break; |
| 557 | }, | 585 | }, |
| | 586 | else => { |
| | 587 | state = .CharLiteral; |
| | 588 | }, |
| | 589 | }, |
| | 590 | .CharLiteral => switch (c) { |
| | 591 | '\\' => { |
| | 592 | string = false; |
| | 593 | state = .EscapeSequence; |
| | 594 | }, |
| | 595 | '\'' => { |
| | 596 | self.index += 1; |
| | 597 | break; |
| | 598 | }, |
| | 599 | '\n' => { |
| | 600 | result.id = .Invalid; |
| | 601 | break; |
| | 602 | }, |
| 558 | else => {}, | 603 | else => {}, |
| 559 | }, | 604 | }, |
| 560 | .EscapeSequence => switch (c) { | 605 | .EscapeSequence => switch (c) { |
| 561 | '\'', '"', '?', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v' => {}, | 606 | '\'', '"', '?', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v', '\n' => { |
| | 607 | state = if (string) .StringLiteral else .CharLiteral; |
| | 608 | }, |
| | 609 | '\r' => { |
| | 610 | state = .CrEscape; |
| | 611 | }, |
| 562 | '0'...'7' => { | 612 | '0'...'7' => { |
| 563 | counter = 1; | 613 | counter = 1; |
| 564 | state = .OctalEscape; | 614 | state = .OctalEscape; |
| ... | @@ -579,6 +629,15 @@ pub const Tokenizer = struct { | ... | @@ -579,6 +629,15 @@ pub const Tokenizer = struct { |
| 579 | break; | 629 | break; |
| 580 | }, | 630 | }, |
| 581 | }, | 631 | }, |
| | 632 | .CrEscape => switch (c) { |
| | 633 | '\n' => { |
| | 634 | state = if (string) .StringLiteral else .CharLiteral; |
| | 635 | }, |
| | 636 | else => { |
| | 637 | result.id = .Invalid; |
| | 638 | break; |
| | 639 | }, |
| | 640 | }, |
| 582 | .OctalEscape => switch (c) { | 641 | .OctalEscape => switch (c) { |
| 583 | '0'...'7' => { | 642 | '0'...'7' => { |
| 584 | counter += 1; | 643 | counter += 1; |
| ... | @@ -1056,10 +1115,14 @@ pub const Tokenizer = struct { | ... | @@ -1056,10 +1115,14 @@ pub const Tokenizer = struct { |
| 1056 | }, | 1115 | }, |
| 1057 | | 1116 | |
| 1058 | .Cr, | 1117 | .Cr, |
| | 1118 | .BackSlash, |
| | 1119 | .BackSlashCr, |
| 1059 | .Period2, | 1120 | .Period2, |
| 1060 | .StringLiteral, | 1121 | .StringLiteral, |
| | 1122 | .CharLiteralStart, |
| 1061 | .CharLiteral, | 1123 | .CharLiteral, |
| 1062 | .EscapeSequence, | 1124 | .EscapeSequence, |
| | 1125 | .CrEscape, |
| 1063 | .OctalEscape, | 1126 | .OctalEscape, |
| 1064 | .HexEscape, | 1127 | .HexEscape, |
| 1065 | .UnicodeEscape, | 1128 | .UnicodeEscape, |
| ... | @@ -1269,6 +1332,72 @@ test "preprocessor keywords" { | ... | @@ -1269,6 +1332,72 @@ test "preprocessor keywords" { |
| 1269 | }); | 1332 | }); |
| 1270 | } | 1333 | } |
| 1271 | | 1334 | |
| | 1335 | test "line continuation" { |
| | 1336 | expectTokens( |
| | 1337 | \\#define foo \ |
| | 1338 | \\ bar |
| | 1339 | \\"foo\ |
| | 1340 | \\ bar" |
| | 1341 | \\ |
| | 1342 | , &[_]Token.Id{ |
| | 1343 | .Hash, |
| | 1344 | .Keyword_define, |
| | 1345 | .Identifier, |
| | 1346 | .Identifier, |
| | 1347 | .Nl, |
| | 1348 | .{ .StringLiteral = .None }, |
| | 1349 | }); |
| | 1350 | } |
| | 1351 | |
| | 1352 | test "string prefix" { |
| | 1353 | expectTokens( |
| | 1354 | \\"foo" |
| | 1355 | \\u"foo" |
| | 1356 | \\u8"foo" |
| | 1357 | \\U"foo" |
| | 1358 | \\L"foo" |
| | 1359 | \\'foo' |
| | 1360 | \\u'foo' |
| | 1361 | \\U'foo' |
| | 1362 | \\L'foo' |
| | 1363 | \\ |
| | 1364 | , &[_]Token.Id{ |
| | 1365 | .{ .StringLiteral = .None }, |
| | 1366 | .{ .StringLiteral = .Utf16 }, |
| | 1367 | .{ .StringLiteral = .Utf8 }, |
| | 1368 | .{ .StringLiteral = .Utf32 }, |
| | 1369 | .{ .StringLiteral = .Wide }, |
| | 1370 | .{ .CharLiteral = .None }, |
| | 1371 | .{ .CharLiteral = .Utf16 }, |
| | 1372 | .{ .CharLiteral = .Utf32 }, |
| | 1373 | .{ .CharLiteral = .Wide }, |
| | 1374 | }); |
| | 1375 | } |
| | 1376 | |
| | 1377 | test "num suffixes" { |
| | 1378 | expectTokens( |
| | 1379 | \\ 1.0f 1.0L 1.0 .0 1. |
| | 1380 | \\ 0l 0lu 0ll 0llu 0 |
| | 1381 | \\ 1u 1ul 1ull 1 |
| | 1382 | \\ |
| | 1383 | , &[_]Token.Id{ |
| | 1384 | .{ .FloatLiteral = .F }, |
| | 1385 | .{ .FloatLiteral = .L }, |
| | 1386 | .{ .FloatLiteral = .None }, |
| | 1387 | .{ .FloatLiteral = .None }, |
| | 1388 | .{ .FloatLiteral = .None }, |
| | 1389 | .{ .IntegerLiteral = .L }, |
| | 1390 | .{ .IntegerLiteral = .LU }, |
| | 1391 | .{ .IntegerLiteral = .LL }, |
| | 1392 | .{ .IntegerLiteral = .LLU }, |
| | 1393 | .{ .IntegerLiteral = .None }, |
| | 1394 | .{ .IntegerLiteral = .U }, |
| | 1395 | .{ .IntegerLiteral = .LU }, |
| | 1396 | .{ .IntegerLiteral = .LLU }, |
| | 1397 | .{ .IntegerLiteral = .None }, |
| | 1398 | }); |
| | 1399 | } |
| | 1400 | |
| 1272 | fn expectTokens(source: []const u8, expected_tokens: []const Token.Id) void { | 1401 | fn expectTokens(source: []const u8, expected_tokens: []const Token.Id) void { |
| 1273 | var tokenizer = Tokenizer{ | 1402 | var tokenizer = Tokenizer{ |
| 1274 | .source = &Source{ | 1403 | .source = &Source{ |