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)
36*af732203SDimitry Andric     : line_iterator(Buffer.getMemBufferRef(), SkipBlanks, CommentMarker) {}
37*af732203SDimitry Andric 
line_iterator(const MemoryBufferRef & Buffer,bool SkipBlanks,char CommentMarker)38*af732203SDimitry Andric line_iterator::line_iterator(const MemoryBufferRef &Buffer, bool SkipBlanks,
39*af732203SDimitry Andric                              char CommentMarker)
40*af732203SDimitry Andric     : Buffer(Buffer.getBufferSize() ? Optional<MemoryBufferRef>(Buffer) : None),
410b57cec5SDimitry Andric       CommentMarker(CommentMarker), SkipBlanks(SkipBlanks), LineNumber(1),
420b57cec5SDimitry Andric       CurrentLine(Buffer.getBufferSize() ? Buffer.getBufferStart() : nullptr,
430b57cec5SDimitry Andric                   0) {
440b57cec5SDimitry Andric   // Ensure that if we are constructed on a non-empty memory buffer that it is
450b57cec5SDimitry Andric   // a null terminated buffer.
460b57cec5SDimitry Andric   if (Buffer.getBufferSize()) {
470b57cec5SDimitry Andric     assert(Buffer.getBufferEnd()[0] == '\0');
480b57cec5SDimitry Andric     // Make sure we don't skip a leading newline if we're keeping blanks
490b57cec5SDimitry Andric     if (SkipBlanks || !isAtLineEnd(Buffer.getBufferStart()))
500b57cec5SDimitry Andric       advance();
510b57cec5SDimitry Andric   }
520b57cec5SDimitry Andric }
530b57cec5SDimitry Andric 
advance()540b57cec5SDimitry Andric void line_iterator::advance() {
550b57cec5SDimitry Andric   assert(Buffer && "Cannot advance past the end!");
560b57cec5SDimitry Andric 
570b57cec5SDimitry Andric   const char *Pos = CurrentLine.end();
580b57cec5SDimitry Andric   assert(Pos == Buffer->getBufferStart() || isAtLineEnd(Pos) || *Pos == '\0');
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric   if (skipIfAtLineEnd(Pos))
610b57cec5SDimitry Andric     ++LineNumber;
620b57cec5SDimitry Andric   if (!SkipBlanks && isAtLineEnd(Pos)) {
630b57cec5SDimitry Andric     // Nothing to do for a blank line.
640b57cec5SDimitry Andric   } else if (CommentMarker == '\0') {
650b57cec5SDimitry Andric     // If we're not stripping comments, this is simpler.
660b57cec5SDimitry Andric     while (skipIfAtLineEnd(Pos))
670b57cec5SDimitry Andric       ++LineNumber;
680b57cec5SDimitry Andric   } else {
690b57cec5SDimitry Andric     // Skip comments and count line numbers, which is a bit more complex.
700b57cec5SDimitry Andric     for (;;) {
710b57cec5SDimitry Andric       if (isAtLineEnd(Pos) && !SkipBlanks)
720b57cec5SDimitry Andric         break;
730b57cec5SDimitry Andric       if (*Pos == CommentMarker)
740b57cec5SDimitry Andric         do {
750b57cec5SDimitry Andric           ++Pos;
760b57cec5SDimitry Andric         } while (*Pos != '\0' && !isAtLineEnd(Pos));
770b57cec5SDimitry Andric       if (!skipIfAtLineEnd(Pos))
780b57cec5SDimitry Andric         break;
790b57cec5SDimitry Andric       ++LineNumber;
800b57cec5SDimitry Andric     }
810b57cec5SDimitry Andric   }
820b57cec5SDimitry Andric 
830b57cec5SDimitry Andric   if (*Pos == '\0') {
840b57cec5SDimitry Andric     // We've hit the end of the buffer, reset ourselves to the end state.
85*af732203SDimitry Andric     Buffer = None;
860b57cec5SDimitry Andric     CurrentLine = StringRef();
870b57cec5SDimitry Andric     return;
880b57cec5SDimitry Andric   }
890b57cec5SDimitry Andric 
900b57cec5SDimitry Andric   // Measure the line.
910b57cec5SDimitry Andric   size_t Length = 0;
920b57cec5SDimitry Andric   while (Pos[Length] != '\0' && !isAtLineEnd(&Pos[Length])) {
930b57cec5SDimitry Andric     ++Length;
940b57cec5SDimitry Andric   }
950b57cec5SDimitry Andric 
960b57cec5SDimitry Andric   CurrentLine = StringRef(Pos, Length);
970b57cec5SDimitry Andric }
98