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