yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
3485710e9
master
1// slang-lexer.cpp 2#include "slang-lexer.h" 3 4// This file implements the lexer/scanner, which is responsible for taking a raw stream of 5// input bytes and turning it into semantically useful tokens. 6// 7 8#include "core/slang-char-encode.h" 9#include "core/slang-string-escape-util.h" 10#include "slang-core-diagnostics.h" 11#include "slang-name.h" 12#include "slang-source-loc.h" 13 14namespace Slang 15{ 16Token TokenReader ::getEndOfFileToken () 17{ 18return Token (TokenType ::EndOfFile ,UnownedStringSlice ::fromLiteral ("" ),SourceLoc ()); 19} 20 21const Token * TokenList ::begin ()const 22{ 23SLANG_ASSERT (m_tokens .getCount ()); 24return & m_tokens [0 ]; 25} 26 27const Token * TokenList ::end ()const 28{ 29SLANG_ASSERT (m_tokens .getCount ()); 30SLANG_ASSERT (m_tokens [m_tokens .getCount ()- 1 ].type == TokenType ::EndOfFile ); 31return & m_tokens [m_tokens .getCount ()- 1 ]; 32} 33 34TokenSpan ::TokenSpan () 35 :m_begin (nullptr ),m_end (nullptr ) 36{ 37} 38 39TokenReader ::TokenReader () 40 :m_cursor (nullptr ),m_end (nullptr ) 41{ 42_updateLookaheadToken (); 43} 44 45Token & TokenReader ::peekToken () 46{ 47return m_nextToken ; 48} 49 50TokenType TokenReader ::peekTokenType ()const 51{ 52return m_nextToken .type ; 53} 54 55SourceLoc TokenReader ::peekLoc ()const 56{ 57return m_nextToken .loc ; 58} 59 60Token TokenReader ::advanceToken () 61{ 62Token result = m_nextToken ; 63if (m_cursor != m_end ) 64m_cursor ++ ; 65_updateLookaheadToken (); 66return result ; 67} 68 69void TokenReader ::_updateLookaheadToken () 70{ 71// We assume here that we can read a token from a non-null `m_cursor` 72// *even* in the case where `m_cursor == m_end`, because the invariant 73// for lists of tokens is that they should be terminated with and 74// end-of-file token, so that there is always a token "one past the end." 75// 76m_nextToken = m_cursor ?* m_cursor :getEndOfFileToken (); 77 78// If the token we read came from the end of the sub-sequence we are 79// reading, then we will change the token type to an end-of-file token 80// so that code that reads from the sequence and expects a terminating 81// EOF will find it. 82// 83// TODO: We might eventually want a way to look at the actual token type 84// and not just use EOF in all cases: e.g., when emitting diagnostic 85// messages that include the token that is seen. 86// 87if (m_cursor == m_end ) 88m_nextToken .type = TokenType ::EndOfFile ; 89} 90 91// Lexer 92 93void Lexer ::initialize ( 94SourceView * sourceView , 95DiagnosticSink * sink , 96NamePool * namePool , 97MemoryArena * memoryArena ) 98{ 99m_sourceView = sourceView ; 100m_sink = sink ; 101m_namePool = namePool ; 102m_memoryArena = memoryArena ; 103 104auto content = sourceView -> getContent (); 105 106m_begin = content .begin (); 107m_cursor = content .begin (); 108m_end = content .end (); 109 110// Set the start location 111m_startLoc = sourceView -> getRange ().begin ; 112 113// The first token read from a translation unit should be considered to be at 114// the start of a line, and *also* as coming after whitespace (conceptually 115// both the end-of-file and beginning-of-file pseudo-tokens are whitespace). 116// 117m_tokenFlags = TokenFlag ::AtStartOfLine |TokenFlag ::AfterWhitespace ; 118m_lexerFlags = 0 ; 119} 120 121Lexer ::~Lexer () {} 122 123enum 124{ 125kEOF = -1 126}; 127 128// Get the next input byte, without any handling of 129// escaped newlines, non-ASCII code points, source locations, etc. 130static int _peekRaw (Lexer * lexer ) 131{ 132// If we are at the end of the input, return a designated end-of-file value 133if (lexer -> m_cursor == lexer -> m_end ) 134return kEOF ; 135 136// Otherwise, just look at the next byte 137return * lexer -> m_cursor ; 138} 139 140// Read one input byte without any special handling (similar to `peekRaw`) 141static int _advanceRaw (Lexer * lexer ) 142{ 143// The logic here is basically the same as for `peekRaw()`, 144// escape we advance `cursor` if we aren't at the end. 145 146if (lexer -> m_cursor == lexer -> m_end ) 147return kEOF ; 148 149return * lexer -> m_cursor ++ ; 150} 151 152// When the cursor is already at the first byte of an end-of-line sequence, 153// consume one or two bytes that compose the sequence. 154// 155// Basically, a newline is one of: 156// 157// "\n" 158// "\r" 159// "\r\n" 160// "\n\r" 161// 162// We always look for the longest match possible. 163// 164static void _handleNewLineInner (Lexer * lexer ,int c ) 165{ 166SLANG_ASSERT (c == '\n' || c == '\r' ); 167 168int d = _peekRaw (lexer ); 169if ((c ^d )== ('\n' ^'\r' )) 170 { 171_advanceRaw (lexer ); 172 } 173} 174 175// Look ahead one code point, dealing with complications like 176// escaped newlines. 177static int _peek (Lexer * lexer ,int offset = 0 ) 178{ 179int pos = 0 ; 180int c = kEOF ; 181 182do 183 { 184if (lexer -> m_cursor + pos >=lexer -> m_end ) 185return kEOF ; 186 187c = lexer -> m_cursor [pos ++ ]; 188 189while (c == '\\' ) 190 { 191// We might have a backslash-escaped newline. 192// Look at the next byte (if any) to see. 193// 194// Note(tfoley): We are assuming a null-terminated input here, 195// so that we can safely look at the next byte without issue. 196int d = lexer -> m_cursor [pos ++ ]; 197switch (d ) 198 { 199case '\r' : 200case '\n' : 201 { 202// The newline was escaped, so return the code point after *that* 203int e = lexer -> m_cursor [pos ++ ]; 204if ((d ^e )== ('\r' ^'\n' )) 205c = lexer -> m_cursor [pos ++ ]; 206else 207c = e ; 208continue ; 209 } 210default : 211break ; 212 } 213 214// Only continue this while loop in the case where we consumed 215// some newlines 216break ; 217 } 218if (isUtf8LeadingByte ((Byte )c )) 219 { 220// Consume all unicode characters. 221pos -- ; 222c = getUnicodePointFromUTF8 ( 223 [& ]() 224 { 225if (lexer -> m_cursor + pos >=lexer -> m_end ) 226return (char )0 ; 227return lexer -> m_cursor [pos ++ ]; 228 }); 229 } 230// Default case is to just hand along the byte we read as an ASCII code point. 231 }while (offset -- ); 232 233// If we encounter a \0, return kEOF. 234// if (c == 0) 235// return kEOF; 236return c ; 237} 238 239// Get the next code point from the input, and advance the cursor. 240static int _advance (Lexer * lexer ) 241{ 242// We are going to loop, but only as a way of handling 243// escaped line endings. 244for (;;) 245 { 246// If we are at the end of the input, then the task is easy. 247if (lexer -> m_cursor >=lexer -> m_end ) 248return kEOF ; 249 250// Look at the next raw byte, and decide what to do 251int c = * lexer -> m_cursor ++ ; 252 253if (c == '\\' ) 254 { 255// We might have a backslash-escaped newline. 256// Look at the next byte (if any) to see. 257// 258// Note(tfoley): We are assuming a null-terminated input here, 259// so that we can safely look at the next byte without issue. 260int d = * lexer -> m_cursor ; 261switch (d ) 262 { 263case '\r' : 264case '\n' : 265// handle the end-of-line for our source location tracking 266lexer -> m_cursor ++ ; 267_handleNewLineInner (lexer ,d ); 268 269lexer -> m_tokenFlags |=TokenFlag ::ScrubbingNeeded ; 270 271// Now try again, looking at the character after the 272// escaped newline. 273continue ; 274 275default : 276break ; 277 } 278 } 279 280// Consume all unicode characters. 281bool isInvalidStream = false; 282if (isUtf8LeadingByte ((Byte )c )) 283 { 284lexer -> m_cursor -- ; 285c = getUnicodePointFromUTF8 ( 286 [& ]() 287 { 288if (lexer -> m_cursor >=lexer -> m_end ) 289 { 290isInvalidStream = true; 291return (char )0 ; 292 } 293return * lexer -> m_cursor ++ ; 294 }); 295 } 296 297// If we encounter a \0, return kEOF, and move stream cursor to the end. 298if (c == 0 || isInvalidStream ) 299 { 300lexer -> m_cursor = lexer -> m_end ; 301 } 302 303// Default case is to return the raw byte we saw. 304return c ; 305 } 306} 307 308static const int kMaxLexErrorCount = 100 ; 309 310template < typename P ,typename ...Args > 311static void diagnose ( 312DiagnosticSink * sink , 313const P & loc , 314const DiagnosticInfo & info , 315const Args & ...args ) 316{ 317if (!sink ) 318return ; 319 320// Cap max errors to avoid flooding the sink memory. 321if (sink -> getErrorCount ()> kMaxLexErrorCount ) 322return ; 323sink -> diagnose (loc ,info ,args ...); 324} 325 326static void _handleNewLine (Lexer * lexer ) 327{ 328int c = _advance (lexer ); 329_handleNewLineInner (lexer ,c ); 330} 331 332static void _lexLineComment (Lexer * lexer ) 333{ 334for (;;) 335 { 336switch (_peek (lexer )) 337 { 338case '\n' : 339case '\r' : 340case kEOF : 341return ; 342 343default : 344_advance (lexer ); 345continue ; 346 } 347 } 348} 349 350static void _lexBlockComment (Lexer * lexer ) 351{ 352for (;;) 353 { 354switch (_peek (lexer )) 355 { 356case kEOF : 357// TODO(tfoley) diagnostic! 358return ; 359 360case '\n' : 361case '\r' : 362_handleNewLine (lexer ); 363continue ; 364 365case '*' : 366_advance (lexer ); 367switch (_peek (lexer )) 368 { 369case '/' : 370_advance (lexer ); 371return ; 372 373default : 374continue ; 375 } 376 377default : 378_advance (lexer ); 379continue ; 380 } 381 } 382} 383 384static void _lexHorizontalSpace (Lexer * lexer ) 385{ 386for (;;) 387 { 388switch (_peek (lexer )) 389 { 390case ' ' : 391case '\t' : 392_advance (lexer ); 393continue ; 394 395default : 396return ; 397 } 398 } 399} 400 401static bool isNonAsciiCodePoint (unsigned int codePoint ) 402{ 403return codePoint != 0xFFFFFFFF && codePoint >=0x80 ; 404} 405 406static void _lexIdentifier (Lexer * lexer ) 407{ 408for (;;) 409 { 410int c = _peek (lexer ); 411if (('a' <=c )&& (c <='z' )|| ('A' <=c )&& (c <='Z' )|| ('0' <=c )&& (c <='9' )|| 412 (c == '_' )|| isNonAsciiCodePoint ((unsigned int )c )) 413 { 414_advance (lexer ); 415continue ; 416 } 417return ; 418 } 419} 420 421static SourceLoc _getSourceLoc (const Lexer & lexer ,const char * it ) 422{ 423return lexer .m_startLoc + (it - lexer .m_begin ); 424} 425 426static SourceLoc _getSourceLoc (const Lexer * lexer ) 427{ 428return _getSourceLoc (* lexer ,lexer -> m_cursor ); 429} 430 431static void _lexDigits (Lexer * lexer ,int base ) 432{ 433for (;;) 434 { 435int c = _peek (lexer ); 436 437int digitVal = 0 ; 438switch (c ) 439 { 440case '0' : 441case '1' : 442case '2' : 443case '3' : 444case '4' : 445case '5' : 446case '6' : 447case '7' : 448case '8' : 449case '9' : 450digitVal = c - '0' ; 451break ; 452 453case 'a' : 454case 'b' : 455case 'c' : 456case 'd' : 457case 'e' : 458case 'f' : 459if (base <=10 ) 460return ; 461digitVal = 10 + c - 'a' ; 462break ; 463 464case 'A' : 465case 'B' : 466case 'C' : 467case 'D' : 468case 'E' : 469case 'F' : 470if (base <=10 ) 471return ; 472digitVal = 10 + c - 'A' ; 473break ; 474 475default : 476// Not more digits! 477return ; 478 } 479 480if (digitVal >=base ) 481 { 482if (auto sink = lexer -> getDiagnosticSink ()) 483 { 484char buffer []= {(char )c ,0 }; 485diagnose ( 486sink , 487_getSourceLoc (lexer ), 488LexerDiagnostics ::invalidDigitForBase , 489buffer , 490base ); 491 } 492 } 493 494_advance (lexer ); 495 } 496} 497 498static TokenType _maybeLexNumberSuffix (Lexer * lexer ,TokenType tokenType ) 499{ 500// Be liberal in what we accept here, so that figuring out 501// the semantics of a numeric suffix is left up to the parser 502// and semantic checking logic. 503// 504for (;;) 505 { 506int c = _peek (lexer ); 507 508// Accept any alphanumeric character, plus underscores. 509if (('a' <=c )&& (c <='z' )|| ('A' <=c )&& (c <='Z' )|| ('0' <=c )&& (c <='9' )|| 510 (c == '_' )) 511 { 512_advance (lexer ); 513continue ; 514 } 515 516// Stop at the first character that isn't 517// alphanumeric. 518return tokenType ; 519 } 520} 521 522static bool _isNumberExponent (int c ,int base ) 523{ 524switch (c ) 525 { 526default : 527return false; 528 529case 'e' : 530case 'E' : 531if (base != 10 ) 532return false; 533break ; 534 535case 'p' : 536case 'P' : 537if (base != 16 ) 538return false; 539break ; 540 } 541 542return true; 543} 544 545static bool _maybeLexNumberExponent (Lexer * lexer ,int base ) 546{ 547if (_peek (lexer )== '#' ) 548 { 549// Special case #INF 550const auto inf = toSlice ("#INF" ); 551for (auto c :inf ) 552 { 553if (_peek (lexer )!= c ) 554 { 555return false; 556 } 557_advance (lexer ); 558 } 559 560return true; 561 } 562 563if (!_isNumberExponent (_peek (lexer ),base )) 564return false; 565 566// we saw an exponent marker 567_advance (lexer ); 568 569// Now start to read the exponent 570switch (_peek (lexer )) 571 { 572case '+' : 573case '-' : 574_advance (lexer ); 575break ; 576 } 577 578// TODO(tfoley): it would be an error to not see digits here... 579 580_lexDigits (lexer ,10 ); 581 582return true; 583} 584 585static TokenType _lexNumberAfterDecimalPoint (Lexer * lexer ,int base ) 586{ 587_lexDigits (lexer ,base ); 588_maybeLexNumberExponent (lexer ,base ); 589 590return _maybeLexNumberSuffix (lexer ,TokenType ::FloatingPointLiteral ); 591} 592 593static TokenType _lexNumber (Lexer * lexer ,int base ) 594{ 595// TODO(tfoley): Need to consider whether to allow any kind of digit separator character. 596 597TokenType tokenType = TokenType ::IntegerLiteral ; 598 599// At the start of things, we just concern ourselves with digits 600_lexDigits (lexer ,base ); 601 602if (_peek (lexer )== '.' ) 603 { 604switch (_peek (lexer ,1 )) 605 { 606// 123.xxxx or 123.rrrr 607case 'x' : 608case 'r' : 609break ; 610 611default : 612tokenType = TokenType ::FloatingPointLiteral ; 613 614_advance (lexer ); 615_lexDigits (lexer ,base ); 616 } 617 } 618 619if (_maybeLexNumberExponent (lexer ,base )) 620 { 621tokenType = TokenType ::FloatingPointLiteral ; 622 } 623 624_maybeLexNumberSuffix (lexer ,tokenType ); 625return tokenType ; 626} 627 628static int _maybeReadDigit (char const ** ioCursor ,int base ) 629{ 630auto & cursor = * ioCursor ; 631 632for (;;) 633 { 634int c = * cursor ; 635switch (c ) 636 { 637default : 638return -1 ; 639 640// TODO: need to decide on digit separator characters 641case '_' : 642cursor ++ ; 643continue ; 644 645case '0' : 646case '1' : 647case '2' : 648case '3' : 649case '4' : 650case '5' : 651case '6' : 652case '7' : 653case '8' : 654case '9' : 655cursor ++ ; 656return c - '0' ; 657 658case 'a' : 659case 'b' : 660case 'c' : 661case 'd' : 662case 'e' : 663case 'f' : 664if (base > 10 ) 665 { 666cursor ++ ; 667return 10 + c - 'a' ; 668 } 669return -1 ; 670 671case 'A' : 672case 'B' : 673case 'C' : 674case 'D' : 675case 'E' : 676case 'F' : 677if (base > 10 ) 678 { 679cursor ++ ; 680return 10 + c - 'A' ; 681 } 682return -1 ; 683 } 684 } 685} 686 687static int _readOptionalBase (char const ** ioCursor ) 688{ 689auto & cursor = * ioCursor ; 690if (* cursor == '0' ) 691 { 692cursor ++ ; 693switch (* cursor ) 694 { 695case 'x' : 696case 'X' : 697cursor ++ ; 698return 16 ; 699 700case 'b' : 701case 'B' : 702cursor ++ ; 703return 2 ; 704 705case '0' : 706case '1' : 707case '2' : 708case '3' : 709case '4' : 710case '5' : 711case '6' : 712case '7' : 713case '8' : 714case '9' : 715return 8 ; 716 717default : 718return 10 ; 719 } 720 } 721 722return 10 ; 723} 724 725 726IntegerLiteralValue getIntegerLiteralValue ( 727Token const & token , 728UnownedStringSlice * outSuffix , 729bool * outIsDecimalBase ) 730{ 731IntegerLiteralValue value = 0 ; 732 733const UnownedStringSlice content = token .getContent (); 734 735char const * cursor = content .begin (); 736char const * end = content .end (); 737 738int base = _readOptionalBase (& cursor ); 739 740for (;;) 741 { 742int digit = _maybeReadDigit (& cursor ,base ); 743if (digit < 0 ) 744break ; 745 746value = value * base + digit ; 747 } 748 749if (outSuffix ) 750 { 751* outSuffix = UnownedStringSlice (cursor ,end ); 752 } 753 754if (outIsDecimalBase ) 755 { 756* outIsDecimalBase = (base == 10 ); 757 } 758 759return value ; 760} 761 762FloatingPointLiteralValue getFloatingPointLiteralValue ( 763Token const & token , 764UnownedStringSlice * outSuffix ) 765{ 766FloatingPointLiteralValue value = 0 ; 767 768const UnownedStringSlice content = token .getContent (); 769 770char const * cursor = content .begin (); 771char const * end = content .end (); 772 773int radix = _readOptionalBase (& cursor ); 774 775bool seenDot = false; 776FloatingPointLiteralValue divisor = 1 ; 777for (;;) 778 { 779if (* cursor == '.' ) 780 { 781cursor ++ ; 782seenDot = true; 783continue ; 784 } 785 786int digit = _maybeReadDigit (& cursor ,radix ); 787if (digit < 0 ) 788break ; 789 790value = value * radix + digit ; 791 792if (seenDot ) 793 { 794divisor *=radix ; 795 } 796 } 797 798if (* cursor == '#' ) 799 { 800// It must be INF 801const auto inf = toSlice ("#INF" ); 802 803if (UnownedStringSlice (cursor ,end ).startsWith (inf )) 804 { 805if (outSuffix ) 806 { 807* outSuffix = UnownedStringSlice (cursor + inf .getLength (),end ); 808 } 809 810value = INFINITY ; 811 812return value ; 813 } 814 } 815 816// Now read optional exponent 817if (_isNumberExponent (* cursor ,radix )) 818 { 819cursor ++ ; 820 821bool exponentIsNegative = false; 822switch (* cursor ) 823 { 824default : 825break ; 826 827case '-' : 828exponentIsNegative = true; 829cursor ++ ; 830break ; 831 832case '+' : 833cursor ++ ; 834break ; 835 } 836 837int exponentRadix = 10 ; 838int exponent = 0 ; 839 840for (;;) 841 { 842int digit = _maybeReadDigit (& cursor ,exponentRadix ); 843if (digit < 0 ) 844break ; 845 846exponent = exponent * exponentRadix + digit ; 847 } 848 849FloatingPointLiteralValue exponentBase = 10 ; 850if (radix == 16 ) 851 { 852exponentBase = 2 ; 853 } 854 855FloatingPointLiteralValue exponentValue = pow (exponentBase ,exponent ); 856 857if (exponentIsNegative ) 858 { 859divisor *=exponentValue ; 860 } 861else 862 { 863value *=exponentValue ; 864 } 865 } 866 867value /=divisor ; 868 869if (outSuffix ) 870 { 871* outSuffix = UnownedStringSlice (cursor ,end ); 872 } 873 874return value ; 875} 876 877IntegerLiteralValue getCharLiteralValue (Token const & token ) 878{ 879String unquotedContent = StringEscapeUtil ::unquote ('\'' ,token .getContent ()); 880StringBuilder unescaped (4 ); 881auto escapeHandler = StringEscapeUtil ::getHandler (StringEscapeUtil ::Style ::Cpp ); 882escapeHandler -> appendUnescaped (unquotedContent .getUnownedSlice (),unescaped ); 883 884char const * cursor = unescaped .getBuffer (); 885 886IntegerLiteralValue codepoint = getUnicodePointFromUTF8 ([& ]() {return * cursor ++ ; }); 887return codepoint ; 888} 889 890static void _lexStringLiteralBody (Lexer * lexer ,char quote ,bool singleChar ) 891{ 892int len = 0 ; 893for (;;) 894 { 895int c = _peek (lexer ); 896if (c == quote ) 897 { 898if (singleChar && len == 0 ) 899 {// Empty char literal - size must be exactly 1. 900if (auto sink = lexer -> getDiagnosticSink ()) 901 { 902diagnose (sink ,_getSourceLoc (lexer ),LexerDiagnostics ::illegalCharacterLiteral ); 903 } 904 } 905_advance (lexer ); 906return ; 907 } 908 909len ++ ; 910 911if (singleChar && len == 2 ) 912 {// Char literal about to have more than 1 char. 913if (auto sink = lexer -> getDiagnosticSink ()) 914 { 915diagnose (sink ,_getSourceLoc (lexer ),LexerDiagnostics ::illegalCharacterLiteral ); 916 } 917 } 918 919switch (c ) 920 { 921case kEOF : 922if (auto sink = lexer -> getDiagnosticSink ()) 923 { 924diagnose (sink ,_getSourceLoc (lexer ),LexerDiagnostics ::endOfFileInLiteral ); 925 } 926return ; 927 928case '\n' : 929case '\r' : 930if (auto sink = lexer -> getDiagnosticSink ()) 931 { 932diagnose (sink ,_getSourceLoc (lexer ),LexerDiagnostics ::newlineInLiteral ); 933 } 934return ; 935 936case '\\' : 937// Need to handle various escape sequence cases 938_advance (lexer ); 939switch (_peek (lexer )) 940 { 941case '\'' : 942case '\"' : 943case '\\' : 944case '?' : 945case 'a' : 946case 'b' : 947case 'f' : 948case 'n' : 949case 'r' : 950case 't' : 951case 'v' : 952_advance (lexer ); 953break ; 954 955case '0' : 956case '1' : 957case '2' : 958case '3' : 959case '4' : 960case '5' : 961case '6' : 962case '7' : 963// octal escape: up to 3 characters 964_advance (lexer ); 965for (int ii = 0 ;ii < 3 ;++ ii ) 966 { 967int d = _peek (lexer ); 968if (('0' <=d )&& (d <='7' )) 969 { 970_advance (lexer ); 971continue ; 972 } 973else 974 { 975break ; 976 } 977 } 978break ; 979 980case 'x' : 981// hexadecimal escape: any number of characters 982_advance (lexer ); 983for (;;) 984 { 985int d = _peek (lexer ); 986if (('0' <=d )&& (d <='9' )|| ('a' <=d )&& (d <='f' )|| 987 ('A' <=d )&& (d <='F' )) 988 { 989_advance (lexer ); 990continue ; 991 } 992else 993 { 994break ; 995 } 996 } 997break ; 998 999// TODO: Unicode escape sequences 1000 } 1001break ; 1002 1003default : 1004_advance (lexer ); 1005continue ; 1006 } 1007 } 1008} 1009 1010static void _lexRawStringLiteralBody (Lexer * lexer ) 1011{ 1012const char * start = lexer -> m_cursor ; 1013const char * endOfDelimiter = nullptr ; 1014for (;;) 1015 { 1016int c = _peek (lexer ); 1017if (c == '(' && endOfDelimiter == nullptr ) 1018endOfDelimiter = lexer -> m_cursor ; 1019if (c == '\"' ) 1020 { 1021if (!endOfDelimiter ) 1022 { 1023if (auto sink = lexer -> getDiagnosticSink ()) 1024 { 1025diagnose (sink ,_getSourceLoc (lexer ),LexerDiagnostics ::quoteCannotBeDelimiter ); 1026 } 1027 } 1028else 1029 { 1030auto testStart = lexer -> m_cursor - (endOfDelimiter - start ); 1031if (testStart > endOfDelimiter ) 1032 { 1033auto testDelimiter = UnownedStringSlice (testStart ,lexer -> m_cursor ); 1034auto delimiter = UnownedStringSlice (start ,endOfDelimiter ); 1035if (* (testStart - 1 )== ')' && testDelimiter == delimiter ) 1036 { 1037_advance (lexer ); 1038return ; 1039 } 1040 } 1041 } 1042 } 1043 1044switch (c ) 1045 { 1046case kEOF : 1047if (auto sink = lexer -> getDiagnosticSink ()) 1048 { 1049diagnose (sink ,_getSourceLoc (lexer ),LexerDiagnostics ::endOfFileInLiteral ); 1050 } 1051return ; 1052default : 1053_advance (lexer ); 1054continue ; 1055 } 1056 } 1057} 1058 1059UnownedStringSlice getRawStringLiteralTokenValue (Token const & token ) 1060{ 1061auto content = token .getContent (); 1062if (content .getLength () <=5 ) 1063return UnownedStringSlice (); 1064auto start = content .begin ()+ 2 ; 1065auto delimEnd = start ; 1066while (delimEnd < content .end ()&& * delimEnd != '(' ) 1067delimEnd ++ ; 1068auto delimLength = delimEnd - start ; 1069auto contentEnd = content .end ()- delimLength - 2 ; 1070auto contentBegin = start + delimLength + 1 ; 1071if (contentEnd <=contentBegin ) 1072return UnownedStringSlice (); 1073return UnownedStringSlice (contentBegin ,contentEnd ); 1074} 1075 1076String getStringLiteralTokenValue (Token const & token ) 1077{ 1078SLANG_ASSERT (token .type == TokenType ::StringLiteral || token .type == TokenType ::CharLiteral ); 1079 1080if (token .getContent ().startsWith ("R" )) 1081return getRawStringLiteralTokenValue (token ); 1082 1083const UnownedStringSlice content = token .getContent (); 1084 1085char const * cursor = content .begin (); 1086char const * end = content .end (); 1087SLANG_UNREFERENCED_VARIABLE (end ); 1088 1089auto quote = * cursor ++ ; 1090SLANG_ASSERT (quote == '\'' || quote == '"' ); 1091 1092StringBuilder valueBuilder ; 1093for (;;) 1094 { 1095SLANG_ASSERT (cursor != end ); 1096 1097auto c = * cursor ++ ; 1098 1099// If we see a closing quote, then we are at the end of the string literal 1100if (c == quote ) 1101 { 1102SLANG_ASSERT (cursor == end ); 1103return valueBuilder .produceString (); 1104 } 1105 1106// Characters that don't being escape sequences are easy; 1107// just append them to the buffer and move on. 1108if (c != '\\' ) 1109 { 1110valueBuilder .append (c ); 1111continue ; 1112 } 1113 1114// Now we look at another character to figure out the kind of 1115// escape sequence we are dealing with: 1116 1117char d = * cursor ++ ; 1118 1119switch (d ) 1120 { 1121// Simple characters that just needed to be escaped 1122case '\'' : 1123case '\"' : 1124case '\\' : 1125case '?' : 1126valueBuilder .append (d ); 1127continue ; 1128 1129// Traditional escape sequences for special characters 1130case 'a' : 1131valueBuilder .append ('\a' ); 1132continue ; 1133case 'b' : 1134valueBuilder .append ('\b' ); 1135continue ; 1136case 'f' : 1137valueBuilder .append ('\f' ); 1138continue ; 1139case 'n' : 1140valueBuilder .append ('\n' ); 1141continue ; 1142case 'r' : 1143valueBuilder .append ('\r' ); 1144continue ; 1145case 't' : 1146valueBuilder .append ('\t' ); 1147continue ; 1148case 'v' : 1149valueBuilder .append ('\v' ); 1150continue ; 1151 1152// Octal escape: up to 3 characters 1153case '0' : 1154case '1' : 1155case '2' : 1156case '3' : 1157case '4' : 1158case '5' : 1159case '6' : 1160case '7' : 1161 { 1162cursor -- ; 1163int value = 0 ; 1164for (int ii = 0 ;ii < 3 ;++ ii ) 1165 { 1166d = * cursor ; 1167if (('0' <=d )&& (d <='7' )) 1168 { 1169value = value * 8 + (d - '0' ); 1170 1171cursor ++ ; 1172continue ; 1173 } 1174else 1175 { 1176break ; 1177 } 1178 } 1179 1180// TODO: add support for appending an arbitrary code point? 1181valueBuilder .append ((char )value ); 1182 } 1183continue ; 1184 1185// Hexadecimal escape: any number of characters 1186case 'x' : 1187 { 1188int value = 0 ; 1189for (;;) 1190 { 1191d = * cursor ++ ; 1192int digitValue = 0 ; 1193if (('0' <=d )&& (d <='9' )) 1194 { 1195digitValue = d - '0' ; 1196 } 1197else if (('a' <=d )&& (d <='f' )) 1198 { 1199digitValue = d - 'a' ; 1200 } 1201else if (('A' <=d )&& (d <='F' )) 1202 { 1203digitValue = d - 'A' ; 1204 } 1205else 1206 { 1207cursor -- ; 1208break ; 1209 } 1210 1211value = value * 16 + digitValue ; 1212 } 1213 1214// TODO: add support for appending an arbitrary code point? 1215valueBuilder .append ((char )value ); 1216 } 1217continue ; 1218 1219// TODO: Unicode escape sequences 1220 } 1221 } 1222} 1223 1224String getFileNameTokenValue (Token const & token ) 1225{ 1226const UnownedStringSlice content = token .getContent (); 1227 1228// A file name usually doesn't process escape sequences 1229// (this is import on Windows, where `\\` is a valid 1230// path separator character). 1231 1232// Just trim off the first and last characters to remove the quotes 1233// (whether they were `""` or `<>`. 1234return String (content .begin ()+ 1 ,content .end ()- 1 ); 1235} 1236 1237static TokenType _lexTokenImpl (Lexer * lexer ) 1238{ 1239int nextCodePoint = _peek (lexer ); 1240switch (nextCodePoint ) 1241 { 1242default : 1243break ; 1244 1245case kEOF : 1246return TokenType ::EndOfFile ; 1247 1248case '\r' : 1249case '\n' : 1250_handleNewLine (lexer ); 1251return TokenType ::NewLine ; 1252 1253case ' ' : 1254case '\t' : 1255_lexHorizontalSpace (lexer ); 1256return TokenType ::WhiteSpace ; 1257 1258case '.' : 1259_advance (lexer ); 1260switch (_peek (lexer )) 1261 { 1262case '0' : 1263case '1' : 1264case '2' : 1265case '3' : 1266case '4' : 1267case '5' : 1268case '6' : 1269case '7' : 1270case '8' : 1271case '9' : 1272return _lexNumberAfterDecimalPoint (lexer ,10 ); 1273 1274case '.' : 1275// Note: consuming the second `.` here means that 1276// we cannot back up and return a `.` token by itself 1277// any more. We thus end up having distinct tokens for 1278// `.`, `..`, and `...` even though the `..` case is 1279// not part of HLSL. 1280// 1281_advance (lexer ); 1282switch (_peek (lexer )) 1283 { 1284case '.' : 1285_advance (lexer ); 1286return TokenType ::Ellipsis ; 1287 1288default : 1289return TokenType ::DotDot ; 1290 } 1291 1292default : 1293return TokenType ::Dot ; 1294 } 1295 1296case '1' : 1297case '2' : 1298case '3' : 1299case '4' : 1300case '5' : 1301case '6' : 1302case '7' : 1303case '8' : 1304case '9' : 1305return _lexNumber (lexer ,10 ); 1306 1307case '0' : 1308 { 1309auto loc = _getSourceLoc (lexer ); 1310_advance (lexer ); 1311switch (_peek (lexer )) 1312 { 1313default : 1314return _maybeLexNumberSuffix (lexer ,TokenType ::IntegerLiteral ); 1315 1316case '.' : 1317switch (_peek (lexer ,1 )) 1318 { 1319// 0.xxxx or 0.rrrr 1320case 'x' : 1321case 'r' : 1322return _maybeLexNumberSuffix (lexer ,TokenType ::IntegerLiteral ); 1323default : 1324_advance (lexer ); 1325return _lexNumberAfterDecimalPoint (lexer ,10 ); 1326 } 1327 1328case 'x' : 1329case 'X' : 1330_advance (lexer ); 1331return _lexNumber (lexer ,16 ); 1332 1333case 'b' : 1334case 'B' : 1335_advance (lexer ); 1336return _lexNumber (lexer ,2 ); 1337 1338case '0' : 1339case '1' : 1340case '2' : 1341case '3' : 1342case '4' : 1343case '5' : 1344case '6' : 1345case '7' : 1346case '8' : 1347case '9' : 1348if (auto sink = lexer -> getDiagnosticSink ()) 1349 { 1350diagnose (sink ,loc ,LexerDiagnostics ::octalLiteral ); 1351 } 1352return _lexNumber (lexer ,8 ); 1353 } 1354 } 1355 1356case 'a' : 1357case 'b' : 1358case 'c' : 1359case 'd' : 1360case 'e' : 1361case 'f' : 1362case 'g' : 1363case 'h' : 1364case 'i' : 1365case 'j' : 1366case 'k' : 1367case 'l' : 1368case 'm' : 1369case 'n' : 1370case 'o' : 1371case 'p' : 1372case 'q' : 1373case 'r' : 1374case 's' : 1375case 't' : 1376case 'u' : 1377case 'v' : 1378case 'w' : 1379case 'x' : 1380case 'y' : 1381case 'z' : 1382case 'A' : 1383case 'B' : 1384case 'C' : 1385case 'D' : 1386case 'E' : 1387case 'F' : 1388case 'G' : 1389case 'H' : 1390case 'I' : 1391case 'J' : 1392case 'K' : 1393case 'L' : 1394case 'M' : 1395case 'N' : 1396case 'O' : 1397case 'P' : 1398case 'Q' : 1399case 'S' : 1400case 'T' : 1401case 'U' : 1402case 'V' : 1403case 'W' : 1404case 'X' : 1405case 'Y' : 1406case 'Z' : 1407case '_' : 1408_lexIdentifier (lexer ); 1409return TokenType ::Identifier ; 1410case 'R' : 1411_advance (lexer ); 1412switch (_peek (lexer )) 1413 { 1414default : 1415_lexIdentifier (lexer ); 1416return TokenType ::Identifier ; 1417case '\"' : 1418_advance (lexer ); 1419_lexRawStringLiteralBody (lexer ); 1420return TokenType ::StringLiteral ; 1421 } 1422 1423case '\"' : 1424_advance (lexer ); 1425_lexStringLiteralBody (lexer ,'\"' , false); 1426return TokenType ::StringLiteral ; 1427 1428case '\'' : 1429_advance (lexer ); 1430_lexStringLiteralBody (lexer ,'\'' , true); 1431return TokenType ::CharLiteral ; 1432 1433 1434case '+' : 1435_advance (lexer ); 1436switch (_peek (lexer )) 1437 { 1438case '+' : 1439_advance (lexer ); 1440return TokenType ::OpInc ; 1441case '=' : 1442_advance (lexer ); 1443return TokenType ::OpAddAssign ; 1444default : 1445return TokenType ::OpAdd ; 1446 } 1447 1448case '-' : 1449_advance (lexer ); 1450switch (_peek (lexer )) 1451 { 1452case '-' : 1453_advance (lexer ); 1454return TokenType ::OpDec ; 1455case '=' : 1456_advance (lexer ); 1457return TokenType ::OpSubAssign ; 1458case '>' : 1459_advance (lexer ); 1460return TokenType ::RightArrow ; 1461default : 1462return TokenType ::OpSub ; 1463 } 1464 1465case '*' : 1466_advance (lexer ); 1467switch (_peek (lexer )) 1468 { 1469case '=' : 1470_advance (lexer ); 1471return TokenType ::OpMulAssign ; 1472default : 1473return TokenType ::OpMul ; 1474 } 1475 1476case '/' : 1477_advance (lexer ); 1478switch (_peek (lexer )) 1479 { 1480case '=' : 1481_advance (lexer ); 1482return TokenType ::OpDivAssign ; 1483case '/' : 1484_advance (lexer ); 1485_lexLineComment (lexer ); 1486return TokenType ::LineComment ; 1487case '*' : 1488_advance (lexer ); 1489_lexBlockComment (lexer ); 1490return TokenType ::BlockComment ; 1491default : 1492return TokenType ::OpDiv ; 1493 } 1494 1495case '%' : 1496_advance (lexer ); 1497switch (_peek (lexer )) 1498 { 1499case '=' : 1500_advance (lexer ); 1501return TokenType ::OpModAssign ; 1502default : 1503return TokenType ::OpMod ; 1504 } 1505 1506case '|' : 1507_advance (lexer ); 1508switch (_peek (lexer )) 1509 { 1510case '|' : 1511_advance (lexer ); 1512return TokenType ::OpOr ; 1513case '=' : 1514_advance (lexer ); 1515return TokenType ::OpOrAssign ; 1516default : 1517return TokenType ::OpBitOr ; 1518 } 1519 1520case '&' : 1521_advance (lexer ); 1522switch (_peek (lexer )) 1523 { 1524case '&' : 1525_advance (lexer ); 1526return TokenType ::OpAnd ; 1527case '=' : 1528_advance (lexer ); 1529return TokenType ::OpAndAssign ; 1530default : 1531return TokenType ::OpBitAnd ; 1532 } 1533 1534case '^' : 1535_advance (lexer ); 1536switch (_peek (lexer )) 1537 { 1538case '=' : 1539_advance (lexer ); 1540return TokenType ::OpXorAssign ; 1541default : 1542return TokenType ::OpBitXor ; 1543 } 1544 1545case '>' : 1546_advance (lexer ); 1547switch (_peek (lexer )) 1548 { 1549case '>' : 1550_advance (lexer ); 1551switch (_peek (lexer )) 1552 { 1553case '=' : 1554_advance (lexer ); 1555return TokenType ::OpShrAssign ; 1556default : 1557return TokenType ::OpRsh ; 1558 } 1559case '=' : 1560_advance (lexer ); 1561return TokenType ::OpGeq ; 1562default : 1563return TokenType ::OpGreater ; 1564 } 1565 1566case '<' : 1567_advance (lexer ); 1568switch (_peek (lexer )) 1569 { 1570case '<' : 1571_advance (lexer ); 1572switch (_peek (lexer )) 1573 { 1574case '=' : 1575_advance (lexer ); 1576return TokenType ::OpShlAssign ; 1577default : 1578return TokenType ::OpLsh ; 1579 } 1580case '=' : 1581_advance (lexer ); 1582return TokenType ::OpLeq ; 1583default : 1584return TokenType ::OpLess ; 1585 } 1586 1587case '=' : 1588_advance (lexer ); 1589switch (_peek (lexer )) 1590 { 1591case '=' : 1592_advance (lexer ); 1593return TokenType ::OpEql ; 1594case '>' : 1595_advance (lexer ); 1596return TokenType ::DoubleRightArrow ; 1597default : 1598return TokenType ::OpAssign ; 1599 } 1600 1601case '!' : 1602_advance (lexer ); 1603switch (_peek (lexer )) 1604 { 1605case '=' : 1606_advance (lexer ); 1607return TokenType ::OpNeq ; 1608default : 1609return TokenType ::OpNot ; 1610 } 1611 1612case '#' : 1613_advance (lexer ); 1614switch (_peek (lexer )) 1615 { 1616case '#' : 1617_advance (lexer ); 1618return TokenType ::PoundPound ; 1619 1620case '?' : 1621_advance (lexer ); 1622return TokenType ::CompletionRequest ; 1623 1624default : 1625return TokenType ::Pound ; 1626 } 1627 1628case '~' : 1629_advance (lexer ); 1630return TokenType ::OpBitNot ; 1631 1632case ':' : 1633 { 1634_advance (lexer ); 1635if (_peek (lexer )== ':' ) 1636 { 1637_advance (lexer ); 1638return TokenType ::Scope ; 1639 } 1640return TokenType ::Colon ; 1641 } 1642case ';' : 1643_advance (lexer ); 1644return TokenType ::Semicolon ; 1645case ',' : 1646_advance (lexer ); 1647return TokenType ::Comma ; 1648 1649case '{' : 1650_advance (lexer ); 1651return TokenType ::LBrace ; 1652case '}' : 1653_advance (lexer ); 1654return TokenType ::RBrace ; 1655case '[' : 1656_advance (lexer ); 1657return TokenType ::LBracket ; 1658case ']' : 1659_advance (lexer ); 1660return TokenType ::RBracket ; 1661case '(' : 1662_advance (lexer ); 1663return TokenType ::LParent ; 1664case ')' : 1665_advance (lexer ); 1666return TokenType ::RParent ; 1667 1668case '?' : 1669_advance (lexer ); 1670return TokenType ::QuestionMark ; 1671case '@' : 1672_advance (lexer ); 1673return TokenType ::At ; 1674case '$' : 1675 { 1676_advance (lexer ); 1677if (_peek (lexer )== '$' ) 1678 { 1679_advance (lexer ); 1680return TokenType ::DollarDollar ; 1681 } 1682return TokenType ::Dollar ; 1683 } 1684 } 1685 1686// We treat all unicode characters as a part of an identifier. 1687if (isNonAsciiCodePoint (nextCodePoint )) 1688 { 1689_lexIdentifier (lexer ); 1690return TokenType ::Identifier ; 1691 } 1692 1693 { 1694// If none of the above cases matched, then we have an 1695// unexpected/invalid character. 1696 1697auto loc = _getSourceLoc (lexer ); 1698int c = _advance (lexer ); 1699 1700if (auto sink = lexer -> getDiagnosticSink ()) 1701 { 1702if (c >=0x20 && c <=0x7E ) 1703 { 1704char buffer []= {(char )c ,0 }; 1705diagnose (sink ,loc ,LexerDiagnostics ::illegalCharacterPrint ,buffer ); 1706 } 1707else if (c == kEOF ) 1708 { 1709diagnose (sink ,loc ,LexerDiagnostics ::unexpectedEndOfInput ); 1710 } 1711else 1712 { 1713// Fallback: print as hexadecimal 1714diagnose ( 1715sink , 1716loc , 1717LexerDiagnostics ::illegalCharacterHex , 1718String ((unsigned char )c ,16 )); 1719 } 1720 } 1721 1722return TokenType ::Invalid ; 1723 } 1724} 1725 1726Token Lexer ::lexToken () 1727{ 1728for (;;) 1729 { 1730Token token ; 1731token .loc = _getSourceLoc (this ); 1732 1733char const * textBegin = m_cursor ; 1734 1735auto tokenType = _lexTokenImpl (this ); 1736 1737// The flags on the token we just lexed will be based 1738// on the current state of the lexer. 1739// 1740auto tokenFlags = m_tokenFlags ; 1741// 1742// Depending on what kind of token we just lexed, the 1743// flags that will be used for the *next* token might 1744// need to be updated. 1745// 1746switch (tokenType ) 1747 { 1748case TokenType ::NewLine : 1749 { 1750// If we just reached the end of a line, then the next token 1751// should count as being at the start of a line, and also after 1752// whitespace. 1753// 1754m_tokenFlags = TokenFlag ::AtStartOfLine |TokenFlag ::AfterWhitespace ; 1755break ; 1756 } 1757 1758case TokenType ::WhiteSpace : 1759case TokenType ::BlockComment : 1760case TokenType ::LineComment : 1761 { 1762// True horizontal whitespace and comments both count as whitespace. 1763// 1764// Note that a line comment does not include the terminating newline, 1765// we do not need to set `AtStartOfLine` here. 1766// 1767m_tokenFlags |=TokenFlag ::AfterWhitespace ; 1768break ; 1769 } 1770 1771default : 1772 { 1773// If we read some token other then the above cases, then we are 1774// neither after whitespace nor at the start of a line. 1775// 1776m_tokenFlags = 0 ; 1777break ; 1778 } 1779 } 1780 1781token .type = tokenType ; 1782token .flags = tokenFlags ; 1783 1784char const * textEnd = m_cursor ; 1785 1786// Note(tfoley): `StringBuilder::Append()` seems to crash when appending zero bytes 1787if (textEnd != textBegin ) 1788 { 1789// "scrubbing" token value here to remove escaped newlines... 1790// 1791// Only perform this work if we encountered an escaped newline 1792// while lexing this token (e.g., keep a flag on the lexer), or 1793// do it on-demand when the actual value of the token is needed. 1794if (tokenFlags & TokenFlag ::ScrubbingNeeded ) 1795 { 1796// Allocate space that will always be more than enough for stripped contents 1797char * startDst = (char * )m_memoryArena -> allocateUnaligned (textEnd - textBegin ); 1798char * dst = startDst ; 1799 1800auto tt = textBegin ; 1801while (tt != textEnd ) 1802 { 1803char c = * tt ++ ; 1804if (c == '\\' ) 1805 { 1806char d = * tt ; 1807switch (d ) 1808 { 1809case '\r' : 1810case '\n' : 1811 { 1812tt ++ ; 1813char e = * tt ; 1814if ((d ^e )== ('\r' ^'\n' )) 1815 { 1816tt ++ ; 1817 } 1818 } 1819continue ; 1820 1821default : 1822break ; 1823 } 1824 } 1825* dst ++ = c ; 1826 } 1827token .setContent (UnownedStringSlice (startDst ,dst )); 1828 } 1829else 1830 { 1831token .setContent (UnownedStringSlice (textBegin ,textEnd )); 1832 } 1833 } 1834 1835if (m_namePool ) 1836 { 1837if (tokenType == TokenType ::Identifier || tokenType == TokenType ::CompletionRequest ) 1838 { 1839token .setName (m_namePool -> getName (token .getContent ())); 1840 } 1841 } 1842 1843return token ; 1844 } 1845} 1846 1847TokenList Lexer ::lexAllSemanticTokens () 1848{ 1849TokenList tokenList ; 1850for (;;) 1851 { 1852Token token = lexToken (); 1853 1854// We are only interested intokens that are semantically 1855// significant, so we will skip over forms of whitespace 1856// and comments. 1857// 1858switch (token .type ) 1859 { 1860default : 1861break ; 1862 1863case TokenType ::WhiteSpace : 1864case TokenType ::BlockComment : 1865case TokenType ::LineComment : 1866case TokenType ::NewLine : 1867continue ; 1868 } 1869 1870tokenList .add (token ); 1871if (token .type == TokenType ::EndOfFile ) 1872return tokenList ; 1873 } 1874} 1875 1876TokenList Lexer ::lexAllMarkupTokens () 1877{ 1878TokenList tokenList ; 1879for (;;) 1880 { 1881Token token = lexToken (); 1882switch (token .type ) 1883 { 1884default : 1885break ; 1886 1887case TokenType ::WhiteSpace : 1888case TokenType ::NewLine : 1889continue ; 1890 } 1891 1892tokenList .add (token ); 1893if (token .type == TokenType ::EndOfFile ) 1894return tokenList ; 1895 } 1896} 1897 1898TokenList Lexer ::lexAllTokens () 1899{ 1900TokenList tokenList ; 1901for (;;) 1902 { 1903Token token = lexToken (); 1904tokenList .add (token ); 1905if (token .type == TokenType ::EndOfFile ) 1906return tokenList ; 1907 } 1908} 1909 1910/* static */ UnownedStringSlice Lexer ::sourceLocationLexer (const UnownedStringSlice & in ) 1911{ 1912Lexer lexer ; 1913 1914SourceManager sourceManager ; 1915sourceManager .initialize (nullptr ,nullptr ); 1916 1917auto sourceFile = sourceManager .createSourceFileWithString (PathInfo ::makeUnknown (),in ); 1918auto sourceView = sourceManager .createSourceView (sourceFile ,nullptr ,SourceLoc ::fromRaw (0 )); 1919 1920DiagnosticSink sink (& sourceManager ,nullptr ); 1921 1922MemoryArena arena ; 1923 1924NamePool namePool ; 1925 1926lexer .initialize (sourceView ,& sink ,& namePool ,& arena ); 1927 1928Token tok = lexer .lexToken (); 1929 1930if (tok .type == TokenType ::Invalid ) 1931 { 1932return UnownedStringSlice (); 1933 } 1934 1935const int offset = sourceView -> getRange ().getOffset (tok .loc ); 1936 1937SLANG_ASSERT (offset >=0 && offset <=in .getLength ()); 1938SLANG_ASSERT (Index (offset + tok .charsCount ) <=in .getLength ()); 1939 1940return UnownedStringSlice (in .begin ()+ offset ,in .begin ()+ offset + tok .charsCount ); 1941} 1942 1943SourceLoc Lexer ::findNextLineEnd (SourceLoc from ,UInt & lineCount )const 1944{ 1945const char * it = m_begin + (from .getRaw ()- m_startLoc .getRaw ()); 1946if (it >=m_begin && it < m_end ) 1947 { 1948while (it != m_end ) 1949 { 1950const char c = * it ; 1951if (c == '\n' || c == '\r' ) 1952 { 1953const char next = ((it + 1 )== m_end ) ?char (kEOF ) :* (it + 1 ); 1954if ((next ^c )== ('\n' ^'\r' )) 1955 { 1956++ it ; 1957 } 1958-- lineCount ; 1959if (lineCount == 0 ) 1960 { 1961SourceLoc res = _getSourceLoc (* this ,it ); 1962return res ; 1963 } 1964 } 1965++ it ; 1966 } 1967 } 1968return {}; 1969} 1970 1971}// namespace Slang