xref: /freebsd/contrib/llvm-project/llvm/lib/Support/LineIterator.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
10b57cec5SDimitry Andric //===- LineIterator.cpp - Implementation of line iteration ----------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "llvm/Support/LineIterator.h"
100b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
110b57cec5SDimitry Andric 
120b57cec5SDimitry Andric using namespace llvm;
130b57cec5SDimitry Andric 
isAtLineEnd(const char * P)140b57cec5SDimitry Andric static bool isAtLineEnd(const char *P) {
150b57cec5SDimitry Andric   if (*P == '\n')
160b57cec5SDimitry Andric     return true;
170b57cec5SDimitry Andric   if (*P == '\r' && *(P + 1) == '\n')
180b57cec5SDimitry Andric     return true;
190b57cec5SDimitry Andric   return false;
200b57cec5SDimitry Andric }
210b57cec5SDimitry Andric 
skipIfAtLineEnd(const char * & P)220b57cec5SDimitry Andric static bool skipIfAtLineEnd(const char *&P) {
230b57cec5SDimitry Andric   if (*P == '\n') {
240b57cec5SDimitry Andric     ++P;
250b57cec5SDimitry Andric     return true;
260b57cec5SDimitry Andric   }
270b57cec5SDimitry Andric   if (*P == '\r' && *(P + 1) == '\n') {
280b57cec5SDimitry Andric     P += 2;
290b57cec5SDimitry Andric     return true;
300b57cec5SDimitry Andric   }
310b57cec5SDimitry Andric   return false;
320b57cec5SDimitry Andric }
330b57cec5SDimitry Andric 
line_iterator(const MemoryBuffer & Buffer,bool SkipBlanks,char CommentMarker)340b57cec5SDimitry Andric line_iterator::line_iterator(const MemoryBuffer &Buffer, bool SkipBlanks,
350b57cec5SDimitry Andric                              char CommentMarker)
36e8d8bef9SDimitry Andric     : line_iterator(Buffer.getMemBufferRef(), SkipBlanks, CommentMarker) {}
37e8d8bef9SDimitry Andric 
line_iterator(const MemoryBufferRef & Buffer,bool SkipBlanks,char CommentMarker)38e8d8bef9SDimitry Andric line_iterator::line_iterator(const MemoryBufferRef &Buffer, bool SkipBlanks,
39e8d8bef9SDimitry Andric                              char CommentMarker)
40*bdd1243dSDimitry Andric     : Buffer(Buffer.getBufferSize() ? std::optional<MemoryBufferRef>(Buffer)
41*bdd1243dSDimitry Andric                                     : std::nullopt),
4281ad6265SDimitry Andric       CommentMarker(CommentMarker), SkipBlanks(SkipBlanks),
430b57cec5SDimitry Andric       CurrentLine(Buffer.getBufferSize() ? Buffer.getBufferStart() : nullptr,
440b57cec5SDimitry Andric                   0) {
450b57cec5SDimitry Andric   // Ensure that if we are constructed on a non-empty memory buffer that it is
460b57cec5SDimitry Andric   // a null terminated buffer.
470b57cec5SDimitry Andric   if (Buffer.getBufferSize()) {
480b57cec5SDimitry Andric     assert(Buffer.getBufferEnd()[0] == '\0');
490b57cec5SDimitry Andric     // Make sure we don't skip a leading newline if we're keeping blanks
500b57cec5SDimitry Andric     if (SkipBlanks || !isAtLineEnd(Buffer.getBufferStart()))
510b57cec5SDimitry Andric       advance();
520b57cec5SDimitry Andric   }
530b57cec5SDimitry Andric }
540b57cec5SDimitry Andric 
advance()550b57cec5SDimitry Andric void line_iterator::advance() {
560b57cec5SDimitry Andric   assert(Buffer && "Cannot advance past the end!");
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric   const char *Pos = CurrentLine.end();
590b57cec5SDimitry Andric   assert(Pos == Buffer->getBufferStart() || isAtLineEnd(Pos) || *Pos == '\0');
600b57cec5SDimitry Andric 
610b57cec5SDimitry Andric   if (skipIfAtLineEnd(Pos))
620b57cec5SDimitry Andric     ++LineNumber;
630b57cec5SDimitry Andric   if (!SkipBlanks && isAtLineEnd(Pos)) {
640b57cec5SDimitry Andric     // Nothing to do for a blank line.
650b57cec5SDimitry Andric   } else if (CommentMarker == '\0') {
660b57cec5SDimitry Andric     // If we're not stripping comments, this is simpler.
670b57cec5SDimitry Andric     while (skipIfAtLineEnd(Pos))
680b57cec5SDimitry Andric       ++LineNumber;
690b57cec5SDimitry Andric   } else {
700b57cec5SDimitry Andric     // Skip comments and count line numbers, which is a bit more complex.
710b57cec5SDimitry Andric     for (;;) {
720b57cec5SDimitry Andric       if (isAtLineEnd(Pos) && !SkipBlanks)
730b57cec5SDimitry Andric         break;
740b57cec5SDimitry Andric       if (*Pos == CommentMarker)
750b57cec5SDimitry Andric         do {
760b57cec5SDimitry Andric           ++Pos;
770b57cec5SDimitry Andric         } while (*Pos != '\0' && !isAtLineEnd(Pos));
780b57cec5SDimitry Andric       if (!skipIfAtLineEnd(Pos))
790b57cec5SDimitry Andric         break;
800b57cec5SDimitry Andric       ++LineNumber;
810b57cec5SDimitry Andric     }
820b57cec5SDimitry Andric   }
830b57cec5SDimitry Andric 
840b57cec5SDimitry Andric   if (*Pos == '\0') {
850b57cec5SDimitry Andric     // We've hit the end of the buffer, reset ourselves to the end state.
86*bdd1243dSDimitry Andric     Buffer = std::nullopt;
870b57cec5SDimitry Andric     CurrentLine = StringRef();
880b57cec5SDimitry Andric     return;
890b57cec5SDimitry Andric   }
900b57cec5SDimitry Andric 
910b57cec5SDimitry Andric   // Measure the line.
920b57cec5SDimitry Andric   size_t Length = 0;
930b57cec5SDimitry Andric   while (Pos[Length] != '\0' && !isAtLineEnd(&Pos[Length])) {
940b57cec5SDimitry Andric     ++Length;
950b57cec5SDimitry Andric   }
960b57cec5SDimitry Andric 
970b57cec5SDimitry Andric   CurrentLine = StringRef(Pos, Length);
980b57cec5SDimitry Andric }
99