yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

CopilotMerge NamePool and RootNamePool into a single type (#7797)3485710e9

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