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