15188c4b9SDmitri Gribenko #include "clang/AST/CommentLexer.h"
2ca7f80adSDmitri Gribenko #include "clang/AST/CommentCommandTraits.h"
34586df76SDmitri Gribenko #include "clang/Basic/ConvertUTF.h"
45188c4b9SDmitri Gribenko #include "llvm/ADT/StringSwitch.h"
55188c4b9SDmitri Gribenko #include "llvm/Support/ErrorHandling.h"
65188c4b9SDmitri Gribenko 
75188c4b9SDmitri Gribenko namespace clang {
85188c4b9SDmitri Gribenko namespace comments {
95188c4b9SDmitri Gribenko 
105188c4b9SDmitri Gribenko void Token::dump(const Lexer &L, const SourceManager &SM) const {
115188c4b9SDmitri Gribenko   llvm::errs() << "comments::Token Kind=" << Kind << " ";
125188c4b9SDmitri Gribenko   Loc.dump(SM);
135188c4b9SDmitri Gribenko   llvm::errs() << " " << Length << " \"" << L.getSpelling(*this, SM) << "\"\n";
145188c4b9SDmitri Gribenko }
155188c4b9SDmitri Gribenko 
164586df76SDmitri Gribenko namespace {
174586df76SDmitri Gribenko bool isHTMLNamedCharacterReferenceCharacter(char C) {
184586df76SDmitri Gribenko   return (C >= 'a' && C <= 'z') ||
194586df76SDmitri Gribenko          (C >= 'A' && C <= 'Z');
204586df76SDmitri Gribenko }
214586df76SDmitri Gribenko 
224586df76SDmitri Gribenko bool isHTMLDecimalCharacterReferenceCharacter(char C) {
234586df76SDmitri Gribenko   return C >= '0' && C <= '9';
244586df76SDmitri Gribenko }
254586df76SDmitri Gribenko 
264586df76SDmitri Gribenko bool isHTMLHexCharacterReferenceCharacter(char C) {
274586df76SDmitri Gribenko   return (C >= '0' && C <= '9') ||
284586df76SDmitri Gribenko          (C >= 'a' && C <= 'f') ||
294586df76SDmitri Gribenko          (C >= 'A' && C <= 'F');
304586df76SDmitri Gribenko }
31*107618a6SDmitri Gribenko 
32*107618a6SDmitri Gribenko bool isHTMLTagName(StringRef Name) {
33*107618a6SDmitri Gribenko   return llvm::StringSwitch<bool>(Name)
34*107618a6SDmitri Gribenko       .Cases("em", "strong", true)
35*107618a6SDmitri Gribenko       .Cases("tt", "i", "b", "big", "small", true)
36*107618a6SDmitri Gribenko       .Cases("strike", "s", "u", "font", true)
37*107618a6SDmitri Gribenko       .Case("a", true)
38*107618a6SDmitri Gribenko       .Case("hr", true)
39*107618a6SDmitri Gribenko       .Cases("div", "span", true)
40*107618a6SDmitri Gribenko       .Cases("h1", "h2", "h3", true)
41*107618a6SDmitri Gribenko       .Cases("h4", "h5", "h6", true)
42*107618a6SDmitri Gribenko       .Case("code", true)
43*107618a6SDmitri Gribenko       .Case("blockquote", true)
44*107618a6SDmitri Gribenko       .Cases("sub", "sup", true)
45*107618a6SDmitri Gribenko       .Case("img", true)
46*107618a6SDmitri Gribenko       .Case("p", true)
47*107618a6SDmitri Gribenko       .Case("br", true)
48*107618a6SDmitri Gribenko       .Case("pre", true)
49*107618a6SDmitri Gribenko       .Cases("ins", "del", true)
50*107618a6SDmitri Gribenko       .Cases("ul", "ol", "li", true)
51*107618a6SDmitri Gribenko       .Cases("dl", "dt", "dd", true)
52*107618a6SDmitri Gribenko       .Cases("table", "caption", true)
53*107618a6SDmitri Gribenko       .Cases("thead", "tfoot", "tbody", true)
54*107618a6SDmitri Gribenko       .Cases("colgroup", "col", true)
55*107618a6SDmitri Gribenko       .Cases("tr", "th", "td", true)
56*107618a6SDmitri Gribenko       .Default(false);
57*107618a6SDmitri Gribenko }
584586df76SDmitri Gribenko } // unnamed namespace
594586df76SDmitri Gribenko 
604586df76SDmitri Gribenko StringRef Lexer::resolveHTMLNamedCharacterReference(StringRef Name) const {
614586df76SDmitri Gribenko   return llvm::StringSwitch<StringRef>(Name)
624586df76SDmitri Gribenko       .Case("amp", "&")
634586df76SDmitri Gribenko       .Case("lt", "<")
644586df76SDmitri Gribenko       .Case("gt", ">")
654586df76SDmitri Gribenko       .Case("quot", "\"")
664586df76SDmitri Gribenko       .Case("apos", "\'")
674586df76SDmitri Gribenko       .Default("");
684586df76SDmitri Gribenko }
694586df76SDmitri Gribenko 
704586df76SDmitri Gribenko StringRef Lexer::resolveHTMLDecimalCharacterReference(StringRef Name) const {
714586df76SDmitri Gribenko   unsigned CodePoint = 0;
724586df76SDmitri Gribenko   for (unsigned i = 0, e = Name.size(); i != e; ++i) {
734586df76SDmitri Gribenko     assert(isHTMLDecimalCharacterReferenceCharacter(Name[i]));
744586df76SDmitri Gribenko     CodePoint *= 10;
754586df76SDmitri Gribenko     CodePoint += Name[i] - '0';
764586df76SDmitri Gribenko   }
774586df76SDmitri Gribenko 
784586df76SDmitri Gribenko   char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT);
794586df76SDmitri Gribenko   char *ResolvedPtr = Resolved;
804586df76SDmitri Gribenko   if (ConvertCodePointToUTF8(CodePoint, ResolvedPtr))
814586df76SDmitri Gribenko     return StringRef(Resolved, ResolvedPtr - Resolved);
824586df76SDmitri Gribenko   else
834586df76SDmitri Gribenko     return StringRef();
844586df76SDmitri Gribenko }
854586df76SDmitri Gribenko 
864586df76SDmitri Gribenko StringRef Lexer::resolveHTMLHexCharacterReference(StringRef Name) const {
874586df76SDmitri Gribenko   unsigned CodePoint = 0;
884586df76SDmitri Gribenko   for (unsigned i = 0, e = Name.size(); i != e; ++i) {
894586df76SDmitri Gribenko     CodePoint *= 16;
904586df76SDmitri Gribenko     const char C = Name[i];
914586df76SDmitri Gribenko     assert(isHTMLHexCharacterReferenceCharacter(C));
924586df76SDmitri Gribenko     if (C >= '0' && C <= '9')
934586df76SDmitri Gribenko       CodePoint += Name[i] - '0';
944586df76SDmitri Gribenko     else if (C >= 'a' && C <= 'f')
954586df76SDmitri Gribenko       CodePoint += Name[i] - 'a' + 10;
964586df76SDmitri Gribenko     else
974586df76SDmitri Gribenko       CodePoint += Name[i] - 'A' + 10;
984586df76SDmitri Gribenko   }
994586df76SDmitri Gribenko 
1004586df76SDmitri Gribenko   char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT);
1014586df76SDmitri Gribenko   char *ResolvedPtr = Resolved;
1024586df76SDmitri Gribenko   if (ConvertCodePointToUTF8(CodePoint, ResolvedPtr))
1034586df76SDmitri Gribenko     return StringRef(Resolved, ResolvedPtr - Resolved);
1044586df76SDmitri Gribenko   else
1054586df76SDmitri Gribenko     return StringRef();
1064586df76SDmitri Gribenko }
1074586df76SDmitri Gribenko 
1085188c4b9SDmitri Gribenko void Lexer::skipLineStartingDecorations() {
1095188c4b9SDmitri Gribenko   // This function should be called only for C comments
1105188c4b9SDmitri Gribenko   assert(CommentState == LCS_InsideCComment);
1115188c4b9SDmitri Gribenko 
1125188c4b9SDmitri Gribenko   if (BufferPtr == CommentEnd)
1135188c4b9SDmitri Gribenko     return;
1145188c4b9SDmitri Gribenko 
1155188c4b9SDmitri Gribenko   switch (*BufferPtr) {
1165188c4b9SDmitri Gribenko   case ' ':
1175188c4b9SDmitri Gribenko   case '\t':
1185188c4b9SDmitri Gribenko   case '\f':
1195188c4b9SDmitri Gribenko   case '\v': {
1205188c4b9SDmitri Gribenko     const char *NewBufferPtr = BufferPtr;
1215188c4b9SDmitri Gribenko     NewBufferPtr++;
1225188c4b9SDmitri Gribenko     if (NewBufferPtr == CommentEnd)
1235188c4b9SDmitri Gribenko       return;
1245188c4b9SDmitri Gribenko 
1255188c4b9SDmitri Gribenko     char C = *NewBufferPtr;
1265188c4b9SDmitri Gribenko     while (C == ' ' || C == '\t' || C == '\f' || C == '\v') {
1275188c4b9SDmitri Gribenko       NewBufferPtr++;
1285188c4b9SDmitri Gribenko       if (NewBufferPtr == CommentEnd)
1295188c4b9SDmitri Gribenko         return;
1305188c4b9SDmitri Gribenko       C = *NewBufferPtr;
1315188c4b9SDmitri Gribenko     }
1325188c4b9SDmitri Gribenko     if (C == '*')
1335188c4b9SDmitri Gribenko       BufferPtr = NewBufferPtr + 1;
1345188c4b9SDmitri Gribenko     break;
1355188c4b9SDmitri Gribenko   }
1365188c4b9SDmitri Gribenko   case '*':
1375188c4b9SDmitri Gribenko     BufferPtr++;
1385188c4b9SDmitri Gribenko     break;
1395188c4b9SDmitri Gribenko   }
1405188c4b9SDmitri Gribenko }
1415188c4b9SDmitri Gribenko 
1425188c4b9SDmitri Gribenko namespace {
143ec92531cSDmitri Gribenko /// Returns pointer to the first newline character in the string.
1445188c4b9SDmitri Gribenko const char *findNewline(const char *BufferPtr, const char *BufferEnd) {
1455188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1465188c4b9SDmitri Gribenko     const char C = *BufferPtr;
1475188c4b9SDmitri Gribenko     if (C == '\n' || C == '\r')
1485188c4b9SDmitri Gribenko       return BufferPtr;
1495188c4b9SDmitri Gribenko   }
1505188c4b9SDmitri Gribenko   return BufferEnd;
1515188c4b9SDmitri Gribenko }
1525188c4b9SDmitri Gribenko 
1535188c4b9SDmitri Gribenko const char *skipNewline(const char *BufferPtr, const char *BufferEnd) {
1545188c4b9SDmitri Gribenko   if (BufferPtr == BufferEnd)
1555188c4b9SDmitri Gribenko     return BufferPtr;
1565188c4b9SDmitri Gribenko 
1575188c4b9SDmitri Gribenko   if (*BufferPtr == '\n')
1585188c4b9SDmitri Gribenko     BufferPtr++;
1595188c4b9SDmitri Gribenko   else {
1605188c4b9SDmitri Gribenko     assert(*BufferPtr == '\r');
1615188c4b9SDmitri Gribenko     BufferPtr++;
1625188c4b9SDmitri Gribenko     if (BufferPtr != BufferEnd && *BufferPtr == '\n')
1635188c4b9SDmitri Gribenko       BufferPtr++;
1645188c4b9SDmitri Gribenko   }
1655188c4b9SDmitri Gribenko   return BufferPtr;
1665188c4b9SDmitri Gribenko }
1675188c4b9SDmitri Gribenko 
1684586df76SDmitri Gribenko const char *skipNamedCharacterReference(const char *BufferPtr,
1694586df76SDmitri Gribenko                                         const char *BufferEnd) {
1704586df76SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1714586df76SDmitri Gribenko     if (!isHTMLNamedCharacterReferenceCharacter(*BufferPtr))
1724586df76SDmitri Gribenko       return BufferPtr;
1734586df76SDmitri Gribenko   }
1744586df76SDmitri Gribenko   return BufferEnd;
1754586df76SDmitri Gribenko }
1764586df76SDmitri Gribenko 
1774586df76SDmitri Gribenko const char *skipDecimalCharacterReference(const char *BufferPtr,
1784586df76SDmitri Gribenko                                           const char *BufferEnd) {
1794586df76SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1804586df76SDmitri Gribenko     if (!isHTMLDecimalCharacterReferenceCharacter(*BufferPtr))
1814586df76SDmitri Gribenko       return BufferPtr;
1824586df76SDmitri Gribenko   }
1834586df76SDmitri Gribenko   return BufferEnd;
1844586df76SDmitri Gribenko }
1854586df76SDmitri Gribenko 
1864586df76SDmitri Gribenko const char *skipHexCharacterReference(const char *BufferPtr,
1874586df76SDmitri Gribenko                                           const char *BufferEnd) {
1884586df76SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1894586df76SDmitri Gribenko     if (!isHTMLHexCharacterReferenceCharacter(*BufferPtr))
1904586df76SDmitri Gribenko       return BufferPtr;
1914586df76SDmitri Gribenko   }
1924586df76SDmitri Gribenko   return BufferEnd;
1934586df76SDmitri Gribenko }
1944586df76SDmitri Gribenko 
19517709ae8SDmitri Gribenko bool isHTMLIdentifierStartingCharacter(char C) {
19617709ae8SDmitri Gribenko   return (C >= 'a' && C <= 'z') ||
19717709ae8SDmitri Gribenko          (C >= 'A' && C <= 'Z');
19817709ae8SDmitri Gribenko }
19917709ae8SDmitri Gribenko 
2005188c4b9SDmitri Gribenko bool isHTMLIdentifierCharacter(char C) {
2015188c4b9SDmitri Gribenko   return (C >= 'a' && C <= 'z') ||
2025188c4b9SDmitri Gribenko          (C >= 'A' && C <= 'Z') ||
2035188c4b9SDmitri Gribenko          (C >= '0' && C <= '9');
2045188c4b9SDmitri Gribenko }
2055188c4b9SDmitri Gribenko 
2065188c4b9SDmitri Gribenko const char *skipHTMLIdentifier(const char *BufferPtr, const char *BufferEnd) {
2075188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
2085188c4b9SDmitri Gribenko     if (!isHTMLIdentifierCharacter(*BufferPtr))
2095188c4b9SDmitri Gribenko       return BufferPtr;
2105188c4b9SDmitri Gribenko   }
2115188c4b9SDmitri Gribenko   return BufferEnd;
2125188c4b9SDmitri Gribenko }
2135188c4b9SDmitri Gribenko 
2145188c4b9SDmitri Gribenko /// Skip HTML string quoted in single or double quotes.  Escaping quotes inside
2155188c4b9SDmitri Gribenko /// string allowed.
2165188c4b9SDmitri Gribenko ///
2175188c4b9SDmitri Gribenko /// Returns pointer to closing quote.
2185188c4b9SDmitri Gribenko const char *skipHTMLQuotedString(const char *BufferPtr, const char *BufferEnd)
2195188c4b9SDmitri Gribenko {
2205188c4b9SDmitri Gribenko   const char Quote = *BufferPtr;
2215188c4b9SDmitri Gribenko   assert(Quote == '\"' || Quote == '\'');
2225188c4b9SDmitri Gribenko 
2235188c4b9SDmitri Gribenko   BufferPtr++;
2245188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
2255188c4b9SDmitri Gribenko     const char C = *BufferPtr;
2265188c4b9SDmitri Gribenko     if (C == Quote && BufferPtr[-1] != '\\')
2275188c4b9SDmitri Gribenko       return BufferPtr;
2285188c4b9SDmitri Gribenko   }
2295188c4b9SDmitri Gribenko   return BufferEnd;
2305188c4b9SDmitri Gribenko }
2315188c4b9SDmitri Gribenko 
2325188c4b9SDmitri Gribenko bool isHorizontalWhitespace(char C) {
2335188c4b9SDmitri Gribenko   return C == ' ' || C == '\t' || C == '\f' || C == '\v';
2345188c4b9SDmitri Gribenko }
2355188c4b9SDmitri Gribenko 
2365188c4b9SDmitri Gribenko bool isWhitespace(char C) {
2375188c4b9SDmitri Gribenko   return C == ' ' || C == '\n' || C == '\r' ||
2385188c4b9SDmitri Gribenko          C == '\t' || C == '\f' || C == '\v';
2395188c4b9SDmitri Gribenko }
2405188c4b9SDmitri Gribenko 
2415188c4b9SDmitri Gribenko const char *skipWhitespace(const char *BufferPtr, const char *BufferEnd) {
2425188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
2435188c4b9SDmitri Gribenko     if (!isWhitespace(*BufferPtr))
2445188c4b9SDmitri Gribenko       return BufferPtr;
2455188c4b9SDmitri Gribenko   }
2465188c4b9SDmitri Gribenko   return BufferEnd;
2475188c4b9SDmitri Gribenko }
2485188c4b9SDmitri Gribenko 
249e4a3997dSDmitri Gribenko bool isWhitespace(const char *BufferPtr, const char *BufferEnd) {
250e4a3997dSDmitri Gribenko   return skipWhitespace(BufferPtr, BufferEnd) == BufferEnd;
251e4a3997dSDmitri Gribenko }
252e4a3997dSDmitri Gribenko 
2535188c4b9SDmitri Gribenko bool isCommandNameCharacter(char C) {
2545188c4b9SDmitri Gribenko   return (C >= 'a' && C <= 'z') ||
2555188c4b9SDmitri Gribenko          (C >= 'A' && C <= 'Z') ||
2565188c4b9SDmitri Gribenko          (C >= '0' && C <= '9');
2575188c4b9SDmitri Gribenko }
2585188c4b9SDmitri Gribenko 
2595188c4b9SDmitri Gribenko const char *skipCommandName(const char *BufferPtr, const char *BufferEnd) {
2605188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
2615188c4b9SDmitri Gribenko     if (!isCommandNameCharacter(*BufferPtr))
2625188c4b9SDmitri Gribenko       return BufferPtr;
2635188c4b9SDmitri Gribenko   }
2645188c4b9SDmitri Gribenko   return BufferEnd;
2655188c4b9SDmitri Gribenko }
2665188c4b9SDmitri Gribenko 
2675188c4b9SDmitri Gribenko /// Return the one past end pointer for BCPL comments.
2685188c4b9SDmitri Gribenko /// Handles newlines escaped with backslash or trigraph for backslahs.
2695188c4b9SDmitri Gribenko const char *findBCPLCommentEnd(const char *BufferPtr, const char *BufferEnd) {
2705188c4b9SDmitri Gribenko   const char *CurPtr = BufferPtr;
2715188c4b9SDmitri Gribenko   while (CurPtr != BufferEnd) {
2725188c4b9SDmitri Gribenko     char C = *CurPtr;
2735188c4b9SDmitri Gribenko     while (C != '\n' && C != '\r') {
2745188c4b9SDmitri Gribenko       CurPtr++;
2755188c4b9SDmitri Gribenko       if (CurPtr == BufferEnd)
2765188c4b9SDmitri Gribenko         return BufferEnd;
2775188c4b9SDmitri Gribenko       C = *CurPtr;
2785188c4b9SDmitri Gribenko     }
2795188c4b9SDmitri Gribenko     // We found a newline, check if it is escaped.
2805188c4b9SDmitri Gribenko     const char *EscapePtr = CurPtr - 1;
2815188c4b9SDmitri Gribenko     while(isHorizontalWhitespace(*EscapePtr))
2825188c4b9SDmitri Gribenko       EscapePtr--;
2835188c4b9SDmitri Gribenko 
2845188c4b9SDmitri Gribenko     if (*EscapePtr == '\\' ||
2855188c4b9SDmitri Gribenko         (EscapePtr - 2 >= BufferPtr && EscapePtr[0] == '/' &&
2865188c4b9SDmitri Gribenko          EscapePtr[-1] == '?' && EscapePtr[-2] == '?')) {
2875188c4b9SDmitri Gribenko       // We found an escaped newline.
2885188c4b9SDmitri Gribenko       CurPtr = skipNewline(CurPtr, BufferEnd);
2895188c4b9SDmitri Gribenko     } else
2905188c4b9SDmitri Gribenko       return CurPtr; // Not an escaped newline.
2915188c4b9SDmitri Gribenko   }
2925188c4b9SDmitri Gribenko   return BufferEnd;
2935188c4b9SDmitri Gribenko }
2945188c4b9SDmitri Gribenko 
2955188c4b9SDmitri Gribenko /// Return the one past end pointer for C comments.
2965188c4b9SDmitri Gribenko /// Very dumb, does not handle escaped newlines or trigraphs.
2975188c4b9SDmitri Gribenko const char *findCCommentEnd(const char *BufferPtr, const char *BufferEnd) {
2985188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
2995188c4b9SDmitri Gribenko     if (*BufferPtr == '*') {
3005188c4b9SDmitri Gribenko       assert(BufferPtr + 1 != BufferEnd);
3015188c4b9SDmitri Gribenko       if (*(BufferPtr + 1) == '/')
3025188c4b9SDmitri Gribenko         return BufferPtr;
3035188c4b9SDmitri Gribenko     }
3045188c4b9SDmitri Gribenko   }
3055188c4b9SDmitri Gribenko   llvm_unreachable("buffer end hit before '*/' was seen");
3065188c4b9SDmitri Gribenko }
3075188c4b9SDmitri Gribenko } // unnamed namespace
3085188c4b9SDmitri Gribenko 
3095188c4b9SDmitri Gribenko void Lexer::lexCommentText(Token &T) {
3105188c4b9SDmitri Gribenko   assert(CommentState == LCS_InsideBCPLComment ||
3115188c4b9SDmitri Gribenko          CommentState == LCS_InsideCComment);
3125188c4b9SDmitri Gribenko 
3135188c4b9SDmitri Gribenko   switch (State) {
3145188c4b9SDmitri Gribenko   case LS_Normal:
3155188c4b9SDmitri Gribenko     break;
3165188c4b9SDmitri Gribenko   case LS_VerbatimBlockFirstLine:
3175188c4b9SDmitri Gribenko     lexVerbatimBlockFirstLine(T);
3185188c4b9SDmitri Gribenko     return;
3195188c4b9SDmitri Gribenko   case LS_VerbatimBlockBody:
3205188c4b9SDmitri Gribenko     lexVerbatimBlockBody(T);
3215188c4b9SDmitri Gribenko     return;
3221669f702SDmitri Gribenko   case LS_VerbatimLineText:
3231669f702SDmitri Gribenko     lexVerbatimLineText(T);
3241669f702SDmitri Gribenko     return;
325e00ffc7bSDmitri Gribenko   case LS_HTMLStartTag:
326e00ffc7bSDmitri Gribenko     lexHTMLStartTag(T);
3275188c4b9SDmitri Gribenko     return;
328e00ffc7bSDmitri Gribenko   case LS_HTMLEndTag:
329e00ffc7bSDmitri Gribenko     lexHTMLEndTag(T);
330ec92531cSDmitri Gribenko     return;
3315188c4b9SDmitri Gribenko   }
3325188c4b9SDmitri Gribenko 
3335188c4b9SDmitri Gribenko   assert(State == LS_Normal);
3345188c4b9SDmitri Gribenko 
3355188c4b9SDmitri Gribenko   const char *TokenPtr = BufferPtr;
3365188c4b9SDmitri Gribenko   assert(TokenPtr < CommentEnd);
3375188c4b9SDmitri Gribenko   while (TokenPtr != CommentEnd) {
3385188c4b9SDmitri Gribenko     switch(*TokenPtr) {
3395188c4b9SDmitri Gribenko       case '\\':
3405188c4b9SDmitri Gribenko       case '@': {
3415188c4b9SDmitri Gribenko         TokenPtr++;
3425188c4b9SDmitri Gribenko         if (TokenPtr == CommentEnd) {
3434586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
3445188c4b9SDmitri Gribenko           return;
3455188c4b9SDmitri Gribenko         }
3465188c4b9SDmitri Gribenko         char C = *TokenPtr;
3475188c4b9SDmitri Gribenko         switch (C) {
3485188c4b9SDmitri Gribenko         default:
3495188c4b9SDmitri Gribenko           break;
3505188c4b9SDmitri Gribenko 
3515188c4b9SDmitri Gribenko         case '\\': case '@': case '&': case '$':
3525188c4b9SDmitri Gribenko         case '#':  case '<': case '>': case '%':
3535188c4b9SDmitri Gribenko         case '\"': case '.': case ':':
3545188c4b9SDmitri Gribenko           // This is one of \\ \@ \& \$ etc escape sequences.
3555188c4b9SDmitri Gribenko           TokenPtr++;
3565188c4b9SDmitri Gribenko           if (C == ':' && TokenPtr != CommentEnd && *TokenPtr == ':') {
3575188c4b9SDmitri Gribenko             // This is the \:: escape sequence.
3585188c4b9SDmitri Gribenko             TokenPtr++;
3595188c4b9SDmitri Gribenko           }
36060ddd8a1SDmitri Gribenko           StringRef UnescapedText(BufferPtr + 1, TokenPtr - (BufferPtr + 1));
3615188c4b9SDmitri Gribenko           formTokenWithChars(T, TokenPtr, tok::text);
36260ddd8a1SDmitri Gribenko           T.setText(UnescapedText);
3635188c4b9SDmitri Gribenko           return;
3645188c4b9SDmitri Gribenko         }
3655188c4b9SDmitri Gribenko 
3665188c4b9SDmitri Gribenko         // Don't make zero-length commands.
3675188c4b9SDmitri Gribenko         if (!isCommandNameCharacter(*TokenPtr)) {
3684586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
3695188c4b9SDmitri Gribenko           return;
3705188c4b9SDmitri Gribenko         }
3715188c4b9SDmitri Gribenko 
3725188c4b9SDmitri Gribenko         TokenPtr = skipCommandName(TokenPtr, CommentEnd);
3735188c4b9SDmitri Gribenko         unsigned Length = TokenPtr - (BufferPtr + 1);
3745188c4b9SDmitri Gribenko 
3755188c4b9SDmitri Gribenko         // Hardcoded support for lexing LaTeX formula commands
3765188c4b9SDmitri Gribenko         // \f$ \f[ \f] \f{ \f} as a single command.
3775188c4b9SDmitri Gribenko         if (Length == 1 && TokenPtr[-1] == 'f' && TokenPtr != CommentEnd) {
3785188c4b9SDmitri Gribenko           C = *TokenPtr;
3795188c4b9SDmitri Gribenko           if (C == '$' || C == '[' || C == ']' || C == '{' || C == '}') {
3805188c4b9SDmitri Gribenko             TokenPtr++;
3815188c4b9SDmitri Gribenko             Length++;
3825188c4b9SDmitri Gribenko           }
3835188c4b9SDmitri Gribenko         }
3845188c4b9SDmitri Gribenko 
3855188c4b9SDmitri Gribenko         const StringRef CommandName(BufferPtr + 1, Length);
3865188c4b9SDmitri Gribenko         StringRef EndName;
3875188c4b9SDmitri Gribenko 
388ca7f80adSDmitri Gribenko         if (Traits.isVerbatimBlockCommand(CommandName, EndName)) {
3895188c4b9SDmitri Gribenko           setupAndLexVerbatimBlock(T, TokenPtr, *BufferPtr, EndName);
3905188c4b9SDmitri Gribenko           return;
3915188c4b9SDmitri Gribenko         }
392ca7f80adSDmitri Gribenko         if (Traits.isVerbatimLineCommand(CommandName)) {
3931669f702SDmitri Gribenko           setupAndLexVerbatimLine(T, TokenPtr);
3945188c4b9SDmitri Gribenko           return;
3955188c4b9SDmitri Gribenko         }
3965188c4b9SDmitri Gribenko         formTokenWithChars(T, TokenPtr, tok::command);
3975188c4b9SDmitri Gribenko         T.setCommandName(CommandName);
3985188c4b9SDmitri Gribenko         return;
3995188c4b9SDmitri Gribenko       }
4005188c4b9SDmitri Gribenko 
4014586df76SDmitri Gribenko       case '&':
4024586df76SDmitri Gribenko         lexHTMLCharacterReference(T);
4034586df76SDmitri Gribenko         return;
4044586df76SDmitri Gribenko 
4055188c4b9SDmitri Gribenko       case '<': {
4065188c4b9SDmitri Gribenko         TokenPtr++;
4075188c4b9SDmitri Gribenko         if (TokenPtr == CommentEnd) {
4084586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
4095188c4b9SDmitri Gribenko           return;
4105188c4b9SDmitri Gribenko         }
4115188c4b9SDmitri Gribenko         const char C = *TokenPtr;
41217709ae8SDmitri Gribenko         if (isHTMLIdentifierStartingCharacter(C))
413e00ffc7bSDmitri Gribenko           setupAndLexHTMLStartTag(T);
4145188c4b9SDmitri Gribenko         else if (C == '/')
415e00ffc7bSDmitri Gribenko           setupAndLexHTMLEndTag(T);
4164586df76SDmitri Gribenko         else
4174586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
4184586df76SDmitri Gribenko 
4195188c4b9SDmitri Gribenko         return;
4205188c4b9SDmitri Gribenko       }
4215188c4b9SDmitri Gribenko 
4225188c4b9SDmitri Gribenko       case '\n':
4235188c4b9SDmitri Gribenko       case '\r':
4245188c4b9SDmitri Gribenko         TokenPtr = skipNewline(TokenPtr, CommentEnd);
4255188c4b9SDmitri Gribenko         formTokenWithChars(T, TokenPtr, tok::newline);
4265188c4b9SDmitri Gribenko 
4275188c4b9SDmitri Gribenko         if (CommentState == LCS_InsideCComment)
4285188c4b9SDmitri Gribenko           skipLineStartingDecorations();
4295188c4b9SDmitri Gribenko         return;
4305188c4b9SDmitri Gribenko 
4315188c4b9SDmitri Gribenko       default: {
4325188c4b9SDmitri Gribenko         while (true) {
4335188c4b9SDmitri Gribenko           TokenPtr++;
4345188c4b9SDmitri Gribenko           if (TokenPtr == CommentEnd)
4355188c4b9SDmitri Gribenko             break;
43617709ae8SDmitri Gribenko           const char C = *TokenPtr;
4375188c4b9SDmitri Gribenko           if(C == '\n' || C == '\r' ||
4384586df76SDmitri Gribenko              C == '\\' || C == '@' || C == '&' || C == '<')
4395188c4b9SDmitri Gribenko             break;
4405188c4b9SDmitri Gribenko         }
4414586df76SDmitri Gribenko         formTextToken(T, TokenPtr);
4425188c4b9SDmitri Gribenko         return;
4435188c4b9SDmitri Gribenko       }
4445188c4b9SDmitri Gribenko     }
4455188c4b9SDmitri Gribenko   }
4465188c4b9SDmitri Gribenko }
4475188c4b9SDmitri Gribenko 
4485188c4b9SDmitri Gribenko void Lexer::setupAndLexVerbatimBlock(Token &T,
4495188c4b9SDmitri Gribenko                                      const char *TextBegin,
4505188c4b9SDmitri Gribenko                                      char Marker, StringRef EndName) {
4515188c4b9SDmitri Gribenko   VerbatimBlockEndCommandName.clear();
4525188c4b9SDmitri Gribenko   VerbatimBlockEndCommandName.append(Marker == '\\' ? "\\" : "@");
4535188c4b9SDmitri Gribenko   VerbatimBlockEndCommandName.append(EndName);
4545188c4b9SDmitri Gribenko 
45560ddd8a1SDmitri Gribenko   StringRef Name(BufferPtr + 1, TextBegin - (BufferPtr + 1));
4565188c4b9SDmitri Gribenko   formTokenWithChars(T, TextBegin, tok::verbatim_block_begin);
45760ddd8a1SDmitri Gribenko   T.setVerbatimBlockName(Name);
4585188c4b9SDmitri Gribenko 
459ec92531cSDmitri Gribenko   // If there is a newline following the verbatim opening command, skip the
460ec92531cSDmitri Gribenko   // newline so that we don't create an tok::verbatim_block_line with empty
461ec92531cSDmitri Gribenko   // text content.
462ec92531cSDmitri Gribenko   if (BufferPtr != CommentEnd) {
463ec92531cSDmitri Gribenko     const char C = *BufferPtr;
464ec92531cSDmitri Gribenko     if (C == '\n' || C == '\r') {
465ec92531cSDmitri Gribenko       BufferPtr = skipNewline(BufferPtr, CommentEnd);
466ec92531cSDmitri Gribenko       State = LS_VerbatimBlockBody;
467ec92531cSDmitri Gribenko       return;
468ec92531cSDmitri Gribenko     }
469ec92531cSDmitri Gribenko   }
470ec92531cSDmitri Gribenko 
4715188c4b9SDmitri Gribenko   State = LS_VerbatimBlockFirstLine;
4725188c4b9SDmitri Gribenko }
4735188c4b9SDmitri Gribenko 
4745188c4b9SDmitri Gribenko void Lexer::lexVerbatimBlockFirstLine(Token &T) {
475e4a3997dSDmitri Gribenko again:
4765188c4b9SDmitri Gribenko   assert(BufferPtr < CommentEnd);
4775188c4b9SDmitri Gribenko 
4785188c4b9SDmitri Gribenko   // FIXME: It would be better to scan the text once, finding either the block
4795188c4b9SDmitri Gribenko   // end command or newline.
4805188c4b9SDmitri Gribenko   //
4815188c4b9SDmitri Gribenko   // Extract current line.
4825188c4b9SDmitri Gribenko   const char *Newline = findNewline(BufferPtr, CommentEnd);
4835188c4b9SDmitri Gribenko   StringRef Line(BufferPtr, Newline - BufferPtr);
4845188c4b9SDmitri Gribenko 
4855188c4b9SDmitri Gribenko   // Look for end command in current line.
4865188c4b9SDmitri Gribenko   size_t Pos = Line.find(VerbatimBlockEndCommandName);
487ec92531cSDmitri Gribenko   const char *TextEnd;
4885188c4b9SDmitri Gribenko   const char *NextLine;
4895188c4b9SDmitri Gribenko   if (Pos == StringRef::npos) {
4905188c4b9SDmitri Gribenko     // Current line is completely verbatim.
491ec92531cSDmitri Gribenko     TextEnd = Newline;
4925188c4b9SDmitri Gribenko     NextLine = skipNewline(Newline, CommentEnd);
4935188c4b9SDmitri Gribenko   } else if (Pos == 0) {
4945188c4b9SDmitri Gribenko     // Current line contains just an end command.
4955188c4b9SDmitri Gribenko     const char *End = BufferPtr + VerbatimBlockEndCommandName.size();
49660ddd8a1SDmitri Gribenko     StringRef Name(BufferPtr + 1, End - (BufferPtr + 1));
4975188c4b9SDmitri Gribenko     formTokenWithChars(T, End, tok::verbatim_block_end);
49860ddd8a1SDmitri Gribenko     T.setVerbatimBlockName(Name);
4995188c4b9SDmitri Gribenko     State = LS_Normal;
5005188c4b9SDmitri Gribenko     return;
5015188c4b9SDmitri Gribenko   } else {
5025188c4b9SDmitri Gribenko     // There is some text, followed by end command.  Extract text first.
503ec92531cSDmitri Gribenko     TextEnd = BufferPtr + Pos;
504ec92531cSDmitri Gribenko     NextLine = TextEnd;
505e4a3997dSDmitri Gribenko     // If there is only whitespace before end command, skip whitespace.
506e4a3997dSDmitri Gribenko     if (isWhitespace(BufferPtr, TextEnd)) {
507e4a3997dSDmitri Gribenko       BufferPtr = TextEnd;
508e4a3997dSDmitri Gribenko       goto again;
509e4a3997dSDmitri Gribenko     }
5105188c4b9SDmitri Gribenko   }
5115188c4b9SDmitri Gribenko 
512ec92531cSDmitri Gribenko   StringRef Text(BufferPtr, TextEnd - BufferPtr);
5135188c4b9SDmitri Gribenko   formTokenWithChars(T, NextLine, tok::verbatim_block_line);
51460ddd8a1SDmitri Gribenko   T.setVerbatimBlockText(Text);
5155188c4b9SDmitri Gribenko 
5165188c4b9SDmitri Gribenko   State = LS_VerbatimBlockBody;
5175188c4b9SDmitri Gribenko }
5185188c4b9SDmitri Gribenko 
5195188c4b9SDmitri Gribenko void Lexer::lexVerbatimBlockBody(Token &T) {
5205188c4b9SDmitri Gribenko   assert(State == LS_VerbatimBlockBody);
5215188c4b9SDmitri Gribenko 
5225188c4b9SDmitri Gribenko   if (CommentState == LCS_InsideCComment)
5235188c4b9SDmitri Gribenko     skipLineStartingDecorations();
5245188c4b9SDmitri Gribenko 
5255188c4b9SDmitri Gribenko   lexVerbatimBlockFirstLine(T);
5265188c4b9SDmitri Gribenko }
5275188c4b9SDmitri Gribenko 
5281669f702SDmitri Gribenko void Lexer::setupAndLexVerbatimLine(Token &T, const char *TextBegin) {
5291669f702SDmitri Gribenko   const StringRef Name(BufferPtr + 1, TextBegin - BufferPtr - 1);
5301669f702SDmitri Gribenko   formTokenWithChars(T, TextBegin, tok::verbatim_line_name);
5311669f702SDmitri Gribenko   T.setVerbatimLineName(Name);
5321669f702SDmitri Gribenko 
5331669f702SDmitri Gribenko   State = LS_VerbatimLineText;
5341669f702SDmitri Gribenko }
5351669f702SDmitri Gribenko 
5361669f702SDmitri Gribenko void Lexer::lexVerbatimLineText(Token &T) {
5371669f702SDmitri Gribenko   assert(State == LS_VerbatimLineText);
5381669f702SDmitri Gribenko 
5395188c4b9SDmitri Gribenko   // Extract current line.
5405188c4b9SDmitri Gribenko   const char *Newline = findNewline(BufferPtr, CommentEnd);
5411669f702SDmitri Gribenko   const StringRef Text(BufferPtr, Newline - BufferPtr);
5421669f702SDmitri Gribenko   formTokenWithChars(T, Newline, tok::verbatim_line_text);
5435188c4b9SDmitri Gribenko   T.setVerbatimLineText(Text);
5441669f702SDmitri Gribenko 
5451669f702SDmitri Gribenko   State = LS_Normal;
5465188c4b9SDmitri Gribenko }
5475188c4b9SDmitri Gribenko 
5484586df76SDmitri Gribenko void Lexer::lexHTMLCharacterReference(Token &T) {
5494586df76SDmitri Gribenko   const char *TokenPtr = BufferPtr;
5504586df76SDmitri Gribenko   assert(*TokenPtr == '&');
5514586df76SDmitri Gribenko   TokenPtr++;
5524586df76SDmitri Gribenko   if (TokenPtr == CommentEnd) {
5534586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
5544586df76SDmitri Gribenko     return;
5554586df76SDmitri Gribenko   }
5564586df76SDmitri Gribenko   const char *NamePtr;
5574586df76SDmitri Gribenko   bool isNamed = false;
5584586df76SDmitri Gribenko   bool isDecimal = false;
5594586df76SDmitri Gribenko   char C = *TokenPtr;
5604586df76SDmitri Gribenko   if (isHTMLNamedCharacterReferenceCharacter(C)) {
5614586df76SDmitri Gribenko     NamePtr = TokenPtr;
5624586df76SDmitri Gribenko     TokenPtr = skipNamedCharacterReference(TokenPtr, CommentEnd);
5634586df76SDmitri Gribenko     isNamed = true;
5644586df76SDmitri Gribenko   } else if (C == '#') {
5654586df76SDmitri Gribenko     TokenPtr++;
5664586df76SDmitri Gribenko     if (TokenPtr == CommentEnd) {
5674586df76SDmitri Gribenko       formTextToken(T, TokenPtr);
5684586df76SDmitri Gribenko       return;
5694586df76SDmitri Gribenko     }
5704586df76SDmitri Gribenko     C = *TokenPtr;
5714586df76SDmitri Gribenko     if (isHTMLDecimalCharacterReferenceCharacter(C)) {
5724586df76SDmitri Gribenko       NamePtr = TokenPtr;
5734586df76SDmitri Gribenko       TokenPtr = skipDecimalCharacterReference(TokenPtr, CommentEnd);
5744586df76SDmitri Gribenko       isDecimal = true;
5754586df76SDmitri Gribenko     } else if (C == 'x' || C == 'X') {
5764586df76SDmitri Gribenko       TokenPtr++;
5774586df76SDmitri Gribenko       NamePtr = TokenPtr;
5784586df76SDmitri Gribenko       TokenPtr = skipHexCharacterReference(TokenPtr, CommentEnd);
5794586df76SDmitri Gribenko     } else {
5804586df76SDmitri Gribenko       formTextToken(T, TokenPtr);
5814586df76SDmitri Gribenko       return;
5824586df76SDmitri Gribenko     }
5834586df76SDmitri Gribenko   } else {
5844586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
5854586df76SDmitri Gribenko     return;
5864586df76SDmitri Gribenko   }
5874586df76SDmitri Gribenko   if (NamePtr == TokenPtr || TokenPtr == CommentEnd ||
5884586df76SDmitri Gribenko       *TokenPtr != ';') {
5894586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
5904586df76SDmitri Gribenko     return;
5914586df76SDmitri Gribenko   }
5924586df76SDmitri Gribenko   StringRef Name(NamePtr, TokenPtr - NamePtr);
5934586df76SDmitri Gribenko   TokenPtr++; // Skip semicolon.
5944586df76SDmitri Gribenko   StringRef Resolved;
5954586df76SDmitri Gribenko   if (isNamed)
5964586df76SDmitri Gribenko     Resolved = resolveHTMLNamedCharacterReference(Name);
5974586df76SDmitri Gribenko   else if (isDecimal)
5984586df76SDmitri Gribenko     Resolved = resolveHTMLDecimalCharacterReference(Name);
5994586df76SDmitri Gribenko   else
6004586df76SDmitri Gribenko     Resolved = resolveHTMLHexCharacterReference(Name);
6014586df76SDmitri Gribenko 
6024586df76SDmitri Gribenko   if (Resolved.empty()) {
6034586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
6044586df76SDmitri Gribenko     return;
6054586df76SDmitri Gribenko   }
6064586df76SDmitri Gribenko   formTokenWithChars(T, TokenPtr, tok::text);
6074586df76SDmitri Gribenko   T.setText(Resolved);
6084586df76SDmitri Gribenko   return;
6094586df76SDmitri Gribenko }
6104586df76SDmitri Gribenko 
611e00ffc7bSDmitri Gribenko void Lexer::setupAndLexHTMLStartTag(Token &T) {
61217709ae8SDmitri Gribenko   assert(BufferPtr[0] == '<' &&
61317709ae8SDmitri Gribenko          isHTMLIdentifierStartingCharacter(BufferPtr[1]));
6145188c4b9SDmitri Gribenko   const char *TagNameEnd = skipHTMLIdentifier(BufferPtr + 2, CommentEnd);
61560ddd8a1SDmitri Gribenko   StringRef Name(BufferPtr + 1, TagNameEnd - (BufferPtr + 1));
616*107618a6SDmitri Gribenko   if (!isHTMLTagName(Name)) {
617*107618a6SDmitri Gribenko     formTextToken(T, TagNameEnd);
618*107618a6SDmitri Gribenko     return;
619*107618a6SDmitri Gribenko   }
620*107618a6SDmitri Gribenko 
621e00ffc7bSDmitri Gribenko   formTokenWithChars(T, TagNameEnd, tok::html_start_tag);
622e00ffc7bSDmitri Gribenko   T.setHTMLTagStartName(Name);
6235188c4b9SDmitri Gribenko 
6245188c4b9SDmitri Gribenko   BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
6255188c4b9SDmitri Gribenko 
62617709ae8SDmitri Gribenko   const char C = *BufferPtr;
62717709ae8SDmitri Gribenko   if (BufferPtr != CommentEnd &&
628f26054f0SDmitri Gribenko       (C == '>' || C == '/' || isHTMLIdentifierStartingCharacter(C)))
629e00ffc7bSDmitri Gribenko     State = LS_HTMLStartTag;
6305188c4b9SDmitri Gribenko }
6315188c4b9SDmitri Gribenko 
632e00ffc7bSDmitri Gribenko void Lexer::lexHTMLStartTag(Token &T) {
633e00ffc7bSDmitri Gribenko   assert(State == LS_HTMLStartTag);
6345188c4b9SDmitri Gribenko 
6355188c4b9SDmitri Gribenko   const char *TokenPtr = BufferPtr;
6365188c4b9SDmitri Gribenko   char C = *TokenPtr;
6375188c4b9SDmitri Gribenko   if (isHTMLIdentifierCharacter(C)) {
6385188c4b9SDmitri Gribenko     TokenPtr = skipHTMLIdentifier(TokenPtr, CommentEnd);
63960ddd8a1SDmitri Gribenko     StringRef Ident(BufferPtr, TokenPtr - BufferPtr);
6405188c4b9SDmitri Gribenko     formTokenWithChars(T, TokenPtr, tok::html_ident);
64160ddd8a1SDmitri Gribenko     T.setHTMLIdent(Ident);
6425188c4b9SDmitri Gribenko   } else {
6435188c4b9SDmitri Gribenko     switch (C) {
6445188c4b9SDmitri Gribenko     case '=':
6455188c4b9SDmitri Gribenko       TokenPtr++;
6465188c4b9SDmitri Gribenko       formTokenWithChars(T, TokenPtr, tok::html_equals);
6475188c4b9SDmitri Gribenko       break;
6485188c4b9SDmitri Gribenko     case '\"':
6495188c4b9SDmitri Gribenko     case '\'': {
6505188c4b9SDmitri Gribenko       const char *OpenQuote = TokenPtr;
6515188c4b9SDmitri Gribenko       TokenPtr = skipHTMLQuotedString(TokenPtr, CommentEnd);
6525188c4b9SDmitri Gribenko       const char *ClosingQuote = TokenPtr;
6535188c4b9SDmitri Gribenko       if (TokenPtr != CommentEnd) // Skip closing quote.
6545188c4b9SDmitri Gribenko         TokenPtr++;
6555188c4b9SDmitri Gribenko       formTokenWithChars(T, TokenPtr, tok::html_quoted_string);
6565188c4b9SDmitri Gribenko       T.setHTMLQuotedString(StringRef(OpenQuote + 1,
6575188c4b9SDmitri Gribenko                                       ClosingQuote - (OpenQuote + 1)));
6585188c4b9SDmitri Gribenko       break;
6595188c4b9SDmitri Gribenko     }
6605188c4b9SDmitri Gribenko     case '>':
6615188c4b9SDmitri Gribenko       TokenPtr++;
6625188c4b9SDmitri Gribenko       formTokenWithChars(T, TokenPtr, tok::html_greater);
66317709ae8SDmitri Gribenko       State = LS_Normal;
66417709ae8SDmitri Gribenko       return;
665f26054f0SDmitri Gribenko     case '/':
666f26054f0SDmitri Gribenko       TokenPtr++;
667f26054f0SDmitri Gribenko       if (TokenPtr != CommentEnd && *TokenPtr == '>') {
668f26054f0SDmitri Gribenko         TokenPtr++;
669f26054f0SDmitri Gribenko         formTokenWithChars(T, TokenPtr, tok::html_slash_greater);
6704586df76SDmitri Gribenko       } else
6714586df76SDmitri Gribenko         formTextToken(T, TokenPtr);
6724586df76SDmitri Gribenko 
673f26054f0SDmitri Gribenko       State = LS_Normal;
674f26054f0SDmitri Gribenko       return;
6755188c4b9SDmitri Gribenko     }
6765188c4b9SDmitri Gribenko   }
6775188c4b9SDmitri Gribenko 
6785188c4b9SDmitri Gribenko   // Now look ahead and return to normal state if we don't see any HTML tokens
6795188c4b9SDmitri Gribenko   // ahead.
6805188c4b9SDmitri Gribenko   BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
6815188c4b9SDmitri Gribenko   if (BufferPtr == CommentEnd) {
6825188c4b9SDmitri Gribenko     State = LS_Normal;
6835188c4b9SDmitri Gribenko     return;
6845188c4b9SDmitri Gribenko   }
6855188c4b9SDmitri Gribenko 
6865188c4b9SDmitri Gribenko   C = *BufferPtr;
68717709ae8SDmitri Gribenko   if (!isHTMLIdentifierStartingCharacter(C) &&
6885188c4b9SDmitri Gribenko       C != '=' && C != '\"' && C != '\'' && C != '>') {
6895188c4b9SDmitri Gribenko     State = LS_Normal;
6905188c4b9SDmitri Gribenko     return;
6915188c4b9SDmitri Gribenko   }
6925188c4b9SDmitri Gribenko }
6935188c4b9SDmitri Gribenko 
694e00ffc7bSDmitri Gribenko void Lexer::setupAndLexHTMLEndTag(Token &T) {
6955188c4b9SDmitri Gribenko   assert(BufferPtr[0] == '<' && BufferPtr[1] == '/');
6965188c4b9SDmitri Gribenko 
6975188c4b9SDmitri Gribenko   const char *TagNameBegin = skipWhitespace(BufferPtr + 2, CommentEnd);
6985188c4b9SDmitri Gribenko   const char *TagNameEnd = skipHTMLIdentifier(TagNameBegin, CommentEnd);
699*107618a6SDmitri Gribenko   StringRef Name(TagNameBegin, TagNameEnd - TagNameBegin);
700*107618a6SDmitri Gribenko   if (!isHTMLTagName(Name)) {
701*107618a6SDmitri Gribenko     formTextToken(T, TagNameEnd);
702*107618a6SDmitri Gribenko     return;
703*107618a6SDmitri Gribenko   }
7045188c4b9SDmitri Gribenko 
7055188c4b9SDmitri Gribenko   const char *End = skipWhitespace(TagNameEnd, CommentEnd);
7065188c4b9SDmitri Gribenko 
707e00ffc7bSDmitri Gribenko   formTokenWithChars(T, End, tok::html_end_tag);
708*107618a6SDmitri Gribenko   T.setHTMLTagEndName(Name);
709ec92531cSDmitri Gribenko 
710ec92531cSDmitri Gribenko   if (BufferPtr != CommentEnd && *BufferPtr == '>')
711e00ffc7bSDmitri Gribenko     State = LS_HTMLEndTag;
712ec92531cSDmitri Gribenko }
713ec92531cSDmitri Gribenko 
714e00ffc7bSDmitri Gribenko void Lexer::lexHTMLEndTag(Token &T) {
715ec92531cSDmitri Gribenko   assert(BufferPtr != CommentEnd && *BufferPtr == '>');
716ec92531cSDmitri Gribenko 
717ec92531cSDmitri Gribenko   formTokenWithChars(T, BufferPtr + 1, tok::html_greater);
718ec92531cSDmitri Gribenko   State = LS_Normal;
7195188c4b9SDmitri Gribenko }
7205188c4b9SDmitri Gribenko 
721ca7f80adSDmitri Gribenko Lexer::Lexer(llvm::BumpPtrAllocator &Allocator, const CommandTraits &Traits,
7224586df76SDmitri Gribenko              SourceLocation FileLoc, const CommentOptions &CommOpts,
7235188c4b9SDmitri Gribenko              const char *BufferStart, const char *BufferEnd):
724ca7f80adSDmitri Gribenko     Allocator(Allocator), Traits(Traits),
7255188c4b9SDmitri Gribenko     BufferStart(BufferStart), BufferEnd(BufferEnd),
7265188c4b9SDmitri Gribenko     FileLoc(FileLoc), CommOpts(CommOpts), BufferPtr(BufferStart),
7275188c4b9SDmitri Gribenko     CommentState(LCS_BeforeComment), State(LS_Normal) {
7285188c4b9SDmitri Gribenko }
7295188c4b9SDmitri Gribenko 
7305188c4b9SDmitri Gribenko void Lexer::lex(Token &T) {
7315188c4b9SDmitri Gribenko again:
7325188c4b9SDmitri Gribenko   switch (CommentState) {
7335188c4b9SDmitri Gribenko   case LCS_BeforeComment:
7345188c4b9SDmitri Gribenko     if (BufferPtr == BufferEnd) {
7355188c4b9SDmitri Gribenko       formTokenWithChars(T, BufferPtr, tok::eof);
7365188c4b9SDmitri Gribenko       return;
7375188c4b9SDmitri Gribenko     }
7385188c4b9SDmitri Gribenko 
7395188c4b9SDmitri Gribenko     assert(*BufferPtr == '/');
7405188c4b9SDmitri Gribenko     BufferPtr++; // Skip first slash.
7415188c4b9SDmitri Gribenko     switch(*BufferPtr) {
7425188c4b9SDmitri Gribenko     case '/': { // BCPL comment.
7435188c4b9SDmitri Gribenko       BufferPtr++; // Skip second slash.
7445188c4b9SDmitri Gribenko 
7455188c4b9SDmitri Gribenko       if (BufferPtr != BufferEnd) {
7465188c4b9SDmitri Gribenko         // Skip Doxygen magic marker, if it is present.
7475188c4b9SDmitri Gribenko         // It might be missing because of a typo //< or /*<, or because we
7485188c4b9SDmitri Gribenko         // merged this non-Doxygen comment into a bunch of Doxygen comments
7495188c4b9SDmitri Gribenko         // around it: /** ... */ /* ... */ /** ... */
7505188c4b9SDmitri Gribenko         const char C = *BufferPtr;
7515188c4b9SDmitri Gribenko         if (C == '/' || C == '!')
7525188c4b9SDmitri Gribenko           BufferPtr++;
7535188c4b9SDmitri Gribenko       }
7545188c4b9SDmitri Gribenko 
7555188c4b9SDmitri Gribenko       // Skip less-than symbol that marks trailing comments.
7565188c4b9SDmitri Gribenko       // Skip it even if the comment is not a Doxygen one, because //< and /*<
7575188c4b9SDmitri Gribenko       // are frequent typos.
7585188c4b9SDmitri Gribenko       if (BufferPtr != BufferEnd && *BufferPtr == '<')
7595188c4b9SDmitri Gribenko         BufferPtr++;
7605188c4b9SDmitri Gribenko 
7615188c4b9SDmitri Gribenko       CommentState = LCS_InsideBCPLComment;
762ec92531cSDmitri Gribenko       if (State != LS_VerbatimBlockBody && State != LS_VerbatimBlockFirstLine)
7635188c4b9SDmitri Gribenko         State = LS_Normal;
7645188c4b9SDmitri Gribenko       CommentEnd = findBCPLCommentEnd(BufferPtr, BufferEnd);
7655188c4b9SDmitri Gribenko       goto again;
7665188c4b9SDmitri Gribenko     }
7675188c4b9SDmitri Gribenko     case '*': { // C comment.
7685188c4b9SDmitri Gribenko       BufferPtr++; // Skip star.
7695188c4b9SDmitri Gribenko 
7705188c4b9SDmitri Gribenko       // Skip Doxygen magic marker.
7715188c4b9SDmitri Gribenko       const char C = *BufferPtr;
7725188c4b9SDmitri Gribenko       if ((C == '*' && *(BufferPtr + 1) != '/') || C == '!')
7735188c4b9SDmitri Gribenko         BufferPtr++;
7745188c4b9SDmitri Gribenko 
7755188c4b9SDmitri Gribenko       // Skip less-than symbol that marks trailing comments.
7765188c4b9SDmitri Gribenko       if (BufferPtr != BufferEnd && *BufferPtr == '<')
7775188c4b9SDmitri Gribenko         BufferPtr++;
7785188c4b9SDmitri Gribenko 
7795188c4b9SDmitri Gribenko       CommentState = LCS_InsideCComment;
7805188c4b9SDmitri Gribenko       State = LS_Normal;
7815188c4b9SDmitri Gribenko       CommentEnd = findCCommentEnd(BufferPtr, BufferEnd);
7825188c4b9SDmitri Gribenko       goto again;
7835188c4b9SDmitri Gribenko     }
7845188c4b9SDmitri Gribenko     default:
7855188c4b9SDmitri Gribenko       llvm_unreachable("second character of comment should be '/' or '*'");
7865188c4b9SDmitri Gribenko     }
7875188c4b9SDmitri Gribenko 
7885188c4b9SDmitri Gribenko   case LCS_BetweenComments: {
7895188c4b9SDmitri Gribenko     // Consecutive comments are extracted only if there is only whitespace
7905188c4b9SDmitri Gribenko     // between them.  So we can search for the start of the next comment.
7915188c4b9SDmitri Gribenko     const char *EndWhitespace = BufferPtr;
7925188c4b9SDmitri Gribenko     while(EndWhitespace != BufferEnd && *EndWhitespace != '/')
7935188c4b9SDmitri Gribenko       EndWhitespace++;
7945188c4b9SDmitri Gribenko 
7955188c4b9SDmitri Gribenko     // Turn any whitespace between comments (and there is only whitespace
79617709ae8SDmitri Gribenko     // between them -- guaranteed by comment extraction) into a newline.  We
79717709ae8SDmitri Gribenko     // have two newlines between C comments in total (first one was synthesized
79817709ae8SDmitri Gribenko     // after a comment).
7995188c4b9SDmitri Gribenko     formTokenWithChars(T, EndWhitespace, tok::newline);
8005188c4b9SDmitri Gribenko 
8015188c4b9SDmitri Gribenko     CommentState = LCS_BeforeComment;
8025188c4b9SDmitri Gribenko     break;
8035188c4b9SDmitri Gribenko   }
8045188c4b9SDmitri Gribenko 
8055188c4b9SDmitri Gribenko   case LCS_InsideBCPLComment:
8065188c4b9SDmitri Gribenko   case LCS_InsideCComment:
8075188c4b9SDmitri Gribenko     if (BufferPtr != CommentEnd) {
8085188c4b9SDmitri Gribenko       lexCommentText(T);
8095188c4b9SDmitri Gribenko       break;
8105188c4b9SDmitri Gribenko     } else {
8115188c4b9SDmitri Gribenko       // Skip C comment closing sequence.
8125188c4b9SDmitri Gribenko       if (CommentState == LCS_InsideCComment) {
8135188c4b9SDmitri Gribenko         assert(BufferPtr[0] == '*' && BufferPtr[1] == '/');
8145188c4b9SDmitri Gribenko         BufferPtr += 2;
8155188c4b9SDmitri Gribenko         assert(BufferPtr <= BufferEnd);
8165188c4b9SDmitri Gribenko 
8175188c4b9SDmitri Gribenko         // Synthenize newline just after the C comment, regardless if there is
8185188c4b9SDmitri Gribenko         // actually a newline.
8195188c4b9SDmitri Gribenko         formTokenWithChars(T, BufferPtr, tok::newline);
8205188c4b9SDmitri Gribenko 
8215188c4b9SDmitri Gribenko         CommentState = LCS_BetweenComments;
8225188c4b9SDmitri Gribenko         break;
8235188c4b9SDmitri Gribenko       } else {
8245188c4b9SDmitri Gribenko         // Don't synthesized a newline after BCPL comment.
8255188c4b9SDmitri Gribenko         CommentState = LCS_BetweenComments;
8265188c4b9SDmitri Gribenko         goto again;
8275188c4b9SDmitri Gribenko       }
8285188c4b9SDmitri Gribenko     }
8295188c4b9SDmitri Gribenko   }
8305188c4b9SDmitri Gribenko }
8315188c4b9SDmitri Gribenko 
8325188c4b9SDmitri Gribenko StringRef Lexer::getSpelling(const Token &Tok,
8335188c4b9SDmitri Gribenko                              const SourceManager &SourceMgr,
8345188c4b9SDmitri Gribenko                              bool *Invalid) const {
8355188c4b9SDmitri Gribenko   SourceLocation Loc = Tok.getLocation();
8365188c4b9SDmitri Gribenko   std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedLoc(Loc);
8375188c4b9SDmitri Gribenko 
8385188c4b9SDmitri Gribenko   bool InvalidTemp = false;
8395188c4b9SDmitri Gribenko   StringRef File = SourceMgr.getBufferData(LocInfo.first, &InvalidTemp);
8405188c4b9SDmitri Gribenko   if (InvalidTemp) {
8415188c4b9SDmitri Gribenko     *Invalid = true;
8425188c4b9SDmitri Gribenko     return StringRef();
8435188c4b9SDmitri Gribenko   }
8445188c4b9SDmitri Gribenko 
8455188c4b9SDmitri Gribenko   const char *Begin = File.data() + LocInfo.second;
8465188c4b9SDmitri Gribenko   return StringRef(Begin, Tok.getLength());
8475188c4b9SDmitri Gribenko }
8485188c4b9SDmitri Gribenko 
8495188c4b9SDmitri Gribenko } // end namespace comments
8505188c4b9SDmitri Gribenko } // end namespace clang
8515188c4b9SDmitri Gribenko 
852