xref: /src/contrib/llvm-project/llvm/lib/Support/LineIterator.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
15ca98fd9SDimitry Andric //===- LineIterator.cpp - Implementation of line iteration ----------------===//
25ca98fd9SDimitry Andric //
3e6d15924SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e6d15924SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e6d15924SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ca98fd9SDimitry Andric //
75ca98fd9SDimitry Andric //===----------------------------------------------------------------------===//
85ca98fd9SDimitry Andric 
95ca98fd9SDimitry Andric #include "llvm/Support/LineIterator.h"
105ca98fd9SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
115ca98fd9SDimitry Andric 
125ca98fd9SDimitry Andric using namespace llvm;
135ca98fd9SDimitry Andric 
isAtLineEnd(const char * P)1467c32a98SDimitry Andric static bool isAtLineEnd(const char *P) {
1567c32a98SDimitry Andric   if (*P == '\n')
1667c32a98SDimitry Andric     return true;
1767c32a98SDimitry Andric   if (*P == '\r' && *(P + 1) == '\n')
1867c32a98SDimitry Andric     return true;
1967c32a98SDimitry Andric   return false;
2067c32a98SDimitry Andric }
2167c32a98SDimitry Andric 
skipIfAtLineEnd(const char * & P)2267c32a98SDimitry Andric static bool skipIfAtLineEnd(const char *&P) {
2367c32a98SDimitry Andric   if (*P == '\n') {
2467c32a98SDimitry Andric     ++P;
2567c32a98SDimitry Andric     return true;
2667c32a98SDimitry Andric   }
2767c32a98SDimitry Andric   if (*P == '\r' && *(P + 1) == '\n') {
2867c32a98SDimitry Andric     P += 2;
2967c32a98SDimitry Andric     return true;
3067c32a98SDimitry Andric   }
3167c32a98SDimitry Andric   return false;
3267c32a98SDimitry Andric }
3367c32a98SDimitry Andric 
line_iterator(const MemoryBuffer & Buffer,bool SkipBlanks,char CommentMarker)3467c32a98SDimitry Andric line_iterator::line_iterator(const MemoryBuffer &Buffer, bool SkipBlanks,
3567c32a98SDimitry Andric                              char CommentMarker)
36b60736ecSDimitry Andric     : line_iterator(Buffer.getMemBufferRef(), SkipBlanks, CommentMarker) {}
37b60736ecSDimitry Andric 
line_iterator(const MemoryBufferRef & Buffer,bool SkipBlanks,char CommentMarker)38b60736ecSDimitry Andric line_iterator::line_iterator(const MemoryBufferRef &Buffer, bool SkipBlanks,
39b60736ecSDimitry Andric                              char CommentMarker)
40e3b55780SDimitry Andric     : Buffer(Buffer.getBufferSize() ? std::optional<MemoryBufferRef>(Buffer)
41e3b55780SDimitry Andric                                     : std::nullopt),
42145449b1SDimitry Andric       CommentMarker(CommentMarker), SkipBlanks(SkipBlanks),
435ca98fd9SDimitry Andric       CurrentLine(Buffer.getBufferSize() ? Buffer.getBufferStart() : nullptr,
445ca98fd9SDimitry Andric                   0) {
455ca98fd9SDimitry Andric   // Ensure that if we are constructed on a non-empty memory buffer that it is
465ca98fd9SDimitry Andric   // a null terminated buffer.
475ca98fd9SDimitry Andric   if (Buffer.getBufferSize()) {
485ca98fd9SDimitry Andric     assert(Buffer.getBufferEnd()[0] == '\0');
4967c32a98SDimitry Andric     // Make sure we don't skip a leading newline if we're keeping blanks
5067c32a98SDimitry Andric     if (SkipBlanks || !isAtLineEnd(Buffer.getBufferStart()))
515ca98fd9SDimitry Andric       advance();
525ca98fd9SDimitry Andric   }
535ca98fd9SDimitry Andric }
545ca98fd9SDimitry Andric 
advance()555ca98fd9SDimitry Andric void line_iterator::advance() {
565ca98fd9SDimitry Andric   assert(Buffer && "Cannot advance past the end!");
575ca98fd9SDimitry Andric 
585ca98fd9SDimitry Andric   const char *Pos = CurrentLine.end();
5967c32a98SDimitry Andric   assert(Pos == Buffer->getBufferStart() || isAtLineEnd(Pos) || *Pos == '\0');
605ca98fd9SDimitry Andric 
6167c32a98SDimitry Andric   if (skipIfAtLineEnd(Pos))
6267c32a98SDimitry Andric     ++LineNumber;
6367c32a98SDimitry Andric   if (!SkipBlanks && isAtLineEnd(Pos)) {
6467c32a98SDimitry Andric     // Nothing to do for a blank line.
6567c32a98SDimitry Andric   } else if (CommentMarker == '\0') {
665ca98fd9SDimitry Andric     // If we're not stripping comments, this is simpler.
6767c32a98SDimitry Andric     while (skipIfAtLineEnd(Pos))
6867c32a98SDimitry Andric       ++LineNumber;
695ca98fd9SDimitry Andric   } else {
705ca98fd9SDimitry Andric     // Skip comments and count line numbers, which is a bit more complex.
715ca98fd9SDimitry Andric     for (;;) {
7267c32a98SDimitry Andric       if (isAtLineEnd(Pos) && !SkipBlanks)
7367c32a98SDimitry Andric         break;
745ca98fd9SDimitry Andric       if (*Pos == CommentMarker)
755ca98fd9SDimitry Andric         do {
765ca98fd9SDimitry Andric           ++Pos;
7767c32a98SDimitry Andric         } while (*Pos != '\0' && !isAtLineEnd(Pos));
7867c32a98SDimitry Andric       if (!skipIfAtLineEnd(Pos))
795ca98fd9SDimitry Andric         break;
805ca98fd9SDimitry Andric       ++LineNumber;
815ca98fd9SDimitry Andric     }
825ca98fd9SDimitry Andric   }
835ca98fd9SDimitry Andric 
845ca98fd9SDimitry Andric   if (*Pos == '\0') {
855ca98fd9SDimitry Andric     // We've hit the end of the buffer, reset ourselves to the end state.
86e3b55780SDimitry Andric     Buffer = std::nullopt;
875ca98fd9SDimitry Andric     CurrentLine = StringRef();
885ca98fd9SDimitry Andric     return;
895ca98fd9SDimitry Andric   }
905ca98fd9SDimitry Andric 
915ca98fd9SDimitry Andric   // Measure the line.
925ca98fd9SDimitry Andric   size_t Length = 0;
9367c32a98SDimitry Andric   while (Pos[Length] != '\0' && !isAtLineEnd(&Pos[Length])) {
945ca98fd9SDimitry Andric     ++Length;
9567c32a98SDimitry Andric   }
965ca98fd9SDimitry Andric 
975ca98fd9SDimitry Andric   CurrentLine = StringRef(Pos, Length);
985ca98fd9SDimitry Andric }
99