15188c4b9SDmitri Gribenko #include "clang/AST/CommentLexer.h"
2*4586df76SDmitri Gribenko #include "clang/Basic/ConvertUTF.h"
35188c4b9SDmitri Gribenko #include "llvm/ADT/StringSwitch.h"
45188c4b9SDmitri Gribenko #include "llvm/Support/ErrorHandling.h"
55188c4b9SDmitri Gribenko 
65188c4b9SDmitri Gribenko namespace clang {
75188c4b9SDmitri Gribenko namespace comments {
85188c4b9SDmitri Gribenko 
95188c4b9SDmitri Gribenko void Token::dump(const Lexer &L, const SourceManager &SM) const {
105188c4b9SDmitri Gribenko   llvm::errs() << "comments::Token Kind=" << Kind << " ";
115188c4b9SDmitri Gribenko   Loc.dump(SM);
125188c4b9SDmitri Gribenko   llvm::errs() << " " << Length << " \"" << L.getSpelling(*this, SM) << "\"\n";
135188c4b9SDmitri Gribenko }
145188c4b9SDmitri Gribenko 
155188c4b9SDmitri Gribenko bool Lexer::isVerbatimBlockCommand(StringRef BeginName,
165188c4b9SDmitri Gribenko                                   StringRef &EndName) const {
175188c4b9SDmitri Gribenko   const char *Result = llvm::StringSwitch<const char *>(BeginName)
185188c4b9SDmitri Gribenko     .Case("code", "endcode")
195188c4b9SDmitri Gribenko     .Case("verbatim", "endverbatim")
205188c4b9SDmitri Gribenko     .Case("htmlonly", "endhtmlonly")
215188c4b9SDmitri Gribenko     .Case("latexonly", "endlatexonly")
225188c4b9SDmitri Gribenko     .Case("xmlonly", "endxmlonly")
235188c4b9SDmitri Gribenko     .Case("manonly", "endmanonly")
245188c4b9SDmitri Gribenko     .Case("rtfonly", "endrtfonly")
255188c4b9SDmitri Gribenko 
265188c4b9SDmitri Gribenko     .Case("dot", "enddot")
275188c4b9SDmitri Gribenko     .Case("msc", "endmsc")
285188c4b9SDmitri Gribenko 
295188c4b9SDmitri Gribenko     .Case("f$", "f$") // Inline LaTeX formula
305188c4b9SDmitri Gribenko     .Case("f[", "f]") // Displayed LaTeX formula
315188c4b9SDmitri Gribenko     .Case("f{", "f}") // LaTeX environment
325188c4b9SDmitri Gribenko 
335188c4b9SDmitri Gribenko     .Default(NULL);
345188c4b9SDmitri Gribenko 
355188c4b9SDmitri Gribenko   if (Result) {
365188c4b9SDmitri Gribenko     EndName = Result;
375188c4b9SDmitri Gribenko     return true;
385188c4b9SDmitri Gribenko   }
395188c4b9SDmitri Gribenko 
405188c4b9SDmitri Gribenko   for (VerbatimBlockCommandVector::const_iterator
415188c4b9SDmitri Gribenko            I = VerbatimBlockCommands.begin(),
425188c4b9SDmitri Gribenko            E = VerbatimBlockCommands.end();
435188c4b9SDmitri Gribenko        I != E; ++I)
445188c4b9SDmitri Gribenko     if (I->BeginName == BeginName) {
455188c4b9SDmitri Gribenko       EndName = I->EndName;
465188c4b9SDmitri Gribenko       return true;
475188c4b9SDmitri Gribenko     }
485188c4b9SDmitri Gribenko 
495188c4b9SDmitri Gribenko   return false;
505188c4b9SDmitri Gribenko }
515188c4b9SDmitri Gribenko 
525188c4b9SDmitri Gribenko bool Lexer::isVerbatimLineCommand(StringRef Name) const {
535188c4b9SDmitri Gribenko   bool Result = llvm::StringSwitch<bool>(Name)
545188c4b9SDmitri Gribenko   .Case("fn", true)
555188c4b9SDmitri Gribenko   .Case("var", true)
565188c4b9SDmitri Gribenko   .Case("property", true)
575188c4b9SDmitri Gribenko   .Case("typedef", true)
585188c4b9SDmitri Gribenko 
595188c4b9SDmitri Gribenko   .Case("overload", true)
605188c4b9SDmitri Gribenko 
615188c4b9SDmitri Gribenko   .Case("defgroup", true)
625188c4b9SDmitri Gribenko   .Case("ingroup", true)
635188c4b9SDmitri Gribenko   .Case("addtogroup", true)
645188c4b9SDmitri Gribenko   .Case("weakgroup", true)
655188c4b9SDmitri Gribenko   .Case("name", true)
665188c4b9SDmitri Gribenko 
675188c4b9SDmitri Gribenko   .Case("section", true)
685188c4b9SDmitri Gribenko   .Case("subsection", true)
695188c4b9SDmitri Gribenko   .Case("subsubsection", true)
705188c4b9SDmitri Gribenko   .Case("paragraph", true)
715188c4b9SDmitri Gribenko 
725188c4b9SDmitri Gribenko   .Case("mainpage", true)
735188c4b9SDmitri Gribenko   .Case("subpage", true)
745188c4b9SDmitri Gribenko   .Case("ref", true)
755188c4b9SDmitri Gribenko 
765188c4b9SDmitri Gribenko   .Default(false);
775188c4b9SDmitri Gribenko 
785188c4b9SDmitri Gribenko   if (Result)
795188c4b9SDmitri Gribenko     return true;
805188c4b9SDmitri Gribenko 
815188c4b9SDmitri Gribenko   for (VerbatimLineCommandVector::const_iterator
825188c4b9SDmitri Gribenko            I = VerbatimLineCommands.begin(),
835188c4b9SDmitri Gribenko            E = VerbatimLineCommands.end();
845188c4b9SDmitri Gribenko        I != E; ++I)
855188c4b9SDmitri Gribenko     if (I->Name == Name)
865188c4b9SDmitri Gribenko       return true;
875188c4b9SDmitri Gribenko 
885188c4b9SDmitri Gribenko   return false;
895188c4b9SDmitri Gribenko }
905188c4b9SDmitri Gribenko 
91*4586df76SDmitri Gribenko namespace {
92*4586df76SDmitri Gribenko bool isHTMLNamedCharacterReferenceCharacter(char C) {
93*4586df76SDmitri Gribenko   return (C >= 'a' && C <= 'z') ||
94*4586df76SDmitri Gribenko          (C >= 'A' && C <= 'Z');
95*4586df76SDmitri Gribenko }
96*4586df76SDmitri Gribenko 
97*4586df76SDmitri Gribenko bool isHTMLDecimalCharacterReferenceCharacter(char C) {
98*4586df76SDmitri Gribenko   return C >= '0' && C <= '9';
99*4586df76SDmitri Gribenko }
100*4586df76SDmitri Gribenko 
101*4586df76SDmitri Gribenko bool isHTMLHexCharacterReferenceCharacter(char C) {
102*4586df76SDmitri Gribenko   return (C >= '0' && C <= '9') ||
103*4586df76SDmitri Gribenko          (C >= 'a' && C <= 'f') ||
104*4586df76SDmitri Gribenko          (C >= 'A' && C <= 'F');
105*4586df76SDmitri Gribenko }
106*4586df76SDmitri Gribenko } // unnamed namespace
107*4586df76SDmitri Gribenko 
108*4586df76SDmitri Gribenko StringRef Lexer::resolveHTMLNamedCharacterReference(StringRef Name) const {
109*4586df76SDmitri Gribenko   return llvm::StringSwitch<StringRef>(Name)
110*4586df76SDmitri Gribenko       .Case("amp", "&")
111*4586df76SDmitri Gribenko       .Case("lt", "<")
112*4586df76SDmitri Gribenko       .Case("gt", ">")
113*4586df76SDmitri Gribenko       .Case("quot", "\"")
114*4586df76SDmitri Gribenko       .Case("apos", "\'")
115*4586df76SDmitri Gribenko       .Default("");
116*4586df76SDmitri Gribenko }
117*4586df76SDmitri Gribenko 
118*4586df76SDmitri Gribenko StringRef Lexer::resolveHTMLDecimalCharacterReference(StringRef Name) const {
119*4586df76SDmitri Gribenko   unsigned CodePoint = 0;
120*4586df76SDmitri Gribenko   for (unsigned i = 0, e = Name.size(); i != e; ++i) {
121*4586df76SDmitri Gribenko     assert(isHTMLDecimalCharacterReferenceCharacter(Name[i]));
122*4586df76SDmitri Gribenko     CodePoint *= 10;
123*4586df76SDmitri Gribenko     CodePoint += Name[i] - '0';
124*4586df76SDmitri Gribenko   }
125*4586df76SDmitri Gribenko 
126*4586df76SDmitri Gribenko   char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT);
127*4586df76SDmitri Gribenko   char *ResolvedPtr = Resolved;
128*4586df76SDmitri Gribenko   if (ConvertCodePointToUTF8(CodePoint, ResolvedPtr))
129*4586df76SDmitri Gribenko     return StringRef(Resolved, ResolvedPtr - Resolved);
130*4586df76SDmitri Gribenko   else
131*4586df76SDmitri Gribenko     return StringRef();
132*4586df76SDmitri Gribenko }
133*4586df76SDmitri Gribenko 
134*4586df76SDmitri Gribenko StringRef Lexer::resolveHTMLHexCharacterReference(StringRef Name) const {
135*4586df76SDmitri Gribenko   unsigned CodePoint = 0;
136*4586df76SDmitri Gribenko   for (unsigned i = 0, e = Name.size(); i != e; ++i) {
137*4586df76SDmitri Gribenko     CodePoint *= 16;
138*4586df76SDmitri Gribenko     const char C = Name[i];
139*4586df76SDmitri Gribenko     assert(isHTMLHexCharacterReferenceCharacter(C));
140*4586df76SDmitri Gribenko     if (C >= '0' && C <= '9')
141*4586df76SDmitri Gribenko       CodePoint += Name[i] - '0';
142*4586df76SDmitri Gribenko     else if (C >= 'a' && C <= 'f')
143*4586df76SDmitri Gribenko       CodePoint += Name[i] - 'a' + 10;
144*4586df76SDmitri Gribenko     else
145*4586df76SDmitri Gribenko       CodePoint += Name[i] - 'A' + 10;
146*4586df76SDmitri Gribenko   }
147*4586df76SDmitri Gribenko 
148*4586df76SDmitri Gribenko   char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT);
149*4586df76SDmitri Gribenko   char *ResolvedPtr = Resolved;
150*4586df76SDmitri Gribenko   if (ConvertCodePointToUTF8(CodePoint, ResolvedPtr))
151*4586df76SDmitri Gribenko     return StringRef(Resolved, ResolvedPtr - Resolved);
152*4586df76SDmitri Gribenko   else
153*4586df76SDmitri Gribenko     return StringRef();
154*4586df76SDmitri Gribenko }
155*4586df76SDmitri Gribenko 
1565188c4b9SDmitri Gribenko void Lexer::skipLineStartingDecorations() {
1575188c4b9SDmitri Gribenko   // This function should be called only for C comments
1585188c4b9SDmitri Gribenko   assert(CommentState == LCS_InsideCComment);
1595188c4b9SDmitri Gribenko 
1605188c4b9SDmitri Gribenko   if (BufferPtr == CommentEnd)
1615188c4b9SDmitri Gribenko     return;
1625188c4b9SDmitri Gribenko 
1635188c4b9SDmitri Gribenko   switch (*BufferPtr) {
1645188c4b9SDmitri Gribenko   case ' ':
1655188c4b9SDmitri Gribenko   case '\t':
1665188c4b9SDmitri Gribenko   case '\f':
1675188c4b9SDmitri Gribenko   case '\v': {
1685188c4b9SDmitri Gribenko     const char *NewBufferPtr = BufferPtr;
1695188c4b9SDmitri Gribenko     NewBufferPtr++;
1705188c4b9SDmitri Gribenko     if (NewBufferPtr == CommentEnd)
1715188c4b9SDmitri Gribenko       return;
1725188c4b9SDmitri Gribenko 
1735188c4b9SDmitri Gribenko     char C = *NewBufferPtr;
1745188c4b9SDmitri Gribenko     while (C == ' ' || C == '\t' || C == '\f' || C == '\v') {
1755188c4b9SDmitri Gribenko       NewBufferPtr++;
1765188c4b9SDmitri Gribenko       if (NewBufferPtr == CommentEnd)
1775188c4b9SDmitri Gribenko         return;
1785188c4b9SDmitri Gribenko       C = *NewBufferPtr;
1795188c4b9SDmitri Gribenko     }
1805188c4b9SDmitri Gribenko     if (C == '*')
1815188c4b9SDmitri Gribenko       BufferPtr = NewBufferPtr + 1;
1825188c4b9SDmitri Gribenko     break;
1835188c4b9SDmitri Gribenko   }
1845188c4b9SDmitri Gribenko   case '*':
1855188c4b9SDmitri Gribenko     BufferPtr++;
1865188c4b9SDmitri Gribenko     break;
1875188c4b9SDmitri Gribenko   }
1885188c4b9SDmitri Gribenko }
1895188c4b9SDmitri Gribenko 
1905188c4b9SDmitri Gribenko namespace {
191ec92531cSDmitri Gribenko /// Returns pointer to the first newline character in the string.
1925188c4b9SDmitri Gribenko const char *findNewline(const char *BufferPtr, const char *BufferEnd) {
1935188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
1945188c4b9SDmitri Gribenko     const char C = *BufferPtr;
1955188c4b9SDmitri Gribenko     if (C == '\n' || C == '\r')
1965188c4b9SDmitri Gribenko       return BufferPtr;
1975188c4b9SDmitri Gribenko   }
1985188c4b9SDmitri Gribenko   return BufferEnd;
1995188c4b9SDmitri Gribenko }
2005188c4b9SDmitri Gribenko 
2015188c4b9SDmitri Gribenko const char *skipNewline(const char *BufferPtr, const char *BufferEnd) {
2025188c4b9SDmitri Gribenko   if (BufferPtr == BufferEnd)
2035188c4b9SDmitri Gribenko     return BufferPtr;
2045188c4b9SDmitri Gribenko 
2055188c4b9SDmitri Gribenko   if (*BufferPtr == '\n')
2065188c4b9SDmitri Gribenko     BufferPtr++;
2075188c4b9SDmitri Gribenko   else {
2085188c4b9SDmitri Gribenko     assert(*BufferPtr == '\r');
2095188c4b9SDmitri Gribenko     BufferPtr++;
2105188c4b9SDmitri Gribenko     if (BufferPtr != BufferEnd && *BufferPtr == '\n')
2115188c4b9SDmitri Gribenko       BufferPtr++;
2125188c4b9SDmitri Gribenko   }
2135188c4b9SDmitri Gribenko   return BufferPtr;
2145188c4b9SDmitri Gribenko }
2155188c4b9SDmitri Gribenko 
216*4586df76SDmitri Gribenko const char *skipNamedCharacterReference(const char *BufferPtr,
217*4586df76SDmitri Gribenko                                         const char *BufferEnd) {
218*4586df76SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
219*4586df76SDmitri Gribenko     if (!isHTMLNamedCharacterReferenceCharacter(*BufferPtr))
220*4586df76SDmitri Gribenko       return BufferPtr;
221*4586df76SDmitri Gribenko   }
222*4586df76SDmitri Gribenko   return BufferEnd;
223*4586df76SDmitri Gribenko }
224*4586df76SDmitri Gribenko 
225*4586df76SDmitri Gribenko const char *skipDecimalCharacterReference(const char *BufferPtr,
226*4586df76SDmitri Gribenko                                           const char *BufferEnd) {
227*4586df76SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
228*4586df76SDmitri Gribenko     if (!isHTMLDecimalCharacterReferenceCharacter(*BufferPtr))
229*4586df76SDmitri Gribenko       return BufferPtr;
230*4586df76SDmitri Gribenko   }
231*4586df76SDmitri Gribenko   return BufferEnd;
232*4586df76SDmitri Gribenko }
233*4586df76SDmitri Gribenko 
234*4586df76SDmitri Gribenko const char *skipHexCharacterReference(const char *BufferPtr,
235*4586df76SDmitri Gribenko                                           const char *BufferEnd) {
236*4586df76SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
237*4586df76SDmitri Gribenko     if (!isHTMLHexCharacterReferenceCharacter(*BufferPtr))
238*4586df76SDmitri Gribenko       return BufferPtr;
239*4586df76SDmitri Gribenko   }
240*4586df76SDmitri Gribenko   return BufferEnd;
241*4586df76SDmitri Gribenko }
242*4586df76SDmitri Gribenko 
24317709ae8SDmitri Gribenko bool isHTMLIdentifierStartingCharacter(char C) {
24417709ae8SDmitri Gribenko   return (C >= 'a' && C <= 'z') ||
24517709ae8SDmitri Gribenko          (C >= 'A' && C <= 'Z');
24617709ae8SDmitri Gribenko }
24717709ae8SDmitri Gribenko 
2485188c4b9SDmitri Gribenko bool isHTMLIdentifierCharacter(char C) {
2495188c4b9SDmitri Gribenko   return (C >= 'a' && C <= 'z') ||
2505188c4b9SDmitri Gribenko          (C >= 'A' && C <= 'Z') ||
2515188c4b9SDmitri Gribenko          (C >= '0' && C <= '9');
2525188c4b9SDmitri Gribenko }
2535188c4b9SDmitri Gribenko 
2545188c4b9SDmitri Gribenko const char *skipHTMLIdentifier(const char *BufferPtr, const char *BufferEnd) {
2555188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
2565188c4b9SDmitri Gribenko     if (!isHTMLIdentifierCharacter(*BufferPtr))
2575188c4b9SDmitri Gribenko       return BufferPtr;
2585188c4b9SDmitri Gribenko   }
2595188c4b9SDmitri Gribenko   return BufferEnd;
2605188c4b9SDmitri Gribenko }
2615188c4b9SDmitri Gribenko 
2625188c4b9SDmitri Gribenko /// Skip HTML string quoted in single or double quotes.  Escaping quotes inside
2635188c4b9SDmitri Gribenko /// string allowed.
2645188c4b9SDmitri Gribenko ///
2655188c4b9SDmitri Gribenko /// Returns pointer to closing quote.
2665188c4b9SDmitri Gribenko const char *skipHTMLQuotedString(const char *BufferPtr, const char *BufferEnd)
2675188c4b9SDmitri Gribenko {
2685188c4b9SDmitri Gribenko   const char Quote = *BufferPtr;
2695188c4b9SDmitri Gribenko   assert(Quote == '\"' || Quote == '\'');
2705188c4b9SDmitri Gribenko 
2715188c4b9SDmitri Gribenko   BufferPtr++;
2725188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
2735188c4b9SDmitri Gribenko     const char C = *BufferPtr;
2745188c4b9SDmitri Gribenko     if (C == Quote && BufferPtr[-1] != '\\')
2755188c4b9SDmitri Gribenko       return BufferPtr;
2765188c4b9SDmitri Gribenko   }
2775188c4b9SDmitri Gribenko   return BufferEnd;
2785188c4b9SDmitri Gribenko }
2795188c4b9SDmitri Gribenko 
2805188c4b9SDmitri Gribenko bool isHorizontalWhitespace(char C) {
2815188c4b9SDmitri Gribenko   return C == ' ' || C == '\t' || C == '\f' || C == '\v';
2825188c4b9SDmitri Gribenko }
2835188c4b9SDmitri Gribenko 
2845188c4b9SDmitri Gribenko bool isWhitespace(char C) {
2855188c4b9SDmitri Gribenko   return C == ' ' || C == '\n' || C == '\r' ||
2865188c4b9SDmitri Gribenko          C == '\t' || C == '\f' || C == '\v';
2875188c4b9SDmitri Gribenko }
2885188c4b9SDmitri Gribenko 
2895188c4b9SDmitri Gribenko const char *skipWhitespace(const char *BufferPtr, const char *BufferEnd) {
2905188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
2915188c4b9SDmitri Gribenko     if (!isWhitespace(*BufferPtr))
2925188c4b9SDmitri Gribenko       return BufferPtr;
2935188c4b9SDmitri Gribenko   }
2945188c4b9SDmitri Gribenko   return BufferEnd;
2955188c4b9SDmitri Gribenko }
2965188c4b9SDmitri Gribenko 
297e4a3997dSDmitri Gribenko bool isWhitespace(const char *BufferPtr, const char *BufferEnd) {
298e4a3997dSDmitri Gribenko   return skipWhitespace(BufferPtr, BufferEnd) == BufferEnd;
299e4a3997dSDmitri Gribenko }
300e4a3997dSDmitri Gribenko 
3015188c4b9SDmitri Gribenko bool isCommandNameCharacter(char C) {
3025188c4b9SDmitri Gribenko   return (C >= 'a' && C <= 'z') ||
3035188c4b9SDmitri Gribenko          (C >= 'A' && C <= 'Z') ||
3045188c4b9SDmitri Gribenko          (C >= '0' && C <= '9');
3055188c4b9SDmitri Gribenko }
3065188c4b9SDmitri Gribenko 
3075188c4b9SDmitri Gribenko const char *skipCommandName(const char *BufferPtr, const char *BufferEnd) {
3085188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
3095188c4b9SDmitri Gribenko     if (!isCommandNameCharacter(*BufferPtr))
3105188c4b9SDmitri Gribenko       return BufferPtr;
3115188c4b9SDmitri Gribenko   }
3125188c4b9SDmitri Gribenko   return BufferEnd;
3135188c4b9SDmitri Gribenko }
3145188c4b9SDmitri Gribenko 
3155188c4b9SDmitri Gribenko /// Return the one past end pointer for BCPL comments.
3165188c4b9SDmitri Gribenko /// Handles newlines escaped with backslash or trigraph for backslahs.
3175188c4b9SDmitri Gribenko const char *findBCPLCommentEnd(const char *BufferPtr, const char *BufferEnd) {
3185188c4b9SDmitri Gribenko   const char *CurPtr = BufferPtr;
3195188c4b9SDmitri Gribenko   while (CurPtr != BufferEnd) {
3205188c4b9SDmitri Gribenko     char C = *CurPtr;
3215188c4b9SDmitri Gribenko     while (C != '\n' && C != '\r') {
3225188c4b9SDmitri Gribenko       CurPtr++;
3235188c4b9SDmitri Gribenko       if (CurPtr == BufferEnd)
3245188c4b9SDmitri Gribenko         return BufferEnd;
3255188c4b9SDmitri Gribenko       C = *CurPtr;
3265188c4b9SDmitri Gribenko     }
3275188c4b9SDmitri Gribenko     // We found a newline, check if it is escaped.
3285188c4b9SDmitri Gribenko     const char *EscapePtr = CurPtr - 1;
3295188c4b9SDmitri Gribenko     while(isHorizontalWhitespace(*EscapePtr))
3305188c4b9SDmitri Gribenko       EscapePtr--;
3315188c4b9SDmitri Gribenko 
3325188c4b9SDmitri Gribenko     if (*EscapePtr == '\\' ||
3335188c4b9SDmitri Gribenko         (EscapePtr - 2 >= BufferPtr && EscapePtr[0] == '/' &&
3345188c4b9SDmitri Gribenko          EscapePtr[-1] == '?' && EscapePtr[-2] == '?')) {
3355188c4b9SDmitri Gribenko       // We found an escaped newline.
3365188c4b9SDmitri Gribenko       CurPtr = skipNewline(CurPtr, BufferEnd);
3375188c4b9SDmitri Gribenko     } else
3385188c4b9SDmitri Gribenko       return CurPtr; // Not an escaped newline.
3395188c4b9SDmitri Gribenko   }
3405188c4b9SDmitri Gribenko   return BufferEnd;
3415188c4b9SDmitri Gribenko }
3425188c4b9SDmitri Gribenko 
3435188c4b9SDmitri Gribenko /// Return the one past end pointer for C comments.
3445188c4b9SDmitri Gribenko /// Very dumb, does not handle escaped newlines or trigraphs.
3455188c4b9SDmitri Gribenko const char *findCCommentEnd(const char *BufferPtr, const char *BufferEnd) {
3465188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
3475188c4b9SDmitri Gribenko     if (*BufferPtr == '*') {
3485188c4b9SDmitri Gribenko       assert(BufferPtr + 1 != BufferEnd);
3495188c4b9SDmitri Gribenko       if (*(BufferPtr + 1) == '/')
3505188c4b9SDmitri Gribenko         return BufferPtr;
3515188c4b9SDmitri Gribenko     }
3525188c4b9SDmitri Gribenko   }
3535188c4b9SDmitri Gribenko   llvm_unreachable("buffer end hit before '*/' was seen");
3545188c4b9SDmitri Gribenko }
3555188c4b9SDmitri Gribenko } // unnamed namespace
3565188c4b9SDmitri Gribenko 
3575188c4b9SDmitri Gribenko void Lexer::lexCommentText(Token &T) {
3585188c4b9SDmitri Gribenko   assert(CommentState == LCS_InsideBCPLComment ||
3595188c4b9SDmitri Gribenko          CommentState == LCS_InsideCComment);
3605188c4b9SDmitri Gribenko 
3615188c4b9SDmitri Gribenko   switch (State) {
3625188c4b9SDmitri Gribenko   case LS_Normal:
3635188c4b9SDmitri Gribenko     break;
3645188c4b9SDmitri Gribenko   case LS_VerbatimBlockFirstLine:
3655188c4b9SDmitri Gribenko     lexVerbatimBlockFirstLine(T);
3665188c4b9SDmitri Gribenko     return;
3675188c4b9SDmitri Gribenko   case LS_VerbatimBlockBody:
3685188c4b9SDmitri Gribenko     lexVerbatimBlockBody(T);
3695188c4b9SDmitri Gribenko     return;
3701669f702SDmitri Gribenko   case LS_VerbatimLineText:
3711669f702SDmitri Gribenko     lexVerbatimLineText(T);
3721669f702SDmitri Gribenko     return;
373e00ffc7bSDmitri Gribenko   case LS_HTMLStartTag:
374e00ffc7bSDmitri Gribenko     lexHTMLStartTag(T);
3755188c4b9SDmitri Gribenko     return;
376e00ffc7bSDmitri Gribenko   case LS_HTMLEndTag:
377e00ffc7bSDmitri Gribenko     lexHTMLEndTag(T);
378ec92531cSDmitri Gribenko     return;
3795188c4b9SDmitri Gribenko   }
3805188c4b9SDmitri Gribenko 
3815188c4b9SDmitri Gribenko   assert(State == LS_Normal);
3825188c4b9SDmitri Gribenko 
3835188c4b9SDmitri Gribenko   const char *TokenPtr = BufferPtr;
3845188c4b9SDmitri Gribenko   assert(TokenPtr < CommentEnd);
3855188c4b9SDmitri Gribenko   while (TokenPtr != CommentEnd) {
3865188c4b9SDmitri Gribenko     switch(*TokenPtr) {
3875188c4b9SDmitri Gribenko       case '\\':
3885188c4b9SDmitri Gribenko       case '@': {
3895188c4b9SDmitri Gribenko         TokenPtr++;
3905188c4b9SDmitri Gribenko         if (TokenPtr == CommentEnd) {
391*4586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
3925188c4b9SDmitri Gribenko           return;
3935188c4b9SDmitri Gribenko         }
3945188c4b9SDmitri Gribenko         char C = *TokenPtr;
3955188c4b9SDmitri Gribenko         switch (C) {
3965188c4b9SDmitri Gribenko         default:
3975188c4b9SDmitri Gribenko           break;
3985188c4b9SDmitri Gribenko 
3995188c4b9SDmitri Gribenko         case '\\': case '@': case '&': case '$':
4005188c4b9SDmitri Gribenko         case '#':  case '<': case '>': case '%':
4015188c4b9SDmitri Gribenko         case '\"': case '.': case ':':
4025188c4b9SDmitri Gribenko           // This is one of \\ \@ \& \$ etc escape sequences.
4035188c4b9SDmitri Gribenko           TokenPtr++;
4045188c4b9SDmitri Gribenko           if (C == ':' && TokenPtr != CommentEnd && *TokenPtr == ':') {
4055188c4b9SDmitri Gribenko             // This is the \:: escape sequence.
4065188c4b9SDmitri Gribenko             TokenPtr++;
4075188c4b9SDmitri Gribenko           }
40860ddd8a1SDmitri Gribenko           StringRef UnescapedText(BufferPtr + 1, TokenPtr - (BufferPtr + 1));
4095188c4b9SDmitri Gribenko           formTokenWithChars(T, TokenPtr, tok::text);
41060ddd8a1SDmitri Gribenko           T.setText(UnescapedText);
4115188c4b9SDmitri Gribenko           return;
4125188c4b9SDmitri Gribenko         }
4135188c4b9SDmitri Gribenko 
4145188c4b9SDmitri Gribenko         // Don't make zero-length commands.
4155188c4b9SDmitri Gribenko         if (!isCommandNameCharacter(*TokenPtr)) {
416*4586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
4175188c4b9SDmitri Gribenko           return;
4185188c4b9SDmitri Gribenko         }
4195188c4b9SDmitri Gribenko 
4205188c4b9SDmitri Gribenko         TokenPtr = skipCommandName(TokenPtr, CommentEnd);
4215188c4b9SDmitri Gribenko         unsigned Length = TokenPtr - (BufferPtr + 1);
4225188c4b9SDmitri Gribenko 
4235188c4b9SDmitri Gribenko         // Hardcoded support for lexing LaTeX formula commands
4245188c4b9SDmitri Gribenko         // \f$ \f[ \f] \f{ \f} as a single command.
4255188c4b9SDmitri Gribenko         if (Length == 1 && TokenPtr[-1] == 'f' && TokenPtr != CommentEnd) {
4265188c4b9SDmitri Gribenko           C = *TokenPtr;
4275188c4b9SDmitri Gribenko           if (C == '$' || C == '[' || C == ']' || C == '{' || C == '}') {
4285188c4b9SDmitri Gribenko             TokenPtr++;
4295188c4b9SDmitri Gribenko             Length++;
4305188c4b9SDmitri Gribenko           }
4315188c4b9SDmitri Gribenko         }
4325188c4b9SDmitri Gribenko 
4335188c4b9SDmitri Gribenko         const StringRef CommandName(BufferPtr + 1, Length);
4345188c4b9SDmitri Gribenko         StringRef EndName;
4355188c4b9SDmitri Gribenko 
4365188c4b9SDmitri Gribenko         if (isVerbatimBlockCommand(CommandName, EndName)) {
4375188c4b9SDmitri Gribenko           setupAndLexVerbatimBlock(T, TokenPtr, *BufferPtr, EndName);
4385188c4b9SDmitri Gribenko           return;
4395188c4b9SDmitri Gribenko         }
4405188c4b9SDmitri Gribenko         if (isVerbatimLineCommand(CommandName)) {
4411669f702SDmitri Gribenko           setupAndLexVerbatimLine(T, TokenPtr);
4425188c4b9SDmitri Gribenko           return;
4435188c4b9SDmitri Gribenko         }
4445188c4b9SDmitri Gribenko         formTokenWithChars(T, TokenPtr, tok::command);
4455188c4b9SDmitri Gribenko         T.setCommandName(CommandName);
4465188c4b9SDmitri Gribenko         return;
4475188c4b9SDmitri Gribenko       }
4485188c4b9SDmitri Gribenko 
449*4586df76SDmitri Gribenko       case '&':
450*4586df76SDmitri Gribenko         lexHTMLCharacterReference(T);
451*4586df76SDmitri Gribenko         return;
452*4586df76SDmitri Gribenko 
4535188c4b9SDmitri Gribenko       case '<': {
4545188c4b9SDmitri Gribenko         TokenPtr++;
4555188c4b9SDmitri Gribenko         if (TokenPtr == CommentEnd) {
456*4586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
4575188c4b9SDmitri Gribenko           return;
4585188c4b9SDmitri Gribenko         }
4595188c4b9SDmitri Gribenko         const char C = *TokenPtr;
46017709ae8SDmitri Gribenko         if (isHTMLIdentifierStartingCharacter(C))
461e00ffc7bSDmitri Gribenko           setupAndLexHTMLStartTag(T);
4625188c4b9SDmitri Gribenko         else if (C == '/')
463e00ffc7bSDmitri Gribenko           setupAndLexHTMLEndTag(T);
464*4586df76SDmitri Gribenko         else
465*4586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
466*4586df76SDmitri Gribenko 
4675188c4b9SDmitri Gribenko         return;
4685188c4b9SDmitri Gribenko       }
4695188c4b9SDmitri Gribenko 
4705188c4b9SDmitri Gribenko       case '\n':
4715188c4b9SDmitri Gribenko       case '\r':
4725188c4b9SDmitri Gribenko         TokenPtr = skipNewline(TokenPtr, CommentEnd);
4735188c4b9SDmitri Gribenko         formTokenWithChars(T, TokenPtr, tok::newline);
4745188c4b9SDmitri Gribenko 
4755188c4b9SDmitri Gribenko         if (CommentState == LCS_InsideCComment)
4765188c4b9SDmitri Gribenko           skipLineStartingDecorations();
4775188c4b9SDmitri Gribenko         return;
4785188c4b9SDmitri Gribenko 
4795188c4b9SDmitri Gribenko       default: {
4805188c4b9SDmitri Gribenko         while (true) {
4815188c4b9SDmitri Gribenko           TokenPtr++;
4825188c4b9SDmitri Gribenko           if (TokenPtr == CommentEnd)
4835188c4b9SDmitri Gribenko             break;
48417709ae8SDmitri Gribenko           const char C = *TokenPtr;
4855188c4b9SDmitri Gribenko           if(C == '\n' || C == '\r' ||
486*4586df76SDmitri Gribenko              C == '\\' || C == '@' || C == '&' || C == '<')
4875188c4b9SDmitri Gribenko             break;
4885188c4b9SDmitri Gribenko         }
489*4586df76SDmitri Gribenko         formTextToken(T, TokenPtr);
4905188c4b9SDmitri Gribenko         return;
4915188c4b9SDmitri Gribenko       }
4925188c4b9SDmitri Gribenko     }
4935188c4b9SDmitri Gribenko   }
4945188c4b9SDmitri Gribenko }
4955188c4b9SDmitri Gribenko 
4965188c4b9SDmitri Gribenko void Lexer::setupAndLexVerbatimBlock(Token &T,
4975188c4b9SDmitri Gribenko                                      const char *TextBegin,
4985188c4b9SDmitri Gribenko                                      char Marker, StringRef EndName) {
4995188c4b9SDmitri Gribenko   VerbatimBlockEndCommandName.clear();
5005188c4b9SDmitri Gribenko   VerbatimBlockEndCommandName.append(Marker == '\\' ? "\\" : "@");
5015188c4b9SDmitri Gribenko   VerbatimBlockEndCommandName.append(EndName);
5025188c4b9SDmitri Gribenko 
50360ddd8a1SDmitri Gribenko   StringRef Name(BufferPtr + 1, TextBegin - (BufferPtr + 1));
5045188c4b9SDmitri Gribenko   formTokenWithChars(T, TextBegin, tok::verbatim_block_begin);
50560ddd8a1SDmitri Gribenko   T.setVerbatimBlockName(Name);
5065188c4b9SDmitri Gribenko 
507ec92531cSDmitri Gribenko   // If there is a newline following the verbatim opening command, skip the
508ec92531cSDmitri Gribenko   // newline so that we don't create an tok::verbatim_block_line with empty
509ec92531cSDmitri Gribenko   // text content.
510ec92531cSDmitri Gribenko   if (BufferPtr != CommentEnd) {
511ec92531cSDmitri Gribenko     const char C = *BufferPtr;
512ec92531cSDmitri Gribenko     if (C == '\n' || C == '\r') {
513ec92531cSDmitri Gribenko       BufferPtr = skipNewline(BufferPtr, CommentEnd);
514ec92531cSDmitri Gribenko       State = LS_VerbatimBlockBody;
515ec92531cSDmitri Gribenko       return;
516ec92531cSDmitri Gribenko     }
517ec92531cSDmitri Gribenko   }
518ec92531cSDmitri Gribenko 
5195188c4b9SDmitri Gribenko   State = LS_VerbatimBlockFirstLine;
5205188c4b9SDmitri Gribenko }
5215188c4b9SDmitri Gribenko 
5225188c4b9SDmitri Gribenko void Lexer::lexVerbatimBlockFirstLine(Token &T) {
523e4a3997dSDmitri Gribenko again:
5245188c4b9SDmitri Gribenko   assert(BufferPtr < CommentEnd);
5255188c4b9SDmitri Gribenko 
5265188c4b9SDmitri Gribenko   // FIXME: It would be better to scan the text once, finding either the block
5275188c4b9SDmitri Gribenko   // end command or newline.
5285188c4b9SDmitri Gribenko   //
5295188c4b9SDmitri Gribenko   // Extract current line.
5305188c4b9SDmitri Gribenko   const char *Newline = findNewline(BufferPtr, CommentEnd);
5315188c4b9SDmitri Gribenko   StringRef Line(BufferPtr, Newline - BufferPtr);
5325188c4b9SDmitri Gribenko 
5335188c4b9SDmitri Gribenko   // Look for end command in current line.
5345188c4b9SDmitri Gribenko   size_t Pos = Line.find(VerbatimBlockEndCommandName);
535ec92531cSDmitri Gribenko   const char *TextEnd;
5365188c4b9SDmitri Gribenko   const char *NextLine;
5375188c4b9SDmitri Gribenko   if (Pos == StringRef::npos) {
5385188c4b9SDmitri Gribenko     // Current line is completely verbatim.
539ec92531cSDmitri Gribenko     TextEnd = Newline;
5405188c4b9SDmitri Gribenko     NextLine = skipNewline(Newline, CommentEnd);
5415188c4b9SDmitri Gribenko   } else if (Pos == 0) {
5425188c4b9SDmitri Gribenko     // Current line contains just an end command.
5435188c4b9SDmitri Gribenko     const char *End = BufferPtr + VerbatimBlockEndCommandName.size();
54460ddd8a1SDmitri Gribenko     StringRef Name(BufferPtr + 1, End - (BufferPtr + 1));
5455188c4b9SDmitri Gribenko     formTokenWithChars(T, End, tok::verbatim_block_end);
54660ddd8a1SDmitri Gribenko     T.setVerbatimBlockName(Name);
5475188c4b9SDmitri Gribenko     State = LS_Normal;
5485188c4b9SDmitri Gribenko     return;
5495188c4b9SDmitri Gribenko   } else {
5505188c4b9SDmitri Gribenko     // There is some text, followed by end command.  Extract text first.
551ec92531cSDmitri Gribenko     TextEnd = BufferPtr + Pos;
552ec92531cSDmitri Gribenko     NextLine = TextEnd;
553e4a3997dSDmitri Gribenko     // If there is only whitespace before end command, skip whitespace.
554e4a3997dSDmitri Gribenko     if (isWhitespace(BufferPtr, TextEnd)) {
555e4a3997dSDmitri Gribenko       BufferPtr = TextEnd;
556e4a3997dSDmitri Gribenko       goto again;
557e4a3997dSDmitri Gribenko     }
5585188c4b9SDmitri Gribenko   }
5595188c4b9SDmitri Gribenko 
560ec92531cSDmitri Gribenko   StringRef Text(BufferPtr, TextEnd - BufferPtr);
5615188c4b9SDmitri Gribenko   formTokenWithChars(T, NextLine, tok::verbatim_block_line);
56260ddd8a1SDmitri Gribenko   T.setVerbatimBlockText(Text);
5635188c4b9SDmitri Gribenko 
5645188c4b9SDmitri Gribenko   State = LS_VerbatimBlockBody;
5655188c4b9SDmitri Gribenko }
5665188c4b9SDmitri Gribenko 
5675188c4b9SDmitri Gribenko void Lexer::lexVerbatimBlockBody(Token &T) {
5685188c4b9SDmitri Gribenko   assert(State == LS_VerbatimBlockBody);
5695188c4b9SDmitri Gribenko 
5705188c4b9SDmitri Gribenko   if (CommentState == LCS_InsideCComment)
5715188c4b9SDmitri Gribenko     skipLineStartingDecorations();
5725188c4b9SDmitri Gribenko 
5735188c4b9SDmitri Gribenko   lexVerbatimBlockFirstLine(T);
5745188c4b9SDmitri Gribenko }
5755188c4b9SDmitri Gribenko 
5761669f702SDmitri Gribenko void Lexer::setupAndLexVerbatimLine(Token &T, const char *TextBegin) {
5771669f702SDmitri Gribenko   const StringRef Name(BufferPtr + 1, TextBegin - BufferPtr - 1);
5781669f702SDmitri Gribenko   formTokenWithChars(T, TextBegin, tok::verbatim_line_name);
5791669f702SDmitri Gribenko   T.setVerbatimLineName(Name);
5801669f702SDmitri Gribenko 
5811669f702SDmitri Gribenko   State = LS_VerbatimLineText;
5821669f702SDmitri Gribenko }
5831669f702SDmitri Gribenko 
5841669f702SDmitri Gribenko void Lexer::lexVerbatimLineText(Token &T) {
5851669f702SDmitri Gribenko   assert(State == LS_VerbatimLineText);
5861669f702SDmitri Gribenko 
5875188c4b9SDmitri Gribenko   // Extract current line.
5885188c4b9SDmitri Gribenko   const char *Newline = findNewline(BufferPtr, CommentEnd);
5891669f702SDmitri Gribenko   const StringRef Text(BufferPtr, Newline - BufferPtr);
5901669f702SDmitri Gribenko   formTokenWithChars(T, Newline, tok::verbatim_line_text);
5915188c4b9SDmitri Gribenko   T.setVerbatimLineText(Text);
5921669f702SDmitri Gribenko 
5931669f702SDmitri Gribenko   State = LS_Normal;
5945188c4b9SDmitri Gribenko }
5955188c4b9SDmitri Gribenko 
596*4586df76SDmitri Gribenko void Lexer::lexHTMLCharacterReference(Token &T) {
597*4586df76SDmitri Gribenko   const char *TokenPtr = BufferPtr;
598*4586df76SDmitri Gribenko   assert(*TokenPtr == '&');
599*4586df76SDmitri Gribenko   TokenPtr++;
600*4586df76SDmitri Gribenko   if (TokenPtr == CommentEnd) {
601*4586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
602*4586df76SDmitri Gribenko     return;
603*4586df76SDmitri Gribenko   }
604*4586df76SDmitri Gribenko   const char *NamePtr;
605*4586df76SDmitri Gribenko   bool isNamed = false;
606*4586df76SDmitri Gribenko   bool isDecimal = false;
607*4586df76SDmitri Gribenko   char C = *TokenPtr;
608*4586df76SDmitri Gribenko   if (isHTMLNamedCharacterReferenceCharacter(C)) {
609*4586df76SDmitri Gribenko     NamePtr = TokenPtr;
610*4586df76SDmitri Gribenko     TokenPtr = skipNamedCharacterReference(TokenPtr, CommentEnd);
611*4586df76SDmitri Gribenko     isNamed = true;
612*4586df76SDmitri Gribenko   } else if (C == '#') {
613*4586df76SDmitri Gribenko     TokenPtr++;
614*4586df76SDmitri Gribenko     if (TokenPtr == CommentEnd) {
615*4586df76SDmitri Gribenko       formTextToken(T, TokenPtr);
616*4586df76SDmitri Gribenko       return;
617*4586df76SDmitri Gribenko     }
618*4586df76SDmitri Gribenko     C = *TokenPtr;
619*4586df76SDmitri Gribenko     if (isHTMLDecimalCharacterReferenceCharacter(C)) {
620*4586df76SDmitri Gribenko       NamePtr = TokenPtr;
621*4586df76SDmitri Gribenko       TokenPtr = skipDecimalCharacterReference(TokenPtr, CommentEnd);
622*4586df76SDmitri Gribenko       isDecimal = true;
623*4586df76SDmitri Gribenko     } else if (C == 'x' || C == 'X') {
624*4586df76SDmitri Gribenko       TokenPtr++;
625*4586df76SDmitri Gribenko       NamePtr = TokenPtr;
626*4586df76SDmitri Gribenko       TokenPtr = skipHexCharacterReference(TokenPtr, CommentEnd);
627*4586df76SDmitri Gribenko     } else {
628*4586df76SDmitri Gribenko       formTextToken(T, TokenPtr);
629*4586df76SDmitri Gribenko       return;
630*4586df76SDmitri Gribenko     }
631*4586df76SDmitri Gribenko   } else {
632*4586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
633*4586df76SDmitri Gribenko     return;
634*4586df76SDmitri Gribenko   }
635*4586df76SDmitri Gribenko   if (NamePtr == TokenPtr || TokenPtr == CommentEnd ||
636*4586df76SDmitri Gribenko       *TokenPtr != ';') {
637*4586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
638*4586df76SDmitri Gribenko     return;
639*4586df76SDmitri Gribenko   }
640*4586df76SDmitri Gribenko   StringRef Name(NamePtr, TokenPtr - NamePtr);
641*4586df76SDmitri Gribenko   TokenPtr++; // Skip semicolon.
642*4586df76SDmitri Gribenko   StringRef Resolved;
643*4586df76SDmitri Gribenko   if (isNamed)
644*4586df76SDmitri Gribenko     Resolved = resolveHTMLNamedCharacterReference(Name);
645*4586df76SDmitri Gribenko   else if (isDecimal)
646*4586df76SDmitri Gribenko     Resolved = resolveHTMLDecimalCharacterReference(Name);
647*4586df76SDmitri Gribenko   else
648*4586df76SDmitri Gribenko     Resolved = resolveHTMLHexCharacterReference(Name);
649*4586df76SDmitri Gribenko 
650*4586df76SDmitri Gribenko   if (Resolved.empty()) {
651*4586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
652*4586df76SDmitri Gribenko     return;
653*4586df76SDmitri Gribenko   }
654*4586df76SDmitri Gribenko   formTokenWithChars(T, TokenPtr, tok::text);
655*4586df76SDmitri Gribenko   T.setText(Resolved);
656*4586df76SDmitri Gribenko   return;
657*4586df76SDmitri Gribenko }
658*4586df76SDmitri Gribenko 
659e00ffc7bSDmitri Gribenko void Lexer::setupAndLexHTMLStartTag(Token &T) {
66017709ae8SDmitri Gribenko   assert(BufferPtr[0] == '<' &&
66117709ae8SDmitri Gribenko          isHTMLIdentifierStartingCharacter(BufferPtr[1]));
6625188c4b9SDmitri Gribenko   const char *TagNameEnd = skipHTMLIdentifier(BufferPtr + 2, CommentEnd);
6635188c4b9SDmitri Gribenko 
66460ddd8a1SDmitri Gribenko   StringRef Name(BufferPtr + 1, TagNameEnd - (BufferPtr + 1));
665e00ffc7bSDmitri Gribenko   formTokenWithChars(T, TagNameEnd, tok::html_start_tag);
666e00ffc7bSDmitri Gribenko   T.setHTMLTagStartName(Name);
6675188c4b9SDmitri Gribenko 
6685188c4b9SDmitri Gribenko   BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
6695188c4b9SDmitri Gribenko 
67017709ae8SDmitri Gribenko   const char C = *BufferPtr;
67117709ae8SDmitri Gribenko   if (BufferPtr != CommentEnd &&
672f26054f0SDmitri Gribenko       (C == '>' || C == '/' || isHTMLIdentifierStartingCharacter(C)))
673e00ffc7bSDmitri Gribenko     State = LS_HTMLStartTag;
6745188c4b9SDmitri Gribenko }
6755188c4b9SDmitri Gribenko 
676e00ffc7bSDmitri Gribenko void Lexer::lexHTMLStartTag(Token &T) {
677e00ffc7bSDmitri Gribenko   assert(State == LS_HTMLStartTag);
6785188c4b9SDmitri Gribenko 
6795188c4b9SDmitri Gribenko   const char *TokenPtr = BufferPtr;
6805188c4b9SDmitri Gribenko   char C = *TokenPtr;
6815188c4b9SDmitri Gribenko   if (isHTMLIdentifierCharacter(C)) {
6825188c4b9SDmitri Gribenko     TokenPtr = skipHTMLIdentifier(TokenPtr, CommentEnd);
68360ddd8a1SDmitri Gribenko     StringRef Ident(BufferPtr, TokenPtr - BufferPtr);
6845188c4b9SDmitri Gribenko     formTokenWithChars(T, TokenPtr, tok::html_ident);
68560ddd8a1SDmitri Gribenko     T.setHTMLIdent(Ident);
6865188c4b9SDmitri Gribenko   } else {
6875188c4b9SDmitri Gribenko     switch (C) {
6885188c4b9SDmitri Gribenko     case '=':
6895188c4b9SDmitri Gribenko       TokenPtr++;
6905188c4b9SDmitri Gribenko       formTokenWithChars(T, TokenPtr, tok::html_equals);
6915188c4b9SDmitri Gribenko       break;
6925188c4b9SDmitri Gribenko     case '\"':
6935188c4b9SDmitri Gribenko     case '\'': {
6945188c4b9SDmitri Gribenko       const char *OpenQuote = TokenPtr;
6955188c4b9SDmitri Gribenko       TokenPtr = skipHTMLQuotedString(TokenPtr, CommentEnd);
6965188c4b9SDmitri Gribenko       const char *ClosingQuote = TokenPtr;
6975188c4b9SDmitri Gribenko       if (TokenPtr != CommentEnd) // Skip closing quote.
6985188c4b9SDmitri Gribenko         TokenPtr++;
6995188c4b9SDmitri Gribenko       formTokenWithChars(T, TokenPtr, tok::html_quoted_string);
7005188c4b9SDmitri Gribenko       T.setHTMLQuotedString(StringRef(OpenQuote + 1,
7015188c4b9SDmitri Gribenko                                       ClosingQuote - (OpenQuote + 1)));
7025188c4b9SDmitri Gribenko       break;
7035188c4b9SDmitri Gribenko     }
7045188c4b9SDmitri Gribenko     case '>':
7055188c4b9SDmitri Gribenko       TokenPtr++;
7065188c4b9SDmitri Gribenko       formTokenWithChars(T, TokenPtr, tok::html_greater);
70717709ae8SDmitri Gribenko       State = LS_Normal;
70817709ae8SDmitri Gribenko       return;
709f26054f0SDmitri Gribenko     case '/':
710f26054f0SDmitri Gribenko       TokenPtr++;
711f26054f0SDmitri Gribenko       if (TokenPtr != CommentEnd && *TokenPtr == '>') {
712f26054f0SDmitri Gribenko         TokenPtr++;
713f26054f0SDmitri Gribenko         formTokenWithChars(T, TokenPtr, tok::html_slash_greater);
714*4586df76SDmitri Gribenko       } else
715*4586df76SDmitri Gribenko         formTextToken(T, TokenPtr);
716*4586df76SDmitri Gribenko 
717f26054f0SDmitri Gribenko       State = LS_Normal;
718f26054f0SDmitri Gribenko       return;
7195188c4b9SDmitri Gribenko     }
7205188c4b9SDmitri Gribenko   }
7215188c4b9SDmitri Gribenko 
7225188c4b9SDmitri Gribenko   // Now look ahead and return to normal state if we don't see any HTML tokens
7235188c4b9SDmitri Gribenko   // ahead.
7245188c4b9SDmitri Gribenko   BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
7255188c4b9SDmitri Gribenko   if (BufferPtr == CommentEnd) {
7265188c4b9SDmitri Gribenko     State = LS_Normal;
7275188c4b9SDmitri Gribenko     return;
7285188c4b9SDmitri Gribenko   }
7295188c4b9SDmitri Gribenko 
7305188c4b9SDmitri Gribenko   C = *BufferPtr;
73117709ae8SDmitri Gribenko   if (!isHTMLIdentifierStartingCharacter(C) &&
7325188c4b9SDmitri Gribenko       C != '=' && C != '\"' && C != '\'' && C != '>') {
7335188c4b9SDmitri Gribenko     State = LS_Normal;
7345188c4b9SDmitri Gribenko     return;
7355188c4b9SDmitri Gribenko   }
7365188c4b9SDmitri Gribenko }
7375188c4b9SDmitri Gribenko 
738e00ffc7bSDmitri Gribenko void Lexer::setupAndLexHTMLEndTag(Token &T) {
7395188c4b9SDmitri Gribenko   assert(BufferPtr[0] == '<' && BufferPtr[1] == '/');
7405188c4b9SDmitri Gribenko 
7415188c4b9SDmitri Gribenko   const char *TagNameBegin = skipWhitespace(BufferPtr + 2, CommentEnd);
7425188c4b9SDmitri Gribenko   const char *TagNameEnd = skipHTMLIdentifier(TagNameBegin, CommentEnd);
7435188c4b9SDmitri Gribenko 
7445188c4b9SDmitri Gribenko   const char *End = skipWhitespace(TagNameEnd, CommentEnd);
7455188c4b9SDmitri Gribenko 
746e00ffc7bSDmitri Gribenko   formTokenWithChars(T, End, tok::html_end_tag);
747e00ffc7bSDmitri Gribenko   T.setHTMLTagEndName(StringRef(TagNameBegin, TagNameEnd - TagNameBegin));
748ec92531cSDmitri Gribenko 
749ec92531cSDmitri Gribenko   if (BufferPtr != CommentEnd && *BufferPtr == '>')
750e00ffc7bSDmitri Gribenko     State = LS_HTMLEndTag;
751ec92531cSDmitri Gribenko }
752ec92531cSDmitri Gribenko 
753e00ffc7bSDmitri Gribenko void Lexer::lexHTMLEndTag(Token &T) {
754ec92531cSDmitri Gribenko   assert(BufferPtr != CommentEnd && *BufferPtr == '>');
755ec92531cSDmitri Gribenko 
756ec92531cSDmitri Gribenko   formTokenWithChars(T, BufferPtr + 1, tok::html_greater);
757ec92531cSDmitri Gribenko   State = LS_Normal;
7585188c4b9SDmitri Gribenko }
7595188c4b9SDmitri Gribenko 
760*4586df76SDmitri Gribenko Lexer::Lexer(llvm::BumpPtrAllocator &Allocator,
761*4586df76SDmitri Gribenko              SourceLocation FileLoc, const CommentOptions &CommOpts,
7625188c4b9SDmitri Gribenko              const char *BufferStart, const char *BufferEnd):
763*4586df76SDmitri Gribenko     Allocator(Allocator),
7645188c4b9SDmitri Gribenko     BufferStart(BufferStart), BufferEnd(BufferEnd),
7655188c4b9SDmitri Gribenko     FileLoc(FileLoc), CommOpts(CommOpts), BufferPtr(BufferStart),
7665188c4b9SDmitri Gribenko     CommentState(LCS_BeforeComment), State(LS_Normal) {
7675188c4b9SDmitri Gribenko }
7685188c4b9SDmitri Gribenko 
7695188c4b9SDmitri Gribenko void Lexer::lex(Token &T) {
7705188c4b9SDmitri Gribenko again:
7715188c4b9SDmitri Gribenko   switch (CommentState) {
7725188c4b9SDmitri Gribenko   case LCS_BeforeComment:
7735188c4b9SDmitri Gribenko     if (BufferPtr == BufferEnd) {
7745188c4b9SDmitri Gribenko       formTokenWithChars(T, BufferPtr, tok::eof);
7755188c4b9SDmitri Gribenko       return;
7765188c4b9SDmitri Gribenko     }
7775188c4b9SDmitri Gribenko 
7785188c4b9SDmitri Gribenko     assert(*BufferPtr == '/');
7795188c4b9SDmitri Gribenko     BufferPtr++; // Skip first slash.
7805188c4b9SDmitri Gribenko     switch(*BufferPtr) {
7815188c4b9SDmitri Gribenko     case '/': { // BCPL comment.
7825188c4b9SDmitri Gribenko       BufferPtr++; // Skip second slash.
7835188c4b9SDmitri Gribenko 
7845188c4b9SDmitri Gribenko       if (BufferPtr != BufferEnd) {
7855188c4b9SDmitri Gribenko         // Skip Doxygen magic marker, if it is present.
7865188c4b9SDmitri Gribenko         // It might be missing because of a typo //< or /*<, or because we
7875188c4b9SDmitri Gribenko         // merged this non-Doxygen comment into a bunch of Doxygen comments
7885188c4b9SDmitri Gribenko         // around it: /** ... */ /* ... */ /** ... */
7895188c4b9SDmitri Gribenko         const char C = *BufferPtr;
7905188c4b9SDmitri Gribenko         if (C == '/' || C == '!')
7915188c4b9SDmitri Gribenko           BufferPtr++;
7925188c4b9SDmitri Gribenko       }
7935188c4b9SDmitri Gribenko 
7945188c4b9SDmitri Gribenko       // Skip less-than symbol that marks trailing comments.
7955188c4b9SDmitri Gribenko       // Skip it even if the comment is not a Doxygen one, because //< and /*<
7965188c4b9SDmitri Gribenko       // are frequent typos.
7975188c4b9SDmitri Gribenko       if (BufferPtr != BufferEnd && *BufferPtr == '<')
7985188c4b9SDmitri Gribenko         BufferPtr++;
7995188c4b9SDmitri Gribenko 
8005188c4b9SDmitri Gribenko       CommentState = LCS_InsideBCPLComment;
801ec92531cSDmitri Gribenko       if (State != LS_VerbatimBlockBody && State != LS_VerbatimBlockFirstLine)
8025188c4b9SDmitri Gribenko         State = LS_Normal;
8035188c4b9SDmitri Gribenko       CommentEnd = findBCPLCommentEnd(BufferPtr, BufferEnd);
8045188c4b9SDmitri Gribenko       goto again;
8055188c4b9SDmitri Gribenko     }
8065188c4b9SDmitri Gribenko     case '*': { // C comment.
8075188c4b9SDmitri Gribenko       BufferPtr++; // Skip star.
8085188c4b9SDmitri Gribenko 
8095188c4b9SDmitri Gribenko       // Skip Doxygen magic marker.
8105188c4b9SDmitri Gribenko       const char C = *BufferPtr;
8115188c4b9SDmitri Gribenko       if ((C == '*' && *(BufferPtr + 1) != '/') || C == '!')
8125188c4b9SDmitri Gribenko         BufferPtr++;
8135188c4b9SDmitri Gribenko 
8145188c4b9SDmitri Gribenko       // Skip less-than symbol that marks trailing comments.
8155188c4b9SDmitri Gribenko       if (BufferPtr != BufferEnd && *BufferPtr == '<')
8165188c4b9SDmitri Gribenko         BufferPtr++;
8175188c4b9SDmitri Gribenko 
8185188c4b9SDmitri Gribenko       CommentState = LCS_InsideCComment;
8195188c4b9SDmitri Gribenko       State = LS_Normal;
8205188c4b9SDmitri Gribenko       CommentEnd = findCCommentEnd(BufferPtr, BufferEnd);
8215188c4b9SDmitri Gribenko       goto again;
8225188c4b9SDmitri Gribenko     }
8235188c4b9SDmitri Gribenko     default:
8245188c4b9SDmitri Gribenko       llvm_unreachable("second character of comment should be '/' or '*'");
8255188c4b9SDmitri Gribenko     }
8265188c4b9SDmitri Gribenko 
8275188c4b9SDmitri Gribenko   case LCS_BetweenComments: {
8285188c4b9SDmitri Gribenko     // Consecutive comments are extracted only if there is only whitespace
8295188c4b9SDmitri Gribenko     // between them.  So we can search for the start of the next comment.
8305188c4b9SDmitri Gribenko     const char *EndWhitespace = BufferPtr;
8315188c4b9SDmitri Gribenko     while(EndWhitespace != BufferEnd && *EndWhitespace != '/')
8325188c4b9SDmitri Gribenko       EndWhitespace++;
8335188c4b9SDmitri Gribenko 
8345188c4b9SDmitri Gribenko     // Turn any whitespace between comments (and there is only whitespace
83517709ae8SDmitri Gribenko     // between them -- guaranteed by comment extraction) into a newline.  We
83617709ae8SDmitri Gribenko     // have two newlines between C comments in total (first one was synthesized
83717709ae8SDmitri Gribenko     // after a comment).
8385188c4b9SDmitri Gribenko     formTokenWithChars(T, EndWhitespace, tok::newline);
8395188c4b9SDmitri Gribenko 
8405188c4b9SDmitri Gribenko     CommentState = LCS_BeforeComment;
8415188c4b9SDmitri Gribenko     break;
8425188c4b9SDmitri Gribenko   }
8435188c4b9SDmitri Gribenko 
8445188c4b9SDmitri Gribenko   case LCS_InsideBCPLComment:
8455188c4b9SDmitri Gribenko   case LCS_InsideCComment:
8465188c4b9SDmitri Gribenko     if (BufferPtr != CommentEnd) {
8475188c4b9SDmitri Gribenko       lexCommentText(T);
8485188c4b9SDmitri Gribenko       break;
8495188c4b9SDmitri Gribenko     } else {
8505188c4b9SDmitri Gribenko       // Skip C comment closing sequence.
8515188c4b9SDmitri Gribenko       if (CommentState == LCS_InsideCComment) {
8525188c4b9SDmitri Gribenko         assert(BufferPtr[0] == '*' && BufferPtr[1] == '/');
8535188c4b9SDmitri Gribenko         BufferPtr += 2;
8545188c4b9SDmitri Gribenko         assert(BufferPtr <= BufferEnd);
8555188c4b9SDmitri Gribenko 
8565188c4b9SDmitri Gribenko         // Synthenize newline just after the C comment, regardless if there is
8575188c4b9SDmitri Gribenko         // actually a newline.
8585188c4b9SDmitri Gribenko         formTokenWithChars(T, BufferPtr, tok::newline);
8595188c4b9SDmitri Gribenko 
8605188c4b9SDmitri Gribenko         CommentState = LCS_BetweenComments;
8615188c4b9SDmitri Gribenko         break;
8625188c4b9SDmitri Gribenko       } else {
8635188c4b9SDmitri Gribenko         // Don't synthesized a newline after BCPL comment.
8645188c4b9SDmitri Gribenko         CommentState = LCS_BetweenComments;
8655188c4b9SDmitri Gribenko         goto again;
8665188c4b9SDmitri Gribenko       }
8675188c4b9SDmitri Gribenko     }
8685188c4b9SDmitri Gribenko   }
8695188c4b9SDmitri Gribenko }
8705188c4b9SDmitri Gribenko 
8715188c4b9SDmitri Gribenko StringRef Lexer::getSpelling(const Token &Tok,
8725188c4b9SDmitri Gribenko                              const SourceManager &SourceMgr,
8735188c4b9SDmitri Gribenko                              bool *Invalid) const {
8745188c4b9SDmitri Gribenko   SourceLocation Loc = Tok.getLocation();
8755188c4b9SDmitri Gribenko   std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedLoc(Loc);
8765188c4b9SDmitri Gribenko 
8775188c4b9SDmitri Gribenko   bool InvalidTemp = false;
8785188c4b9SDmitri Gribenko   StringRef File = SourceMgr.getBufferData(LocInfo.first, &InvalidTemp);
8795188c4b9SDmitri Gribenko   if (InvalidTemp) {
8805188c4b9SDmitri Gribenko     *Invalid = true;
8815188c4b9SDmitri Gribenko     return StringRef();
8825188c4b9SDmitri Gribenko   }
8835188c4b9SDmitri Gribenko 
8845188c4b9SDmitri Gribenko   const char *Begin = File.data() + LocInfo.second;
8855188c4b9SDmitri Gribenko   return StringRef(Begin, Tok.getLength());
8865188c4b9SDmitri Gribenko }
8875188c4b9SDmitri Gribenko 
8885188c4b9SDmitri Gribenko void Lexer::addVerbatimBlockCommand(StringRef BeginName, StringRef EndName) {
8895188c4b9SDmitri Gribenko   VerbatimBlockCommand VBC;
8905188c4b9SDmitri Gribenko   VBC.BeginName = BeginName;
8915188c4b9SDmitri Gribenko   VBC.EndName = EndName;
8925188c4b9SDmitri Gribenko   VerbatimBlockCommands.push_back(VBC);
8935188c4b9SDmitri Gribenko }
8945188c4b9SDmitri Gribenko 
8955188c4b9SDmitri Gribenko void Lexer::addVerbatimLineCommand(StringRef Name) {
8965188c4b9SDmitri Gribenko   VerbatimLineCommand VLC;
8975188c4b9SDmitri Gribenko   VLC.Name = Name;
8985188c4b9SDmitri Gribenko   VerbatimLineCommands.push_back(VLC);
8995188c4b9SDmitri Gribenko }
9005188c4b9SDmitri Gribenko 
9015188c4b9SDmitri Gribenko } // end namespace comments
9025188c4b9SDmitri Gribenko } // end namespace clang
9035188c4b9SDmitri Gribenko 
904