15188c4b9SDmitri Gribenko #include "clang/AST/CommentLexer.h"
2ca7f80adSDmitri Gribenko #include "clang/AST/CommentCommandTraits.h"
3*bcef3411SDmitri Gribenko #include "clang/Basic/CharInfo.h"
4b2e5482bSDmitri Gribenko #include "llvm/ADT/StringExtras.h"
55188c4b9SDmitri Gribenko #include "llvm/ADT/StringSwitch.h"
69feeef40SDmitri Gribenko #include "llvm/Support/ConvertUTF.h"
75188c4b9SDmitri Gribenko #include "llvm/Support/ErrorHandling.h"
85188c4b9SDmitri Gribenko 
95188c4b9SDmitri Gribenko namespace clang {
105188c4b9SDmitri Gribenko namespace comments {
115188c4b9SDmitri Gribenko 
125188c4b9SDmitri Gribenko void Token::dump(const Lexer &L, const SourceManager &SM) const {
135188c4b9SDmitri Gribenko   llvm::errs() << "comments::Token Kind=" << Kind << " ";
145188c4b9SDmitri Gribenko   Loc.dump(SM);
155188c4b9SDmitri Gribenko   llvm::errs() << " " << Length << " \"" << L.getSpelling(*this, SM) << "\"\n";
165188c4b9SDmitri Gribenko }
175188c4b9SDmitri Gribenko 
184586df76SDmitri Gribenko namespace {
194586df76SDmitri Gribenko bool isHTMLNamedCharacterReferenceCharacter(char C) {
20*bcef3411SDmitri Gribenko   return isLetter(C);
214586df76SDmitri Gribenko }
224586df76SDmitri Gribenko 
234586df76SDmitri Gribenko bool isHTMLDecimalCharacterReferenceCharacter(char C) {
24*bcef3411SDmitri Gribenko   return isDigit(C);
254586df76SDmitri Gribenko }
264586df76SDmitri Gribenko 
274586df76SDmitri Gribenko bool isHTMLHexCharacterReferenceCharacter(char C) {
28*bcef3411SDmitri Gribenko   return isHexDigit(C);
294586df76SDmitri Gribenko }
30107618a6SDmitri Gribenko 
3128800da1SDmitri Gribenko StringRef convertCodePointToUTF8(llvm::BumpPtrAllocator &Allocator,
3228800da1SDmitri Gribenko                                  unsigned CodePoint) {
334586df76SDmitri Gribenko   char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT);
344586df76SDmitri Gribenko   char *ResolvedPtr = Resolved;
359feeef40SDmitri Gribenko   if (llvm::ConvertCodePointToUTF8(CodePoint, ResolvedPtr))
364586df76SDmitri Gribenko     return StringRef(Resolved, ResolvedPtr - Resolved);
374586df76SDmitri Gribenko   else
384586df76SDmitri Gribenko     return StringRef();
394586df76SDmitri Gribenko }
404586df76SDmitri Gribenko 
4128800da1SDmitri Gribenko #include "clang/AST/CommentHTMLTags.inc"
4228800da1SDmitri Gribenko #include "clang/AST/CommentHTMLNamedCharacterReferences.inc"
4328800da1SDmitri Gribenko 
4428800da1SDmitri Gribenko } // unnamed namespace
457b3ae190SFariborz Jahanian 
467b3ae190SFariborz Jahanian StringRef Lexer::resolveHTMLNamedCharacterReference(StringRef Name) const {
4728800da1SDmitri Gribenko   // Fast path, first check a few most widely used named character references.
487b3ae190SFariborz Jahanian   return llvm::StringSwitch<StringRef>(Name)
497b3ae190SFariborz Jahanian       .Case("amp", "&")
507b3ae190SFariborz Jahanian       .Case("lt", "<")
517b3ae190SFariborz Jahanian       .Case("gt", ">")
527b3ae190SFariborz Jahanian       .Case("quot", "\"")
537b3ae190SFariborz Jahanian       .Case("apos", "\'")
5428800da1SDmitri Gribenko       // Slow path.
5528800da1SDmitri Gribenko       .Default(translateHTMLNamedCharacterReferenceToUTF8(Name));
567b3ae190SFariborz Jahanian }
577b3ae190SFariborz Jahanian 
587b3ae190SFariborz Jahanian StringRef Lexer::resolveHTMLDecimalCharacterReference(StringRef Name) const {
594586df76SDmitri Gribenko   unsigned CodePoint = 0;
604586df76SDmitri Gribenko   for (unsigned i = 0, e = Name.size(); i != e; ++i) {
617b3ae190SFariborz Jahanian     assert(isHTMLDecimalCharacterReferenceCharacter(Name[i]));
627b3ae190SFariborz Jahanian     CodePoint *= 10;
637b3ae190SFariborz Jahanian     CodePoint += Name[i] - '0';
644586df76SDmitri Gribenko   }
6528800da1SDmitri Gribenko   return convertCodePointToUTF8(Allocator, CodePoint);
6628800da1SDmitri Gribenko }
674586df76SDmitri Gribenko 
6828800da1SDmitri Gribenko StringRef Lexer::resolveHTMLHexCharacterReference(StringRef Name) const {
6928800da1SDmitri Gribenko   unsigned CodePoint = 0;
7028800da1SDmitri Gribenko   for (unsigned i = 0, e = Name.size(); i != e; ++i) {
7128800da1SDmitri Gribenko     CodePoint *= 16;
7228800da1SDmitri Gribenko     const char C = Name[i];
7328800da1SDmitri Gribenko     assert(isHTMLHexCharacterReferenceCharacter(C));
7428800da1SDmitri Gribenko     CodePoint += llvm::hexDigitValue(C);
7528800da1SDmitri Gribenko   }
7628800da1SDmitri Gribenko   return convertCodePointToUTF8(Allocator, CodePoint);
774586df76SDmitri Gribenko }
784586df76SDmitri Gribenko 
795188c4b9SDmitri Gribenko void Lexer::skipLineStartingDecorations() {
805188c4b9SDmitri Gribenko   // This function should be called only for C comments
815188c4b9SDmitri Gribenko   assert(CommentState == LCS_InsideCComment);
825188c4b9SDmitri Gribenko 
835188c4b9SDmitri Gribenko   if (BufferPtr == CommentEnd)
845188c4b9SDmitri Gribenko     return;
855188c4b9SDmitri Gribenko 
865188c4b9SDmitri Gribenko   switch (*BufferPtr) {
875188c4b9SDmitri Gribenko   case ' ':
885188c4b9SDmitri Gribenko   case '\t':
895188c4b9SDmitri Gribenko   case '\f':
905188c4b9SDmitri Gribenko   case '\v': {
915188c4b9SDmitri Gribenko     const char *NewBufferPtr = BufferPtr;
925188c4b9SDmitri Gribenko     NewBufferPtr++;
935188c4b9SDmitri Gribenko     if (NewBufferPtr == CommentEnd)
945188c4b9SDmitri Gribenko       return;
955188c4b9SDmitri Gribenko 
965188c4b9SDmitri Gribenko     char C = *NewBufferPtr;
97*bcef3411SDmitri Gribenko     while (isHorizontalWhitespace(C)) {
985188c4b9SDmitri Gribenko       NewBufferPtr++;
995188c4b9SDmitri Gribenko       if (NewBufferPtr == CommentEnd)
1005188c4b9SDmitri Gribenko         return;
1015188c4b9SDmitri Gribenko       C = *NewBufferPtr;
1025188c4b9SDmitri Gribenko     }
1035188c4b9SDmitri Gribenko     if (C == '*')
1045188c4b9SDmitri Gribenko       BufferPtr = NewBufferPtr + 1;
1055188c4b9SDmitri Gribenko     break;
1065188c4b9SDmitri Gribenko   }
1075188c4b9SDmitri Gribenko   case '*':
1085188c4b9SDmitri Gribenko     BufferPtr++;
1095188c4b9SDmitri Gribenko     break;
1105188c4b9SDmitri Gribenko   }
1115188c4b9SDmitri Gribenko }
1125188c4b9SDmitri Gribenko 
1135188c4b9SDmitri Gribenko namespace {
114ec92531cSDmitri Gribenko /// Returns pointer to the first newline character in the string.
1155188c4b9SDmitri Gribenko const char *findNewline(const char *BufferPtr, const char *BufferEnd) {
1165188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
117*bcef3411SDmitri Gribenko     if (isVerticalWhitespace(*BufferPtr))
1185188c4b9SDmitri Gribenko       return BufferPtr;
1195188c4b9SDmitri Gribenko   }
1205188c4b9SDmitri Gribenko   return BufferEnd;
1215188c4b9SDmitri Gribenko }
1225188c4b9SDmitri Gribenko 
1235188c4b9SDmitri Gribenko const char *skipNewline(const char *BufferPtr, const char *BufferEnd) {
1245188c4b9SDmitri Gribenko   if (BufferPtr == BufferEnd)
1255188c4b9SDmitri Gribenko     return BufferPtr;
1265188c4b9SDmitri Gribenko 
1275188c4b9SDmitri Gribenko   if (*BufferPtr == '\n')
1285188c4b9SDmitri Gribenko     BufferPtr++;
1295188c4b9SDmitri Gribenko   else {
1305188c4b9SDmitri Gribenko     assert(*BufferPtr == '\r');
1315188c4b9SDmitri Gribenko     BufferPtr++;
1325188c4b9SDmitri Gribenko     if (BufferPtr != BufferEnd && *BufferPtr == '\n')
1335188c4b9SDmitri Gribenko       BufferPtr++;
1345188c4b9SDmitri Gribenko   }
1355188c4b9SDmitri Gribenko   return BufferPtr;
1365188c4b9SDmitri Gribenko }
1375188c4b9SDmitri Gribenko 
1384586df76SDmitri Gribenko const char *skipNamedCharacterReference(const char *BufferPtr,
1394586df76SDmitri Gribenko                                         const char *BufferEnd) {
1404586df76SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1414586df76SDmitri Gribenko     if (!isHTMLNamedCharacterReferenceCharacter(*BufferPtr))
1424586df76SDmitri Gribenko       return BufferPtr;
1434586df76SDmitri Gribenko   }
1444586df76SDmitri Gribenko   return BufferEnd;
1454586df76SDmitri Gribenko }
1464586df76SDmitri Gribenko 
1474586df76SDmitri Gribenko const char *skipDecimalCharacterReference(const char *BufferPtr,
1484586df76SDmitri Gribenko                                           const char *BufferEnd) {
1494586df76SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1504586df76SDmitri Gribenko     if (!isHTMLDecimalCharacterReferenceCharacter(*BufferPtr))
1514586df76SDmitri Gribenko       return BufferPtr;
1524586df76SDmitri Gribenko   }
1534586df76SDmitri Gribenko   return BufferEnd;
1544586df76SDmitri Gribenko }
1554586df76SDmitri Gribenko 
1564586df76SDmitri Gribenko const char *skipHexCharacterReference(const char *BufferPtr,
1574586df76SDmitri Gribenko                                           const char *BufferEnd) {
1584586df76SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1594586df76SDmitri Gribenko     if (!isHTMLHexCharacterReferenceCharacter(*BufferPtr))
1604586df76SDmitri Gribenko       return BufferPtr;
1614586df76SDmitri Gribenko   }
1624586df76SDmitri Gribenko   return BufferEnd;
1634586df76SDmitri Gribenko }
1644586df76SDmitri Gribenko 
16517709ae8SDmitri Gribenko bool isHTMLIdentifierStartingCharacter(char C) {
166*bcef3411SDmitri Gribenko   return isLetter(C);
16717709ae8SDmitri Gribenko }
16817709ae8SDmitri Gribenko 
1695188c4b9SDmitri Gribenko bool isHTMLIdentifierCharacter(char C) {
170*bcef3411SDmitri Gribenko   return isAlphanumeric(C);
1715188c4b9SDmitri Gribenko }
1725188c4b9SDmitri Gribenko 
1735188c4b9SDmitri Gribenko const char *skipHTMLIdentifier(const char *BufferPtr, const char *BufferEnd) {
1745188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1755188c4b9SDmitri Gribenko     if (!isHTMLIdentifierCharacter(*BufferPtr))
1765188c4b9SDmitri Gribenko       return BufferPtr;
1775188c4b9SDmitri Gribenko   }
1785188c4b9SDmitri Gribenko   return BufferEnd;
1795188c4b9SDmitri Gribenko }
1805188c4b9SDmitri Gribenko 
1815188c4b9SDmitri Gribenko /// Skip HTML string quoted in single or double quotes.  Escaping quotes inside
1825188c4b9SDmitri Gribenko /// string allowed.
1835188c4b9SDmitri Gribenko ///
1845188c4b9SDmitri Gribenko /// Returns pointer to closing quote.
1855188c4b9SDmitri Gribenko const char *skipHTMLQuotedString(const char *BufferPtr, const char *BufferEnd)
1865188c4b9SDmitri Gribenko {
1875188c4b9SDmitri Gribenko   const char Quote = *BufferPtr;
1885188c4b9SDmitri Gribenko   assert(Quote == '\"' || Quote == '\'');
1895188c4b9SDmitri Gribenko 
1905188c4b9SDmitri Gribenko   BufferPtr++;
1915188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1925188c4b9SDmitri Gribenko     const char C = *BufferPtr;
1935188c4b9SDmitri Gribenko     if (C == Quote && BufferPtr[-1] != '\\')
1945188c4b9SDmitri Gribenko       return BufferPtr;
1955188c4b9SDmitri Gribenko   }
1965188c4b9SDmitri Gribenko   return BufferEnd;
1975188c4b9SDmitri Gribenko }
1985188c4b9SDmitri Gribenko 
1995188c4b9SDmitri Gribenko const char *skipWhitespace(const char *BufferPtr, const char *BufferEnd) {
2005188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
2015188c4b9SDmitri Gribenko     if (!isWhitespace(*BufferPtr))
2025188c4b9SDmitri Gribenko       return BufferPtr;
2035188c4b9SDmitri Gribenko   }
2045188c4b9SDmitri Gribenko   return BufferEnd;
2055188c4b9SDmitri Gribenko }
2065188c4b9SDmitri Gribenko 
207e4a3997dSDmitri Gribenko bool isWhitespace(const char *BufferPtr, const char *BufferEnd) {
208e4a3997dSDmitri Gribenko   return skipWhitespace(BufferPtr, BufferEnd) == BufferEnd;
209e4a3997dSDmitri Gribenko }
210e4a3997dSDmitri Gribenko 
211ad45ad6fSDmitri Gribenko bool isCommandNameStartCharacter(char C) {
212*bcef3411SDmitri Gribenko   return isLetter(C);
213ad45ad6fSDmitri Gribenko }
214ad45ad6fSDmitri Gribenko 
2155188c4b9SDmitri Gribenko bool isCommandNameCharacter(char C) {
216*bcef3411SDmitri Gribenko   return isAlphanumeric(C);
2175188c4b9SDmitri Gribenko }
2185188c4b9SDmitri Gribenko 
2195188c4b9SDmitri Gribenko const char *skipCommandName(const char *BufferPtr, const char *BufferEnd) {
2205188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
2215188c4b9SDmitri Gribenko     if (!isCommandNameCharacter(*BufferPtr))
2225188c4b9SDmitri Gribenko       return BufferPtr;
2235188c4b9SDmitri Gribenko   }
2245188c4b9SDmitri Gribenko   return BufferEnd;
2255188c4b9SDmitri Gribenko }
2265188c4b9SDmitri Gribenko 
2275188c4b9SDmitri Gribenko /// Return the one past end pointer for BCPL comments.
2285188c4b9SDmitri Gribenko /// Handles newlines escaped with backslash or trigraph for backslahs.
2295188c4b9SDmitri Gribenko const char *findBCPLCommentEnd(const char *BufferPtr, const char *BufferEnd) {
2305188c4b9SDmitri Gribenko   const char *CurPtr = BufferPtr;
2315188c4b9SDmitri Gribenko   while (CurPtr != BufferEnd) {
232*bcef3411SDmitri Gribenko     while (!isVerticalWhitespace(*CurPtr)) {
2335188c4b9SDmitri Gribenko       CurPtr++;
2345188c4b9SDmitri Gribenko       if (CurPtr == BufferEnd)
2355188c4b9SDmitri Gribenko         return BufferEnd;
2365188c4b9SDmitri Gribenko     }
2375188c4b9SDmitri Gribenko     // We found a newline, check if it is escaped.
2385188c4b9SDmitri Gribenko     const char *EscapePtr = CurPtr - 1;
2395188c4b9SDmitri Gribenko     while(isHorizontalWhitespace(*EscapePtr))
2405188c4b9SDmitri Gribenko       EscapePtr--;
2415188c4b9SDmitri Gribenko 
2425188c4b9SDmitri Gribenko     if (*EscapePtr == '\\' ||
2435188c4b9SDmitri Gribenko         (EscapePtr - 2 >= BufferPtr && EscapePtr[0] == '/' &&
2445188c4b9SDmitri Gribenko          EscapePtr[-1] == '?' && EscapePtr[-2] == '?')) {
2455188c4b9SDmitri Gribenko       // We found an escaped newline.
2465188c4b9SDmitri Gribenko       CurPtr = skipNewline(CurPtr, BufferEnd);
2475188c4b9SDmitri Gribenko     } else
2485188c4b9SDmitri Gribenko       return CurPtr; // Not an escaped newline.
2495188c4b9SDmitri Gribenko   }
2505188c4b9SDmitri Gribenko   return BufferEnd;
2515188c4b9SDmitri Gribenko }
2525188c4b9SDmitri Gribenko 
2535188c4b9SDmitri Gribenko /// Return the one past end pointer for C comments.
2545188c4b9SDmitri Gribenko /// Very dumb, does not handle escaped newlines or trigraphs.
2555188c4b9SDmitri Gribenko const char *findCCommentEnd(const char *BufferPtr, const char *BufferEnd) {
2565188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
2575188c4b9SDmitri Gribenko     if (*BufferPtr == '*') {
2585188c4b9SDmitri Gribenko       assert(BufferPtr + 1 != BufferEnd);
2595188c4b9SDmitri Gribenko       if (*(BufferPtr + 1) == '/')
2605188c4b9SDmitri Gribenko         return BufferPtr;
2615188c4b9SDmitri Gribenko     }
2625188c4b9SDmitri Gribenko   }
2635188c4b9SDmitri Gribenko   llvm_unreachable("buffer end hit before '*/' was seen");
2645188c4b9SDmitri Gribenko }
2655188c4b9SDmitri Gribenko } // unnamed namespace
2665188c4b9SDmitri Gribenko 
2675188c4b9SDmitri Gribenko void Lexer::lexCommentText(Token &T) {
2685188c4b9SDmitri Gribenko   assert(CommentState == LCS_InsideBCPLComment ||
2695188c4b9SDmitri Gribenko          CommentState == LCS_InsideCComment);
2705188c4b9SDmitri Gribenko 
2715188c4b9SDmitri Gribenko   switch (State) {
2725188c4b9SDmitri Gribenko   case LS_Normal:
2735188c4b9SDmitri Gribenko     break;
2745188c4b9SDmitri Gribenko   case LS_VerbatimBlockFirstLine:
2755188c4b9SDmitri Gribenko     lexVerbatimBlockFirstLine(T);
2765188c4b9SDmitri Gribenko     return;
2775188c4b9SDmitri Gribenko   case LS_VerbatimBlockBody:
2785188c4b9SDmitri Gribenko     lexVerbatimBlockBody(T);
2795188c4b9SDmitri Gribenko     return;
2801669f702SDmitri Gribenko   case LS_VerbatimLineText:
2811669f702SDmitri Gribenko     lexVerbatimLineText(T);
2821669f702SDmitri Gribenko     return;
283e00ffc7bSDmitri Gribenko   case LS_HTMLStartTag:
284e00ffc7bSDmitri Gribenko     lexHTMLStartTag(T);
2855188c4b9SDmitri Gribenko     return;
286e00ffc7bSDmitri Gribenko   case LS_HTMLEndTag:
287e00ffc7bSDmitri Gribenko     lexHTMLEndTag(T);
288ec92531cSDmitri Gribenko     return;
2895188c4b9SDmitri Gribenko   }
2905188c4b9SDmitri Gribenko 
2915188c4b9SDmitri Gribenko   assert(State == LS_Normal);
2925188c4b9SDmitri Gribenko 
2935188c4b9SDmitri Gribenko   const char *TokenPtr = BufferPtr;
2945188c4b9SDmitri Gribenko   assert(TokenPtr < CommentEnd);
2955188c4b9SDmitri Gribenko   while (TokenPtr != CommentEnd) {
2965188c4b9SDmitri Gribenko     switch(*TokenPtr) {
2975188c4b9SDmitri Gribenko       case '\\':
2985188c4b9SDmitri Gribenko       case '@': {
2995188c4b9SDmitri Gribenko         TokenPtr++;
3005188c4b9SDmitri Gribenko         if (TokenPtr == CommentEnd) {
3014586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
3025188c4b9SDmitri Gribenko           return;
3035188c4b9SDmitri Gribenko         }
3045188c4b9SDmitri Gribenko         char C = *TokenPtr;
3055188c4b9SDmitri Gribenko         switch (C) {
3065188c4b9SDmitri Gribenko         default:
3075188c4b9SDmitri Gribenko           break;
3085188c4b9SDmitri Gribenko 
3095188c4b9SDmitri Gribenko         case '\\': case '@': case '&': case '$':
3105188c4b9SDmitri Gribenko         case '#':  case '<': case '>': case '%':
3115188c4b9SDmitri Gribenko         case '\"': case '.': case ':':
3125188c4b9SDmitri Gribenko           // This is one of \\ \@ \& \$ etc escape sequences.
3135188c4b9SDmitri Gribenko           TokenPtr++;
3145188c4b9SDmitri Gribenko           if (C == ':' && TokenPtr != CommentEnd && *TokenPtr == ':') {
3155188c4b9SDmitri Gribenko             // This is the \:: escape sequence.
3165188c4b9SDmitri Gribenko             TokenPtr++;
3175188c4b9SDmitri Gribenko           }
31860ddd8a1SDmitri Gribenko           StringRef UnescapedText(BufferPtr + 1, TokenPtr - (BufferPtr + 1));
3195188c4b9SDmitri Gribenko           formTokenWithChars(T, TokenPtr, tok::text);
32060ddd8a1SDmitri Gribenko           T.setText(UnescapedText);
3215188c4b9SDmitri Gribenko           return;
3225188c4b9SDmitri Gribenko         }
3235188c4b9SDmitri Gribenko 
3245188c4b9SDmitri Gribenko         // Don't make zero-length commands.
325ad45ad6fSDmitri Gribenko         if (!isCommandNameStartCharacter(*TokenPtr)) {
3264586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
3275188c4b9SDmitri Gribenko           return;
3285188c4b9SDmitri Gribenko         }
3295188c4b9SDmitri Gribenko 
3305188c4b9SDmitri Gribenko         TokenPtr = skipCommandName(TokenPtr, CommentEnd);
3315188c4b9SDmitri Gribenko         unsigned Length = TokenPtr - (BufferPtr + 1);
3325188c4b9SDmitri Gribenko 
3335188c4b9SDmitri Gribenko         // Hardcoded support for lexing LaTeX formula commands
3345188c4b9SDmitri Gribenko         // \f$ \f[ \f] \f{ \f} as a single command.
3355188c4b9SDmitri Gribenko         if (Length == 1 && TokenPtr[-1] == 'f' && TokenPtr != CommentEnd) {
3365188c4b9SDmitri Gribenko           C = *TokenPtr;
3375188c4b9SDmitri Gribenko           if (C == '$' || C == '[' || C == ']' || C == '{' || C == '}') {
3385188c4b9SDmitri Gribenko             TokenPtr++;
3395188c4b9SDmitri Gribenko             Length++;
3405188c4b9SDmitri Gribenko           }
3415188c4b9SDmitri Gribenko         }
3425188c4b9SDmitri Gribenko 
3435188c4b9SDmitri Gribenko         const StringRef CommandName(BufferPtr + 1, Length);
3445188c4b9SDmitri Gribenko 
3457acbf00fSDmitri Gribenko         const CommandInfo *Info = Traits.getCommandInfoOrNULL(CommandName);
3467acbf00fSDmitri Gribenko         if (!Info) {
3477acbf00fSDmitri Gribenko           formTokenWithChars(T, TokenPtr, tok::unknown_command);
3487acbf00fSDmitri Gribenko           T.setUnknownCommandName(CommandName);
3495188c4b9SDmitri Gribenko           return;
3505188c4b9SDmitri Gribenko         }
3517acbf00fSDmitri Gribenko         if (Info->IsVerbatimBlockCommand) {
3527acbf00fSDmitri Gribenko           setupAndLexVerbatimBlock(T, TokenPtr, *BufferPtr, Info);
3537acbf00fSDmitri Gribenko           return;
3547acbf00fSDmitri Gribenko         }
3557acbf00fSDmitri Gribenko         if (Info->IsVerbatimLineCommand) {
3567acbf00fSDmitri Gribenko           setupAndLexVerbatimLine(T, TokenPtr, Info);
3575188c4b9SDmitri Gribenko           return;
3585188c4b9SDmitri Gribenko         }
3595188c4b9SDmitri Gribenko         formTokenWithChars(T, TokenPtr, tok::command);
3607acbf00fSDmitri Gribenko         T.setCommandID(Info->getID());
3615188c4b9SDmitri Gribenko         return;
3625188c4b9SDmitri Gribenko       }
3635188c4b9SDmitri Gribenko 
3644586df76SDmitri Gribenko       case '&':
3654586df76SDmitri Gribenko         lexHTMLCharacterReference(T);
3664586df76SDmitri Gribenko         return;
3674586df76SDmitri Gribenko 
3685188c4b9SDmitri Gribenko       case '<': {
3695188c4b9SDmitri Gribenko         TokenPtr++;
3705188c4b9SDmitri Gribenko         if (TokenPtr == CommentEnd) {
3714586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
3725188c4b9SDmitri Gribenko           return;
3735188c4b9SDmitri Gribenko         }
3745188c4b9SDmitri Gribenko         const char C = *TokenPtr;
37517709ae8SDmitri Gribenko         if (isHTMLIdentifierStartingCharacter(C))
376e00ffc7bSDmitri Gribenko           setupAndLexHTMLStartTag(T);
3775188c4b9SDmitri Gribenko         else if (C == '/')
378e00ffc7bSDmitri Gribenko           setupAndLexHTMLEndTag(T);
3794586df76SDmitri Gribenko         else
3804586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
3814586df76SDmitri Gribenko 
3825188c4b9SDmitri Gribenko         return;
3835188c4b9SDmitri Gribenko       }
3845188c4b9SDmitri Gribenko 
3855188c4b9SDmitri Gribenko       case '\n':
3865188c4b9SDmitri Gribenko       case '\r':
3875188c4b9SDmitri Gribenko         TokenPtr = skipNewline(TokenPtr, CommentEnd);
3885188c4b9SDmitri Gribenko         formTokenWithChars(T, TokenPtr, tok::newline);
3895188c4b9SDmitri Gribenko 
3905188c4b9SDmitri Gribenko         if (CommentState == LCS_InsideCComment)
3915188c4b9SDmitri Gribenko           skipLineStartingDecorations();
3925188c4b9SDmitri Gribenko         return;
3935188c4b9SDmitri Gribenko 
3945188c4b9SDmitri Gribenko       default: {
39510af67a9SDmitri Gribenko         size_t End = StringRef(TokenPtr, CommentEnd - TokenPtr).
39610af67a9SDmitri Gribenko                          find_first_of("\n\r\\@&<");
39710af67a9SDmitri Gribenko         if (End != StringRef::npos)
39810af67a9SDmitri Gribenko           TokenPtr += End;
39910af67a9SDmitri Gribenko         else
40010af67a9SDmitri Gribenko           TokenPtr = CommentEnd;
4014586df76SDmitri Gribenko         formTextToken(T, TokenPtr);
4025188c4b9SDmitri Gribenko         return;
4035188c4b9SDmitri Gribenko       }
4045188c4b9SDmitri Gribenko     }
4055188c4b9SDmitri Gribenko   }
4065188c4b9SDmitri Gribenko }
4075188c4b9SDmitri Gribenko 
4085188c4b9SDmitri Gribenko void Lexer::setupAndLexVerbatimBlock(Token &T,
4095188c4b9SDmitri Gribenko                                      const char *TextBegin,
4107acbf00fSDmitri Gribenko                                      char Marker, const CommandInfo *Info) {
4117acbf00fSDmitri Gribenko   assert(Info->IsVerbatimBlockCommand);
4127acbf00fSDmitri Gribenko 
4135188c4b9SDmitri Gribenko   VerbatimBlockEndCommandName.clear();
4145188c4b9SDmitri Gribenko   VerbatimBlockEndCommandName.append(Marker == '\\' ? "\\" : "@");
4157acbf00fSDmitri Gribenko   VerbatimBlockEndCommandName.append(Info->EndCommandName);
4165188c4b9SDmitri Gribenko 
4175188c4b9SDmitri Gribenko   formTokenWithChars(T, TextBegin, tok::verbatim_block_begin);
4187acbf00fSDmitri Gribenko   T.setVerbatimBlockID(Info->getID());
4195188c4b9SDmitri Gribenko 
420ec92531cSDmitri Gribenko   // If there is a newline following the verbatim opening command, skip the
421ec92531cSDmitri Gribenko   // newline so that we don't create an tok::verbatim_block_line with empty
422ec92531cSDmitri Gribenko   // text content.
423*bcef3411SDmitri Gribenko   if (BufferPtr != CommentEnd &&
424*bcef3411SDmitri Gribenko       isVerticalWhitespace(*BufferPtr)) {
425ec92531cSDmitri Gribenko     BufferPtr = skipNewline(BufferPtr, CommentEnd);
426ec92531cSDmitri Gribenko     State = LS_VerbatimBlockBody;
427ec92531cSDmitri Gribenko     return;
428ec92531cSDmitri Gribenko   }
429ec92531cSDmitri Gribenko 
4305188c4b9SDmitri Gribenko   State = LS_VerbatimBlockFirstLine;
4315188c4b9SDmitri Gribenko }
4325188c4b9SDmitri Gribenko 
4335188c4b9SDmitri Gribenko void Lexer::lexVerbatimBlockFirstLine(Token &T) {
434e4a3997dSDmitri Gribenko again:
4355188c4b9SDmitri Gribenko   assert(BufferPtr < CommentEnd);
4365188c4b9SDmitri Gribenko 
4375188c4b9SDmitri Gribenko   // FIXME: It would be better to scan the text once, finding either the block
4385188c4b9SDmitri Gribenko   // end command or newline.
4395188c4b9SDmitri Gribenko   //
4405188c4b9SDmitri Gribenko   // Extract current line.
4415188c4b9SDmitri Gribenko   const char *Newline = findNewline(BufferPtr, CommentEnd);
4425188c4b9SDmitri Gribenko   StringRef Line(BufferPtr, Newline - BufferPtr);
4435188c4b9SDmitri Gribenko 
4445188c4b9SDmitri Gribenko   // Look for end command in current line.
4455188c4b9SDmitri Gribenko   size_t Pos = Line.find(VerbatimBlockEndCommandName);
446ec92531cSDmitri Gribenko   const char *TextEnd;
4475188c4b9SDmitri Gribenko   const char *NextLine;
4485188c4b9SDmitri Gribenko   if (Pos == StringRef::npos) {
4495188c4b9SDmitri Gribenko     // Current line is completely verbatim.
450ec92531cSDmitri Gribenko     TextEnd = Newline;
4515188c4b9SDmitri Gribenko     NextLine = skipNewline(Newline, CommentEnd);
4525188c4b9SDmitri Gribenko   } else if (Pos == 0) {
4535188c4b9SDmitri Gribenko     // Current line contains just an end command.
4545188c4b9SDmitri Gribenko     const char *End = BufferPtr + VerbatimBlockEndCommandName.size();
45560ddd8a1SDmitri Gribenko     StringRef Name(BufferPtr + 1, End - (BufferPtr + 1));
4565188c4b9SDmitri Gribenko     formTokenWithChars(T, End, tok::verbatim_block_end);
4577acbf00fSDmitri Gribenko     T.setVerbatimBlockID(Traits.getCommandInfo(Name)->getID());
4585188c4b9SDmitri Gribenko     State = LS_Normal;
4595188c4b9SDmitri Gribenko     return;
4605188c4b9SDmitri Gribenko   } else {
4615188c4b9SDmitri Gribenko     // There is some text, followed by end command.  Extract text first.
462ec92531cSDmitri Gribenko     TextEnd = BufferPtr + Pos;
463ec92531cSDmitri Gribenko     NextLine = TextEnd;
464e4a3997dSDmitri Gribenko     // If there is only whitespace before end command, skip whitespace.
465e4a3997dSDmitri Gribenko     if (isWhitespace(BufferPtr, TextEnd)) {
466e4a3997dSDmitri Gribenko       BufferPtr = TextEnd;
467e4a3997dSDmitri Gribenko       goto again;
468e4a3997dSDmitri Gribenko     }
4695188c4b9SDmitri Gribenko   }
4705188c4b9SDmitri Gribenko 
471ec92531cSDmitri Gribenko   StringRef Text(BufferPtr, TextEnd - BufferPtr);
4725188c4b9SDmitri Gribenko   formTokenWithChars(T, NextLine, tok::verbatim_block_line);
47360ddd8a1SDmitri Gribenko   T.setVerbatimBlockText(Text);
4745188c4b9SDmitri Gribenko 
4755188c4b9SDmitri Gribenko   State = LS_VerbatimBlockBody;
4765188c4b9SDmitri Gribenko }
4775188c4b9SDmitri Gribenko 
4785188c4b9SDmitri Gribenko void Lexer::lexVerbatimBlockBody(Token &T) {
4795188c4b9SDmitri Gribenko   assert(State == LS_VerbatimBlockBody);
4805188c4b9SDmitri Gribenko 
4815188c4b9SDmitri Gribenko   if (CommentState == LCS_InsideCComment)
4825188c4b9SDmitri Gribenko     skipLineStartingDecorations();
4835188c4b9SDmitri Gribenko 
4845188c4b9SDmitri Gribenko   lexVerbatimBlockFirstLine(T);
4855188c4b9SDmitri Gribenko }
4865188c4b9SDmitri Gribenko 
4877acbf00fSDmitri Gribenko void Lexer::setupAndLexVerbatimLine(Token &T, const char *TextBegin,
4887acbf00fSDmitri Gribenko                                     const CommandInfo *Info) {
4897acbf00fSDmitri Gribenko   assert(Info->IsVerbatimLineCommand);
4901669f702SDmitri Gribenko   formTokenWithChars(T, TextBegin, tok::verbatim_line_name);
4917acbf00fSDmitri Gribenko   T.setVerbatimLineID(Info->getID());
4921669f702SDmitri Gribenko 
4931669f702SDmitri Gribenko   State = LS_VerbatimLineText;
4941669f702SDmitri Gribenko }
4951669f702SDmitri Gribenko 
4961669f702SDmitri Gribenko void Lexer::lexVerbatimLineText(Token &T) {
4971669f702SDmitri Gribenko   assert(State == LS_VerbatimLineText);
4981669f702SDmitri Gribenko 
4995188c4b9SDmitri Gribenko   // Extract current line.
5005188c4b9SDmitri Gribenko   const char *Newline = findNewline(BufferPtr, CommentEnd);
5011669f702SDmitri Gribenko   const StringRef Text(BufferPtr, Newline - BufferPtr);
5021669f702SDmitri Gribenko   formTokenWithChars(T, Newline, tok::verbatim_line_text);
5035188c4b9SDmitri Gribenko   T.setVerbatimLineText(Text);
5041669f702SDmitri Gribenko 
5051669f702SDmitri Gribenko   State = LS_Normal;
5065188c4b9SDmitri Gribenko }
5075188c4b9SDmitri Gribenko 
5084586df76SDmitri Gribenko void Lexer::lexHTMLCharacterReference(Token &T) {
5094586df76SDmitri Gribenko   const char *TokenPtr = BufferPtr;
5104586df76SDmitri Gribenko   assert(*TokenPtr == '&');
5114586df76SDmitri Gribenko   TokenPtr++;
5124586df76SDmitri Gribenko   if (TokenPtr == CommentEnd) {
5134586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
5144586df76SDmitri Gribenko     return;
5154586df76SDmitri Gribenko   }
5164586df76SDmitri Gribenko   const char *NamePtr;
5174586df76SDmitri Gribenko   bool isNamed = false;
5184586df76SDmitri Gribenko   bool isDecimal = false;
5194586df76SDmitri Gribenko   char C = *TokenPtr;
5204586df76SDmitri Gribenko   if (isHTMLNamedCharacterReferenceCharacter(C)) {
5214586df76SDmitri Gribenko     NamePtr = TokenPtr;
5224586df76SDmitri Gribenko     TokenPtr = skipNamedCharacterReference(TokenPtr, CommentEnd);
5234586df76SDmitri Gribenko     isNamed = true;
5244586df76SDmitri Gribenko   } else if (C == '#') {
5254586df76SDmitri Gribenko     TokenPtr++;
5264586df76SDmitri Gribenko     if (TokenPtr == CommentEnd) {
5274586df76SDmitri Gribenko       formTextToken(T, TokenPtr);
5284586df76SDmitri Gribenko       return;
5294586df76SDmitri Gribenko     }
5304586df76SDmitri Gribenko     C = *TokenPtr;
5314586df76SDmitri Gribenko     if (isHTMLDecimalCharacterReferenceCharacter(C)) {
5324586df76SDmitri Gribenko       NamePtr = TokenPtr;
5334586df76SDmitri Gribenko       TokenPtr = skipDecimalCharacterReference(TokenPtr, CommentEnd);
5344586df76SDmitri Gribenko       isDecimal = true;
5354586df76SDmitri Gribenko     } else if (C == 'x' || C == 'X') {
5364586df76SDmitri Gribenko       TokenPtr++;
5374586df76SDmitri Gribenko       NamePtr = TokenPtr;
5384586df76SDmitri Gribenko       TokenPtr = skipHexCharacterReference(TokenPtr, CommentEnd);
5394586df76SDmitri Gribenko     } else {
5404586df76SDmitri Gribenko       formTextToken(T, TokenPtr);
5414586df76SDmitri Gribenko       return;
5424586df76SDmitri Gribenko     }
5434586df76SDmitri Gribenko   } else {
5444586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
5454586df76SDmitri Gribenko     return;
5464586df76SDmitri Gribenko   }
5474586df76SDmitri Gribenko   if (NamePtr == TokenPtr || TokenPtr == CommentEnd ||
5484586df76SDmitri Gribenko       *TokenPtr != ';') {
5494586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
5504586df76SDmitri Gribenko     return;
5514586df76SDmitri Gribenko   }
5524586df76SDmitri Gribenko   StringRef Name(NamePtr, TokenPtr - NamePtr);
5534586df76SDmitri Gribenko   TokenPtr++; // Skip semicolon.
5544586df76SDmitri Gribenko   StringRef Resolved;
55528800da1SDmitri Gribenko   if (isNamed)
5564586df76SDmitri Gribenko     Resolved = resolveHTMLNamedCharacterReference(Name);
5574586df76SDmitri Gribenko   else if (isDecimal)
5584586df76SDmitri Gribenko     Resolved = resolveHTMLDecimalCharacterReference(Name);
5594586df76SDmitri Gribenko   else
5604586df76SDmitri Gribenko     Resolved = resolveHTMLHexCharacterReference(Name);
5614586df76SDmitri Gribenko 
5624586df76SDmitri Gribenko   if (Resolved.empty()) {
5634586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
5644586df76SDmitri Gribenko     return;
5654586df76SDmitri Gribenko   }
5664586df76SDmitri Gribenko   formTokenWithChars(T, TokenPtr, tok::text);
5674586df76SDmitri Gribenko   T.setText(Resolved);
5684586df76SDmitri Gribenko   return;
5694586df76SDmitri Gribenko }
5704586df76SDmitri Gribenko 
571e00ffc7bSDmitri Gribenko void Lexer::setupAndLexHTMLStartTag(Token &T) {
57217709ae8SDmitri Gribenko   assert(BufferPtr[0] == '<' &&
57317709ae8SDmitri Gribenko          isHTMLIdentifierStartingCharacter(BufferPtr[1]));
5745188c4b9SDmitri Gribenko   const char *TagNameEnd = skipHTMLIdentifier(BufferPtr + 2, CommentEnd);
57560ddd8a1SDmitri Gribenko   StringRef Name(BufferPtr + 1, TagNameEnd - (BufferPtr + 1));
576107618a6SDmitri Gribenko   if (!isHTMLTagName(Name)) {
577107618a6SDmitri Gribenko     formTextToken(T, TagNameEnd);
578107618a6SDmitri Gribenko     return;
579107618a6SDmitri Gribenko   }
580107618a6SDmitri Gribenko 
581e00ffc7bSDmitri Gribenko   formTokenWithChars(T, TagNameEnd, tok::html_start_tag);
582e00ffc7bSDmitri Gribenko   T.setHTMLTagStartName(Name);
5835188c4b9SDmitri Gribenko 
5845188c4b9SDmitri Gribenko   BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
5855188c4b9SDmitri Gribenko 
58617709ae8SDmitri Gribenko   const char C = *BufferPtr;
58717709ae8SDmitri Gribenko   if (BufferPtr != CommentEnd &&
588f26054f0SDmitri Gribenko       (C == '>' || C == '/' || isHTMLIdentifierStartingCharacter(C)))
589e00ffc7bSDmitri Gribenko     State = LS_HTMLStartTag;
5905188c4b9SDmitri Gribenko }
5915188c4b9SDmitri Gribenko 
592e00ffc7bSDmitri Gribenko void Lexer::lexHTMLStartTag(Token &T) {
593e00ffc7bSDmitri Gribenko   assert(State == LS_HTMLStartTag);
5945188c4b9SDmitri Gribenko 
5955188c4b9SDmitri Gribenko   const char *TokenPtr = BufferPtr;
5965188c4b9SDmitri Gribenko   char C = *TokenPtr;
5975188c4b9SDmitri Gribenko   if (isHTMLIdentifierCharacter(C)) {
5985188c4b9SDmitri Gribenko     TokenPtr = skipHTMLIdentifier(TokenPtr, CommentEnd);
59960ddd8a1SDmitri Gribenko     StringRef Ident(BufferPtr, TokenPtr - BufferPtr);
6005188c4b9SDmitri Gribenko     formTokenWithChars(T, TokenPtr, tok::html_ident);
60160ddd8a1SDmitri Gribenko     T.setHTMLIdent(Ident);
6025188c4b9SDmitri Gribenko   } else {
6035188c4b9SDmitri Gribenko     switch (C) {
6045188c4b9SDmitri Gribenko     case '=':
6055188c4b9SDmitri Gribenko       TokenPtr++;
6065188c4b9SDmitri Gribenko       formTokenWithChars(T, TokenPtr, tok::html_equals);
6075188c4b9SDmitri Gribenko       break;
6085188c4b9SDmitri Gribenko     case '\"':
6095188c4b9SDmitri Gribenko     case '\'': {
6105188c4b9SDmitri Gribenko       const char *OpenQuote = TokenPtr;
6115188c4b9SDmitri Gribenko       TokenPtr = skipHTMLQuotedString(TokenPtr, CommentEnd);
6125188c4b9SDmitri Gribenko       const char *ClosingQuote = TokenPtr;
6135188c4b9SDmitri Gribenko       if (TokenPtr != CommentEnd) // Skip closing quote.
6145188c4b9SDmitri Gribenko         TokenPtr++;
6155188c4b9SDmitri Gribenko       formTokenWithChars(T, TokenPtr, tok::html_quoted_string);
6165188c4b9SDmitri Gribenko       T.setHTMLQuotedString(StringRef(OpenQuote + 1,
6175188c4b9SDmitri Gribenko                                       ClosingQuote - (OpenQuote + 1)));
6185188c4b9SDmitri Gribenko       break;
6195188c4b9SDmitri Gribenko     }
6205188c4b9SDmitri Gribenko     case '>':
6215188c4b9SDmitri Gribenko       TokenPtr++;
6225188c4b9SDmitri Gribenko       formTokenWithChars(T, TokenPtr, tok::html_greater);
62317709ae8SDmitri Gribenko       State = LS_Normal;
62417709ae8SDmitri Gribenko       return;
625f26054f0SDmitri Gribenko     case '/':
626f26054f0SDmitri Gribenko       TokenPtr++;
627f26054f0SDmitri Gribenko       if (TokenPtr != CommentEnd && *TokenPtr == '>') {
628f26054f0SDmitri Gribenko         TokenPtr++;
629f26054f0SDmitri Gribenko         formTokenWithChars(T, TokenPtr, tok::html_slash_greater);
6304586df76SDmitri Gribenko       } else
6314586df76SDmitri Gribenko         formTextToken(T, TokenPtr);
6324586df76SDmitri Gribenko 
633f26054f0SDmitri Gribenko       State = LS_Normal;
634f26054f0SDmitri Gribenko       return;
6355188c4b9SDmitri Gribenko     }
6365188c4b9SDmitri Gribenko   }
6375188c4b9SDmitri Gribenko 
6385188c4b9SDmitri Gribenko   // Now look ahead and return to normal state if we don't see any HTML tokens
6395188c4b9SDmitri Gribenko   // ahead.
6405188c4b9SDmitri Gribenko   BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
6415188c4b9SDmitri Gribenko   if (BufferPtr == CommentEnd) {
6425188c4b9SDmitri Gribenko     State = LS_Normal;
6435188c4b9SDmitri Gribenko     return;
6445188c4b9SDmitri Gribenko   }
6455188c4b9SDmitri Gribenko 
6465188c4b9SDmitri Gribenko   C = *BufferPtr;
64717709ae8SDmitri Gribenko   if (!isHTMLIdentifierStartingCharacter(C) &&
6485188c4b9SDmitri Gribenko       C != '=' && C != '\"' && C != '\'' && C != '>') {
6495188c4b9SDmitri Gribenko     State = LS_Normal;
6505188c4b9SDmitri Gribenko     return;
6515188c4b9SDmitri Gribenko   }
6525188c4b9SDmitri Gribenko }
6535188c4b9SDmitri Gribenko 
654e00ffc7bSDmitri Gribenko void Lexer::setupAndLexHTMLEndTag(Token &T) {
6555188c4b9SDmitri Gribenko   assert(BufferPtr[0] == '<' && BufferPtr[1] == '/');
6565188c4b9SDmitri Gribenko 
6575188c4b9SDmitri Gribenko   const char *TagNameBegin = skipWhitespace(BufferPtr + 2, CommentEnd);
6585188c4b9SDmitri Gribenko   const char *TagNameEnd = skipHTMLIdentifier(TagNameBegin, CommentEnd);
659107618a6SDmitri Gribenko   StringRef Name(TagNameBegin, TagNameEnd - TagNameBegin);
660107618a6SDmitri Gribenko   if (!isHTMLTagName(Name)) {
661107618a6SDmitri Gribenko     formTextToken(T, TagNameEnd);
662107618a6SDmitri Gribenko     return;
663107618a6SDmitri Gribenko   }
6645188c4b9SDmitri Gribenko 
6655188c4b9SDmitri Gribenko   const char *End = skipWhitespace(TagNameEnd, CommentEnd);
6665188c4b9SDmitri Gribenko 
667e00ffc7bSDmitri Gribenko   formTokenWithChars(T, End, tok::html_end_tag);
668107618a6SDmitri Gribenko   T.setHTMLTagEndName(Name);
669ec92531cSDmitri Gribenko 
670ec92531cSDmitri Gribenko   if (BufferPtr != CommentEnd && *BufferPtr == '>')
671e00ffc7bSDmitri Gribenko     State = LS_HTMLEndTag;
672ec92531cSDmitri Gribenko }
673ec92531cSDmitri Gribenko 
674e00ffc7bSDmitri Gribenko void Lexer::lexHTMLEndTag(Token &T) {
675ec92531cSDmitri Gribenko   assert(BufferPtr != CommentEnd && *BufferPtr == '>');
676ec92531cSDmitri Gribenko 
677ec92531cSDmitri Gribenko   formTokenWithChars(T, BufferPtr + 1, tok::html_greater);
678ec92531cSDmitri Gribenko   State = LS_Normal;
6795188c4b9SDmitri Gribenko }
6805188c4b9SDmitri Gribenko 
681ca7f80adSDmitri Gribenko Lexer::Lexer(llvm::BumpPtrAllocator &Allocator, const CommandTraits &Traits,
6826bab9113SDmitri Gribenko              SourceLocation FileLoc,
6835188c4b9SDmitri Gribenko              const char *BufferStart, const char *BufferEnd):
684ca7f80adSDmitri Gribenko     Allocator(Allocator), Traits(Traits),
6855188c4b9SDmitri Gribenko     BufferStart(BufferStart), BufferEnd(BufferEnd),
6866bab9113SDmitri Gribenko     FileLoc(FileLoc), BufferPtr(BufferStart),
6875188c4b9SDmitri Gribenko     CommentState(LCS_BeforeComment), State(LS_Normal) {
6885188c4b9SDmitri Gribenko }
6895188c4b9SDmitri Gribenko 
6905188c4b9SDmitri Gribenko void Lexer::lex(Token &T) {
6915188c4b9SDmitri Gribenko again:
6925188c4b9SDmitri Gribenko   switch (CommentState) {
6935188c4b9SDmitri Gribenko   case LCS_BeforeComment:
6945188c4b9SDmitri Gribenko     if (BufferPtr == BufferEnd) {
6955188c4b9SDmitri Gribenko       formTokenWithChars(T, BufferPtr, tok::eof);
6965188c4b9SDmitri Gribenko       return;
6975188c4b9SDmitri Gribenko     }
6985188c4b9SDmitri Gribenko 
6995188c4b9SDmitri Gribenko     assert(*BufferPtr == '/');
7005188c4b9SDmitri Gribenko     BufferPtr++; // Skip first slash.
7015188c4b9SDmitri Gribenko     switch(*BufferPtr) {
7025188c4b9SDmitri Gribenko     case '/': { // BCPL comment.
7035188c4b9SDmitri Gribenko       BufferPtr++; // Skip second slash.
7045188c4b9SDmitri Gribenko 
7055188c4b9SDmitri Gribenko       if (BufferPtr != BufferEnd) {
7065188c4b9SDmitri Gribenko         // Skip Doxygen magic marker, if it is present.
7075188c4b9SDmitri Gribenko         // It might be missing because of a typo //< or /*<, or because we
7085188c4b9SDmitri Gribenko         // merged this non-Doxygen comment into a bunch of Doxygen comments
7095188c4b9SDmitri Gribenko         // around it: /** ... */ /* ... */ /** ... */
7105188c4b9SDmitri Gribenko         const char C = *BufferPtr;
7115188c4b9SDmitri Gribenko         if (C == '/' || C == '!')
7125188c4b9SDmitri Gribenko           BufferPtr++;
7135188c4b9SDmitri Gribenko       }
7145188c4b9SDmitri Gribenko 
7155188c4b9SDmitri Gribenko       // Skip less-than symbol that marks trailing comments.
7165188c4b9SDmitri Gribenko       // Skip it even if the comment is not a Doxygen one, because //< and /*<
7175188c4b9SDmitri Gribenko       // are frequent typos.
7185188c4b9SDmitri Gribenko       if (BufferPtr != BufferEnd && *BufferPtr == '<')
7195188c4b9SDmitri Gribenko         BufferPtr++;
7205188c4b9SDmitri Gribenko 
7215188c4b9SDmitri Gribenko       CommentState = LCS_InsideBCPLComment;
722ec92531cSDmitri Gribenko       if (State != LS_VerbatimBlockBody && State != LS_VerbatimBlockFirstLine)
7235188c4b9SDmitri Gribenko         State = LS_Normal;
7245188c4b9SDmitri Gribenko       CommentEnd = findBCPLCommentEnd(BufferPtr, BufferEnd);
7255188c4b9SDmitri Gribenko       goto again;
7265188c4b9SDmitri Gribenko     }
7275188c4b9SDmitri Gribenko     case '*': { // C comment.
7285188c4b9SDmitri Gribenko       BufferPtr++; // Skip star.
7295188c4b9SDmitri Gribenko 
7305188c4b9SDmitri Gribenko       // Skip Doxygen magic marker.
7315188c4b9SDmitri Gribenko       const char C = *BufferPtr;
7325188c4b9SDmitri Gribenko       if ((C == '*' && *(BufferPtr + 1) != '/') || C == '!')
7335188c4b9SDmitri Gribenko         BufferPtr++;
7345188c4b9SDmitri Gribenko 
7355188c4b9SDmitri Gribenko       // Skip less-than symbol that marks trailing comments.
7365188c4b9SDmitri Gribenko       if (BufferPtr != BufferEnd && *BufferPtr == '<')
7375188c4b9SDmitri Gribenko         BufferPtr++;
7385188c4b9SDmitri Gribenko 
7395188c4b9SDmitri Gribenko       CommentState = LCS_InsideCComment;
7405188c4b9SDmitri Gribenko       State = LS_Normal;
7415188c4b9SDmitri Gribenko       CommentEnd = findCCommentEnd(BufferPtr, BufferEnd);
7425188c4b9SDmitri Gribenko       goto again;
7435188c4b9SDmitri Gribenko     }
7445188c4b9SDmitri Gribenko     default:
7455188c4b9SDmitri Gribenko       llvm_unreachable("second character of comment should be '/' or '*'");
7465188c4b9SDmitri Gribenko     }
7475188c4b9SDmitri Gribenko 
7485188c4b9SDmitri Gribenko   case LCS_BetweenComments: {
7495188c4b9SDmitri Gribenko     // Consecutive comments are extracted only if there is only whitespace
7505188c4b9SDmitri Gribenko     // between them.  So we can search for the start of the next comment.
7515188c4b9SDmitri Gribenko     const char *EndWhitespace = BufferPtr;
7525188c4b9SDmitri Gribenko     while(EndWhitespace != BufferEnd && *EndWhitespace != '/')
7535188c4b9SDmitri Gribenko       EndWhitespace++;
7545188c4b9SDmitri Gribenko 
7555188c4b9SDmitri Gribenko     // Turn any whitespace between comments (and there is only whitespace
75617709ae8SDmitri Gribenko     // between them -- guaranteed by comment extraction) into a newline.  We
75717709ae8SDmitri Gribenko     // have two newlines between C comments in total (first one was synthesized
75817709ae8SDmitri Gribenko     // after a comment).
7595188c4b9SDmitri Gribenko     formTokenWithChars(T, EndWhitespace, tok::newline);
7605188c4b9SDmitri Gribenko 
7615188c4b9SDmitri Gribenko     CommentState = LCS_BeforeComment;
7625188c4b9SDmitri Gribenko     break;
7635188c4b9SDmitri Gribenko   }
7645188c4b9SDmitri Gribenko 
7655188c4b9SDmitri Gribenko   case LCS_InsideBCPLComment:
7665188c4b9SDmitri Gribenko   case LCS_InsideCComment:
7675188c4b9SDmitri Gribenko     if (BufferPtr != CommentEnd) {
7685188c4b9SDmitri Gribenko       lexCommentText(T);
7695188c4b9SDmitri Gribenko       break;
7705188c4b9SDmitri Gribenko     } else {
7715188c4b9SDmitri Gribenko       // Skip C comment closing sequence.
7725188c4b9SDmitri Gribenko       if (CommentState == LCS_InsideCComment) {
7735188c4b9SDmitri Gribenko         assert(BufferPtr[0] == '*' && BufferPtr[1] == '/');
7745188c4b9SDmitri Gribenko         BufferPtr += 2;
7755188c4b9SDmitri Gribenko         assert(BufferPtr <= BufferEnd);
7765188c4b9SDmitri Gribenko 
7775188c4b9SDmitri Gribenko         // Synthenize newline just after the C comment, regardless if there is
7785188c4b9SDmitri Gribenko         // actually a newline.
7795188c4b9SDmitri Gribenko         formTokenWithChars(T, BufferPtr, tok::newline);
7805188c4b9SDmitri Gribenko 
7815188c4b9SDmitri Gribenko         CommentState = LCS_BetweenComments;
7825188c4b9SDmitri Gribenko         break;
7835188c4b9SDmitri Gribenko       } else {
7845188c4b9SDmitri Gribenko         // Don't synthesized a newline after BCPL comment.
7855188c4b9SDmitri Gribenko         CommentState = LCS_BetweenComments;
7865188c4b9SDmitri Gribenko         goto again;
7875188c4b9SDmitri Gribenko       }
7885188c4b9SDmitri Gribenko     }
7895188c4b9SDmitri Gribenko   }
7905188c4b9SDmitri Gribenko }
7915188c4b9SDmitri Gribenko 
7925188c4b9SDmitri Gribenko StringRef Lexer::getSpelling(const Token &Tok,
7935188c4b9SDmitri Gribenko                              const SourceManager &SourceMgr,
7945188c4b9SDmitri Gribenko                              bool *Invalid) const {
7955188c4b9SDmitri Gribenko   SourceLocation Loc = Tok.getLocation();
7965188c4b9SDmitri Gribenko   std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedLoc(Loc);
7975188c4b9SDmitri Gribenko 
7985188c4b9SDmitri Gribenko   bool InvalidTemp = false;
7995188c4b9SDmitri Gribenko   StringRef File = SourceMgr.getBufferData(LocInfo.first, &InvalidTemp);
8005188c4b9SDmitri Gribenko   if (InvalidTemp) {
8015188c4b9SDmitri Gribenko     *Invalid = true;
8025188c4b9SDmitri Gribenko     return StringRef();
8035188c4b9SDmitri Gribenko   }
8045188c4b9SDmitri Gribenko 
8055188c4b9SDmitri Gribenko   const char *Begin = File.data() + LocInfo.second;
8065188c4b9SDmitri Gribenko   return StringRef(Begin, Tok.getLength());
8075188c4b9SDmitri Gribenko }
8085188c4b9SDmitri Gribenko 
8095188c4b9SDmitri Gribenko } // end namespace comments
8105188c4b9SDmitri Gribenko } // end namespace clang
8115188c4b9SDmitri Gribenko 
812