15188c4b9SDmitri Gribenko #include "clang/AST/CommentLexer.h" 2*5b637078SFariborz Jahanian #include "clang/Lex/LexDiagnostic.h" 3ca7f80adSDmitri Gribenko #include "clang/AST/CommentCommandTraits.h" 4bcef3411SDmitri Gribenko #include "clang/Basic/CharInfo.h" 5b2e5482bSDmitri Gribenko #include "llvm/ADT/StringExtras.h" 65188c4b9SDmitri Gribenko #include "llvm/ADT/StringSwitch.h" 79feeef40SDmitri Gribenko #include "llvm/Support/ConvertUTF.h" 85188c4b9SDmitri Gribenko #include "llvm/Support/ErrorHandling.h" 95188c4b9SDmitri Gribenko 105188c4b9SDmitri Gribenko namespace clang { 115188c4b9SDmitri Gribenko namespace comments { 125188c4b9SDmitri Gribenko 135188c4b9SDmitri Gribenko void Token::dump(const Lexer &L, const SourceManager &SM) const { 145188c4b9SDmitri Gribenko llvm::errs() << "comments::Token Kind=" << Kind << " "; 155188c4b9SDmitri Gribenko Loc.dump(SM); 165188c4b9SDmitri Gribenko llvm::errs() << " " << Length << " \"" << L.getSpelling(*this, SM) << "\"\n"; 175188c4b9SDmitri Gribenko } 185188c4b9SDmitri Gribenko 1974f4d028SDmitri Gribenko static inline bool isHTMLNamedCharacterReferenceCharacter(char C) { 20bcef3411SDmitri Gribenko return isLetter(C); 214586df76SDmitri Gribenko } 224586df76SDmitri Gribenko 2374f4d028SDmitri Gribenko static inline bool isHTMLDecimalCharacterReferenceCharacter(char C) { 24bcef3411SDmitri Gribenko return isDigit(C); 254586df76SDmitri Gribenko } 264586df76SDmitri Gribenko 2774f4d028SDmitri Gribenko static inline bool isHTMLHexCharacterReferenceCharacter(char C) { 28bcef3411SDmitri Gribenko return isHexDigit(C); 294586df76SDmitri Gribenko } 30107618a6SDmitri Gribenko 3174f4d028SDmitri Gribenko static inline StringRef convertCodePointToUTF8( 3274f4d028SDmitri Gribenko llvm::BumpPtrAllocator &Allocator, 3328800da1SDmitri Gribenko unsigned CodePoint) { 344586df76SDmitri Gribenko char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT); 354586df76SDmitri Gribenko char *ResolvedPtr = Resolved; 369feeef40SDmitri Gribenko if (llvm::ConvertCodePointToUTF8(CodePoint, ResolvedPtr)) 374586df76SDmitri Gribenko return StringRef(Resolved, ResolvedPtr - Resolved); 384586df76SDmitri Gribenko else 394586df76SDmitri Gribenko return StringRef(); 404586df76SDmitri Gribenko } 414586df76SDmitri Gribenko 4274f4d028SDmitri Gribenko namespace { 4374f4d028SDmitri Gribenko 4428800da1SDmitri Gribenko #include "clang/AST/CommentHTMLTags.inc" 4528800da1SDmitri Gribenko #include "clang/AST/CommentHTMLNamedCharacterReferences.inc" 4628800da1SDmitri Gribenko 4728800da1SDmitri Gribenko } // unnamed namespace 487b3ae190SFariborz Jahanian 497b3ae190SFariborz Jahanian StringRef Lexer::resolveHTMLNamedCharacterReference(StringRef Name) const { 5028800da1SDmitri Gribenko // Fast path, first check a few most widely used named character references. 517b3ae190SFariborz Jahanian return llvm::StringSwitch<StringRef>(Name) 527b3ae190SFariborz Jahanian .Case("amp", "&") 537b3ae190SFariborz Jahanian .Case("lt", "<") 547b3ae190SFariborz Jahanian .Case("gt", ">") 557b3ae190SFariborz Jahanian .Case("quot", "\"") 567b3ae190SFariborz Jahanian .Case("apos", "\'") 5728800da1SDmitri Gribenko // Slow path. 5828800da1SDmitri Gribenko .Default(translateHTMLNamedCharacterReferenceToUTF8(Name)); 597b3ae190SFariborz Jahanian } 607b3ae190SFariborz Jahanian 617b3ae190SFariborz Jahanian StringRef Lexer::resolveHTMLDecimalCharacterReference(StringRef Name) const { 624586df76SDmitri Gribenko unsigned CodePoint = 0; 634586df76SDmitri Gribenko for (unsigned i = 0, e = Name.size(); i != e; ++i) { 647b3ae190SFariborz Jahanian assert(isHTMLDecimalCharacterReferenceCharacter(Name[i])); 657b3ae190SFariborz Jahanian CodePoint *= 10; 667b3ae190SFariborz Jahanian CodePoint += Name[i] - '0'; 674586df76SDmitri Gribenko } 6828800da1SDmitri Gribenko return convertCodePointToUTF8(Allocator, CodePoint); 6928800da1SDmitri Gribenko } 704586df76SDmitri Gribenko 7128800da1SDmitri Gribenko StringRef Lexer::resolveHTMLHexCharacterReference(StringRef Name) const { 7228800da1SDmitri Gribenko unsigned CodePoint = 0; 7328800da1SDmitri Gribenko for (unsigned i = 0, e = Name.size(); i != e; ++i) { 7428800da1SDmitri Gribenko CodePoint *= 16; 7528800da1SDmitri Gribenko const char C = Name[i]; 7628800da1SDmitri Gribenko assert(isHTMLHexCharacterReferenceCharacter(C)); 7728800da1SDmitri Gribenko CodePoint += llvm::hexDigitValue(C); 7828800da1SDmitri Gribenko } 7928800da1SDmitri Gribenko return convertCodePointToUTF8(Allocator, CodePoint); 804586df76SDmitri Gribenko } 814586df76SDmitri Gribenko 825188c4b9SDmitri Gribenko void Lexer::skipLineStartingDecorations() { 835188c4b9SDmitri Gribenko // This function should be called only for C comments 845188c4b9SDmitri Gribenko assert(CommentState == LCS_InsideCComment); 855188c4b9SDmitri Gribenko 865188c4b9SDmitri Gribenko if (BufferPtr == CommentEnd) 875188c4b9SDmitri Gribenko return; 885188c4b9SDmitri Gribenko 895188c4b9SDmitri Gribenko switch (*BufferPtr) { 905188c4b9SDmitri Gribenko case ' ': 915188c4b9SDmitri Gribenko case '\t': 925188c4b9SDmitri Gribenko case '\f': 935188c4b9SDmitri Gribenko case '\v': { 945188c4b9SDmitri Gribenko const char *NewBufferPtr = BufferPtr; 955188c4b9SDmitri Gribenko NewBufferPtr++; 965188c4b9SDmitri Gribenko if (NewBufferPtr == CommentEnd) 975188c4b9SDmitri Gribenko return; 985188c4b9SDmitri Gribenko 995188c4b9SDmitri Gribenko char C = *NewBufferPtr; 100bcef3411SDmitri Gribenko while (isHorizontalWhitespace(C)) { 1015188c4b9SDmitri Gribenko NewBufferPtr++; 1025188c4b9SDmitri Gribenko if (NewBufferPtr == CommentEnd) 1035188c4b9SDmitri Gribenko return; 1045188c4b9SDmitri Gribenko C = *NewBufferPtr; 1055188c4b9SDmitri Gribenko } 1065188c4b9SDmitri Gribenko if (C == '*') 1075188c4b9SDmitri Gribenko BufferPtr = NewBufferPtr + 1; 1085188c4b9SDmitri Gribenko break; 1095188c4b9SDmitri Gribenko } 1105188c4b9SDmitri Gribenko case '*': 1115188c4b9SDmitri Gribenko BufferPtr++; 1125188c4b9SDmitri Gribenko break; 1135188c4b9SDmitri Gribenko } 1145188c4b9SDmitri Gribenko } 1155188c4b9SDmitri Gribenko 1165188c4b9SDmitri Gribenko namespace { 117ec92531cSDmitri Gribenko /// Returns pointer to the first newline character in the string. 1185188c4b9SDmitri Gribenko const char *findNewline(const char *BufferPtr, const char *BufferEnd) { 1195188c4b9SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 120bcef3411SDmitri Gribenko if (isVerticalWhitespace(*BufferPtr)) 1215188c4b9SDmitri Gribenko return BufferPtr; 1225188c4b9SDmitri Gribenko } 1235188c4b9SDmitri Gribenko return BufferEnd; 1245188c4b9SDmitri Gribenko } 1255188c4b9SDmitri Gribenko 1265188c4b9SDmitri Gribenko const char *skipNewline(const char *BufferPtr, const char *BufferEnd) { 1275188c4b9SDmitri Gribenko if (BufferPtr == BufferEnd) 1285188c4b9SDmitri Gribenko return BufferPtr; 1295188c4b9SDmitri Gribenko 1305188c4b9SDmitri Gribenko if (*BufferPtr == '\n') 1315188c4b9SDmitri Gribenko BufferPtr++; 1325188c4b9SDmitri Gribenko else { 1335188c4b9SDmitri Gribenko assert(*BufferPtr == '\r'); 1345188c4b9SDmitri Gribenko BufferPtr++; 1355188c4b9SDmitri Gribenko if (BufferPtr != BufferEnd && *BufferPtr == '\n') 1365188c4b9SDmitri Gribenko BufferPtr++; 1375188c4b9SDmitri Gribenko } 1385188c4b9SDmitri Gribenko return BufferPtr; 1395188c4b9SDmitri Gribenko } 1405188c4b9SDmitri Gribenko 1414586df76SDmitri Gribenko const char *skipNamedCharacterReference(const char *BufferPtr, 1424586df76SDmitri Gribenko const char *BufferEnd) { 1434586df76SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 1444586df76SDmitri Gribenko if (!isHTMLNamedCharacterReferenceCharacter(*BufferPtr)) 1454586df76SDmitri Gribenko return BufferPtr; 1464586df76SDmitri Gribenko } 1474586df76SDmitri Gribenko return BufferEnd; 1484586df76SDmitri Gribenko } 1494586df76SDmitri Gribenko 1504586df76SDmitri Gribenko const char *skipDecimalCharacterReference(const char *BufferPtr, 1514586df76SDmitri Gribenko const char *BufferEnd) { 1524586df76SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 1534586df76SDmitri Gribenko if (!isHTMLDecimalCharacterReferenceCharacter(*BufferPtr)) 1544586df76SDmitri Gribenko return BufferPtr; 1554586df76SDmitri Gribenko } 1564586df76SDmitri Gribenko return BufferEnd; 1574586df76SDmitri Gribenko } 1584586df76SDmitri Gribenko 1594586df76SDmitri Gribenko const char *skipHexCharacterReference(const char *BufferPtr, 1604586df76SDmitri Gribenko const char *BufferEnd) { 1614586df76SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 1624586df76SDmitri Gribenko if (!isHTMLHexCharacterReferenceCharacter(*BufferPtr)) 1634586df76SDmitri Gribenko return BufferPtr; 1644586df76SDmitri Gribenko } 1654586df76SDmitri Gribenko return BufferEnd; 1664586df76SDmitri Gribenko } 1674586df76SDmitri Gribenko 16817709ae8SDmitri Gribenko bool isHTMLIdentifierStartingCharacter(char C) { 169bcef3411SDmitri Gribenko return isLetter(C); 17017709ae8SDmitri Gribenko } 17117709ae8SDmitri Gribenko 1725188c4b9SDmitri Gribenko bool isHTMLIdentifierCharacter(char C) { 173bcef3411SDmitri Gribenko return isAlphanumeric(C); 1745188c4b9SDmitri Gribenko } 1755188c4b9SDmitri Gribenko 1765188c4b9SDmitri Gribenko const char *skipHTMLIdentifier(const char *BufferPtr, const char *BufferEnd) { 1775188c4b9SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 1785188c4b9SDmitri Gribenko if (!isHTMLIdentifierCharacter(*BufferPtr)) 1795188c4b9SDmitri Gribenko return BufferPtr; 1805188c4b9SDmitri Gribenko } 1815188c4b9SDmitri Gribenko return BufferEnd; 1825188c4b9SDmitri Gribenko } 1835188c4b9SDmitri Gribenko 1845188c4b9SDmitri Gribenko /// Skip HTML string quoted in single or double quotes. Escaping quotes inside 1855188c4b9SDmitri Gribenko /// string allowed. 1865188c4b9SDmitri Gribenko /// 1875188c4b9SDmitri Gribenko /// Returns pointer to closing quote. 1885188c4b9SDmitri Gribenko const char *skipHTMLQuotedString(const char *BufferPtr, const char *BufferEnd) 1895188c4b9SDmitri Gribenko { 1905188c4b9SDmitri Gribenko const char Quote = *BufferPtr; 1915188c4b9SDmitri Gribenko assert(Quote == '\"' || Quote == '\''); 1925188c4b9SDmitri Gribenko 1935188c4b9SDmitri Gribenko BufferPtr++; 1945188c4b9SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 1955188c4b9SDmitri Gribenko const char C = *BufferPtr; 1965188c4b9SDmitri Gribenko if (C == Quote && BufferPtr[-1] != '\\') 1975188c4b9SDmitri Gribenko return BufferPtr; 1985188c4b9SDmitri Gribenko } 1995188c4b9SDmitri Gribenko return BufferEnd; 2005188c4b9SDmitri Gribenko } 2015188c4b9SDmitri Gribenko 2025188c4b9SDmitri Gribenko const char *skipWhitespace(const char *BufferPtr, const char *BufferEnd) { 2035188c4b9SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 2045188c4b9SDmitri Gribenko if (!isWhitespace(*BufferPtr)) 2055188c4b9SDmitri Gribenko return BufferPtr; 2065188c4b9SDmitri Gribenko } 2075188c4b9SDmitri Gribenko return BufferEnd; 2085188c4b9SDmitri Gribenko } 2095188c4b9SDmitri Gribenko 210e4a3997dSDmitri Gribenko bool isWhitespace(const char *BufferPtr, const char *BufferEnd) { 211e4a3997dSDmitri Gribenko return skipWhitespace(BufferPtr, BufferEnd) == BufferEnd; 212e4a3997dSDmitri Gribenko } 213e4a3997dSDmitri Gribenko 214ad45ad6fSDmitri Gribenko bool isCommandNameStartCharacter(char C) { 215bcef3411SDmitri Gribenko return isLetter(C); 216ad45ad6fSDmitri Gribenko } 217ad45ad6fSDmitri Gribenko 2185188c4b9SDmitri Gribenko bool isCommandNameCharacter(char C) { 219bcef3411SDmitri Gribenko return isAlphanumeric(C); 2205188c4b9SDmitri Gribenko } 2215188c4b9SDmitri Gribenko 2225188c4b9SDmitri Gribenko const char *skipCommandName(const char *BufferPtr, const char *BufferEnd) { 2235188c4b9SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 2245188c4b9SDmitri Gribenko if (!isCommandNameCharacter(*BufferPtr)) 2255188c4b9SDmitri Gribenko return BufferPtr; 2265188c4b9SDmitri Gribenko } 2275188c4b9SDmitri Gribenko return BufferEnd; 2285188c4b9SDmitri Gribenko } 2295188c4b9SDmitri Gribenko 2305188c4b9SDmitri Gribenko /// Return the one past end pointer for BCPL comments. 2315188c4b9SDmitri Gribenko /// Handles newlines escaped with backslash or trigraph for backslahs. 2325188c4b9SDmitri Gribenko const char *findBCPLCommentEnd(const char *BufferPtr, const char *BufferEnd) { 2335188c4b9SDmitri Gribenko const char *CurPtr = BufferPtr; 2345188c4b9SDmitri Gribenko while (CurPtr != BufferEnd) { 235bcef3411SDmitri Gribenko while (!isVerticalWhitespace(*CurPtr)) { 2365188c4b9SDmitri Gribenko CurPtr++; 2375188c4b9SDmitri Gribenko if (CurPtr == BufferEnd) 2385188c4b9SDmitri Gribenko return BufferEnd; 2395188c4b9SDmitri Gribenko } 2405188c4b9SDmitri Gribenko // We found a newline, check if it is escaped. 2415188c4b9SDmitri Gribenko const char *EscapePtr = CurPtr - 1; 2425188c4b9SDmitri Gribenko while(isHorizontalWhitespace(*EscapePtr)) 2435188c4b9SDmitri Gribenko EscapePtr--; 2445188c4b9SDmitri Gribenko 2455188c4b9SDmitri Gribenko if (*EscapePtr == '\\' || 2465188c4b9SDmitri Gribenko (EscapePtr - 2 >= BufferPtr && EscapePtr[0] == '/' && 2475188c4b9SDmitri Gribenko EscapePtr[-1] == '?' && EscapePtr[-2] == '?')) { 2485188c4b9SDmitri Gribenko // We found an escaped newline. 2495188c4b9SDmitri Gribenko CurPtr = skipNewline(CurPtr, BufferEnd); 2505188c4b9SDmitri Gribenko } else 2515188c4b9SDmitri Gribenko return CurPtr; // Not an escaped newline. 2525188c4b9SDmitri Gribenko } 2535188c4b9SDmitri Gribenko return BufferEnd; 2545188c4b9SDmitri Gribenko } 2555188c4b9SDmitri Gribenko 2565188c4b9SDmitri Gribenko /// Return the one past end pointer for C comments. 2575188c4b9SDmitri Gribenko /// Very dumb, does not handle escaped newlines or trigraphs. 2585188c4b9SDmitri Gribenko const char *findCCommentEnd(const char *BufferPtr, const char *BufferEnd) { 2595188c4b9SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 2605188c4b9SDmitri Gribenko if (*BufferPtr == '*') { 2615188c4b9SDmitri Gribenko assert(BufferPtr + 1 != BufferEnd); 2625188c4b9SDmitri Gribenko if (*(BufferPtr + 1) == '/') 2635188c4b9SDmitri Gribenko return BufferPtr; 2645188c4b9SDmitri Gribenko } 2655188c4b9SDmitri Gribenko } 2665188c4b9SDmitri Gribenko llvm_unreachable("buffer end hit before '*/' was seen"); 2675188c4b9SDmitri Gribenko } 2685188c4b9SDmitri Gribenko } // unnamed namespace 2695188c4b9SDmitri Gribenko 2705188c4b9SDmitri Gribenko void Lexer::lexCommentText(Token &T) { 2715188c4b9SDmitri Gribenko assert(CommentState == LCS_InsideBCPLComment || 2725188c4b9SDmitri Gribenko CommentState == LCS_InsideCComment); 2735188c4b9SDmitri Gribenko 2745188c4b9SDmitri Gribenko switch (State) { 2755188c4b9SDmitri Gribenko case LS_Normal: 2765188c4b9SDmitri Gribenko break; 2775188c4b9SDmitri Gribenko case LS_VerbatimBlockFirstLine: 2785188c4b9SDmitri Gribenko lexVerbatimBlockFirstLine(T); 2795188c4b9SDmitri Gribenko return; 2805188c4b9SDmitri Gribenko case LS_VerbatimBlockBody: 2815188c4b9SDmitri Gribenko lexVerbatimBlockBody(T); 2825188c4b9SDmitri Gribenko return; 2831669f702SDmitri Gribenko case LS_VerbatimLineText: 2841669f702SDmitri Gribenko lexVerbatimLineText(T); 2851669f702SDmitri Gribenko return; 286e00ffc7bSDmitri Gribenko case LS_HTMLStartTag: 287e00ffc7bSDmitri Gribenko lexHTMLStartTag(T); 2885188c4b9SDmitri Gribenko return; 289e00ffc7bSDmitri Gribenko case LS_HTMLEndTag: 290e00ffc7bSDmitri Gribenko lexHTMLEndTag(T); 291ec92531cSDmitri Gribenko return; 2925188c4b9SDmitri Gribenko } 2935188c4b9SDmitri Gribenko 2945188c4b9SDmitri Gribenko assert(State == LS_Normal); 2955188c4b9SDmitri Gribenko 2965188c4b9SDmitri Gribenko const char *TokenPtr = BufferPtr; 2975188c4b9SDmitri Gribenko assert(TokenPtr < CommentEnd); 2985188c4b9SDmitri Gribenko while (TokenPtr != CommentEnd) { 2995188c4b9SDmitri Gribenko switch(*TokenPtr) { 3005188c4b9SDmitri Gribenko case '\\': 3015188c4b9SDmitri Gribenko case '@': { 302bcf7f4d3SDmitri Gribenko // Commands that start with a backslash and commands that start with 303bcf7f4d3SDmitri Gribenko // 'at' have equivalent semantics. But we keep information about the 304bcf7f4d3SDmitri Gribenko // exact syntax in AST for comments. 305bcf7f4d3SDmitri Gribenko tok::TokenKind CommandKind = 306bcf7f4d3SDmitri Gribenko (*TokenPtr == '@') ? tok::at_command : tok::backslash_command; 3075188c4b9SDmitri Gribenko TokenPtr++; 3085188c4b9SDmitri Gribenko if (TokenPtr == CommentEnd) { 3094586df76SDmitri Gribenko formTextToken(T, TokenPtr); 3105188c4b9SDmitri Gribenko return; 3115188c4b9SDmitri Gribenko } 3125188c4b9SDmitri Gribenko char C = *TokenPtr; 3135188c4b9SDmitri Gribenko switch (C) { 3145188c4b9SDmitri Gribenko default: 3155188c4b9SDmitri Gribenko break; 3165188c4b9SDmitri Gribenko 3175188c4b9SDmitri Gribenko case '\\': case '@': case '&': case '$': 3185188c4b9SDmitri Gribenko case '#': case '<': case '>': case '%': 3195188c4b9SDmitri Gribenko case '\"': case '.': case ':': 3205188c4b9SDmitri Gribenko // This is one of \\ \@ \& \$ etc escape sequences. 3215188c4b9SDmitri Gribenko TokenPtr++; 3225188c4b9SDmitri Gribenko if (C == ':' && TokenPtr != CommentEnd && *TokenPtr == ':') { 3235188c4b9SDmitri Gribenko // This is the \:: escape sequence. 3245188c4b9SDmitri Gribenko TokenPtr++; 3255188c4b9SDmitri Gribenko } 32660ddd8a1SDmitri Gribenko StringRef UnescapedText(BufferPtr + 1, TokenPtr - (BufferPtr + 1)); 3275188c4b9SDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::text); 32860ddd8a1SDmitri Gribenko T.setText(UnescapedText); 3295188c4b9SDmitri Gribenko return; 3305188c4b9SDmitri Gribenko } 3315188c4b9SDmitri Gribenko 3325188c4b9SDmitri Gribenko // Don't make zero-length commands. 333ad45ad6fSDmitri Gribenko if (!isCommandNameStartCharacter(*TokenPtr)) { 3344586df76SDmitri Gribenko formTextToken(T, TokenPtr); 3355188c4b9SDmitri Gribenko return; 3365188c4b9SDmitri Gribenko } 3375188c4b9SDmitri Gribenko 3385188c4b9SDmitri Gribenko TokenPtr = skipCommandName(TokenPtr, CommentEnd); 3395188c4b9SDmitri Gribenko unsigned Length = TokenPtr - (BufferPtr + 1); 3405188c4b9SDmitri Gribenko 3415188c4b9SDmitri Gribenko // Hardcoded support for lexing LaTeX formula commands 3425188c4b9SDmitri Gribenko // \f$ \f[ \f] \f{ \f} as a single command. 3435188c4b9SDmitri Gribenko if (Length == 1 && TokenPtr[-1] == 'f' && TokenPtr != CommentEnd) { 3445188c4b9SDmitri Gribenko C = *TokenPtr; 3455188c4b9SDmitri Gribenko if (C == '$' || C == '[' || C == ']' || C == '{' || C == '}') { 3465188c4b9SDmitri Gribenko TokenPtr++; 3475188c4b9SDmitri Gribenko Length++; 3485188c4b9SDmitri Gribenko } 3495188c4b9SDmitri Gribenko } 3505188c4b9SDmitri Gribenko 3515188c4b9SDmitri Gribenko const StringRef CommandName(BufferPtr + 1, Length); 3525188c4b9SDmitri Gribenko 3537acbf00fSDmitri Gribenko const CommandInfo *Info = Traits.getCommandInfoOrNULL(CommandName); 3547acbf00fSDmitri Gribenko if (!Info) { 3557acbf00fSDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::unknown_command); 3567acbf00fSDmitri Gribenko T.setUnknownCommandName(CommandName); 357*5b637078SFariborz Jahanian Diag(T.getLocation(), 358*5b637078SFariborz Jahanian diag::warn_unknown_comment_command_name); 3595188c4b9SDmitri Gribenko return; 3605188c4b9SDmitri Gribenko } 3617acbf00fSDmitri Gribenko if (Info->IsVerbatimBlockCommand) { 3627acbf00fSDmitri Gribenko setupAndLexVerbatimBlock(T, TokenPtr, *BufferPtr, Info); 3637acbf00fSDmitri Gribenko return; 3647acbf00fSDmitri Gribenko } 3657acbf00fSDmitri Gribenko if (Info->IsVerbatimLineCommand) { 3667acbf00fSDmitri Gribenko setupAndLexVerbatimLine(T, TokenPtr, Info); 3675188c4b9SDmitri Gribenko return; 3685188c4b9SDmitri Gribenko } 369bcf7f4d3SDmitri Gribenko formTokenWithChars(T, TokenPtr, CommandKind); 3707acbf00fSDmitri Gribenko T.setCommandID(Info->getID()); 3715188c4b9SDmitri Gribenko return; 3725188c4b9SDmitri Gribenko } 3735188c4b9SDmitri Gribenko 3744586df76SDmitri Gribenko case '&': 3754586df76SDmitri Gribenko lexHTMLCharacterReference(T); 3764586df76SDmitri Gribenko return; 3774586df76SDmitri Gribenko 3785188c4b9SDmitri Gribenko case '<': { 3795188c4b9SDmitri Gribenko TokenPtr++; 3805188c4b9SDmitri Gribenko if (TokenPtr == CommentEnd) { 3814586df76SDmitri Gribenko formTextToken(T, TokenPtr); 3825188c4b9SDmitri Gribenko return; 3835188c4b9SDmitri Gribenko } 3845188c4b9SDmitri Gribenko const char C = *TokenPtr; 38517709ae8SDmitri Gribenko if (isHTMLIdentifierStartingCharacter(C)) 386e00ffc7bSDmitri Gribenko setupAndLexHTMLStartTag(T); 3875188c4b9SDmitri Gribenko else if (C == '/') 388e00ffc7bSDmitri Gribenko setupAndLexHTMLEndTag(T); 3894586df76SDmitri Gribenko else 3904586df76SDmitri Gribenko formTextToken(T, TokenPtr); 3914586df76SDmitri Gribenko 3925188c4b9SDmitri Gribenko return; 3935188c4b9SDmitri Gribenko } 3945188c4b9SDmitri Gribenko 3955188c4b9SDmitri Gribenko case '\n': 3965188c4b9SDmitri Gribenko case '\r': 3975188c4b9SDmitri Gribenko TokenPtr = skipNewline(TokenPtr, CommentEnd); 3985188c4b9SDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::newline); 3995188c4b9SDmitri Gribenko 4005188c4b9SDmitri Gribenko if (CommentState == LCS_InsideCComment) 4015188c4b9SDmitri Gribenko skipLineStartingDecorations(); 4025188c4b9SDmitri Gribenko return; 4035188c4b9SDmitri Gribenko 4045188c4b9SDmitri Gribenko default: { 40510af67a9SDmitri Gribenko size_t End = StringRef(TokenPtr, CommentEnd - TokenPtr). 40610af67a9SDmitri Gribenko find_first_of("\n\r\\@&<"); 40710af67a9SDmitri Gribenko if (End != StringRef::npos) 40810af67a9SDmitri Gribenko TokenPtr += End; 40910af67a9SDmitri Gribenko else 41010af67a9SDmitri Gribenko TokenPtr = CommentEnd; 4114586df76SDmitri Gribenko formTextToken(T, TokenPtr); 4125188c4b9SDmitri Gribenko return; 4135188c4b9SDmitri Gribenko } 4145188c4b9SDmitri Gribenko } 4155188c4b9SDmitri Gribenko } 4165188c4b9SDmitri Gribenko } 4175188c4b9SDmitri Gribenko 4185188c4b9SDmitri Gribenko void Lexer::setupAndLexVerbatimBlock(Token &T, 4195188c4b9SDmitri Gribenko const char *TextBegin, 4207acbf00fSDmitri Gribenko char Marker, const CommandInfo *Info) { 4217acbf00fSDmitri Gribenko assert(Info->IsVerbatimBlockCommand); 4227acbf00fSDmitri Gribenko 4235188c4b9SDmitri Gribenko VerbatimBlockEndCommandName.clear(); 4245188c4b9SDmitri Gribenko VerbatimBlockEndCommandName.append(Marker == '\\' ? "\\" : "@"); 4257acbf00fSDmitri Gribenko VerbatimBlockEndCommandName.append(Info->EndCommandName); 4265188c4b9SDmitri Gribenko 4275188c4b9SDmitri Gribenko formTokenWithChars(T, TextBegin, tok::verbatim_block_begin); 4287acbf00fSDmitri Gribenko T.setVerbatimBlockID(Info->getID()); 4295188c4b9SDmitri Gribenko 430ec92531cSDmitri Gribenko // If there is a newline following the verbatim opening command, skip the 431ec92531cSDmitri Gribenko // newline so that we don't create an tok::verbatim_block_line with empty 432ec92531cSDmitri Gribenko // text content. 433bcef3411SDmitri Gribenko if (BufferPtr != CommentEnd && 434bcef3411SDmitri Gribenko isVerticalWhitespace(*BufferPtr)) { 435ec92531cSDmitri Gribenko BufferPtr = skipNewline(BufferPtr, CommentEnd); 436ec92531cSDmitri Gribenko State = LS_VerbatimBlockBody; 437ec92531cSDmitri Gribenko return; 438ec92531cSDmitri Gribenko } 439ec92531cSDmitri Gribenko 4405188c4b9SDmitri Gribenko State = LS_VerbatimBlockFirstLine; 4415188c4b9SDmitri Gribenko } 4425188c4b9SDmitri Gribenko 4435188c4b9SDmitri Gribenko void Lexer::lexVerbatimBlockFirstLine(Token &T) { 444e4a3997dSDmitri Gribenko again: 4455188c4b9SDmitri Gribenko assert(BufferPtr < CommentEnd); 4465188c4b9SDmitri Gribenko 4475188c4b9SDmitri Gribenko // FIXME: It would be better to scan the text once, finding either the block 4485188c4b9SDmitri Gribenko // end command or newline. 4495188c4b9SDmitri Gribenko // 4505188c4b9SDmitri Gribenko // Extract current line. 4515188c4b9SDmitri Gribenko const char *Newline = findNewline(BufferPtr, CommentEnd); 4525188c4b9SDmitri Gribenko StringRef Line(BufferPtr, Newline - BufferPtr); 4535188c4b9SDmitri Gribenko 4545188c4b9SDmitri Gribenko // Look for end command in current line. 4555188c4b9SDmitri Gribenko size_t Pos = Line.find(VerbatimBlockEndCommandName); 456ec92531cSDmitri Gribenko const char *TextEnd; 4575188c4b9SDmitri Gribenko const char *NextLine; 4585188c4b9SDmitri Gribenko if (Pos == StringRef::npos) { 4595188c4b9SDmitri Gribenko // Current line is completely verbatim. 460ec92531cSDmitri Gribenko TextEnd = Newline; 4615188c4b9SDmitri Gribenko NextLine = skipNewline(Newline, CommentEnd); 4625188c4b9SDmitri Gribenko } else if (Pos == 0) { 4635188c4b9SDmitri Gribenko // Current line contains just an end command. 4645188c4b9SDmitri Gribenko const char *End = BufferPtr + VerbatimBlockEndCommandName.size(); 46560ddd8a1SDmitri Gribenko StringRef Name(BufferPtr + 1, End - (BufferPtr + 1)); 4665188c4b9SDmitri Gribenko formTokenWithChars(T, End, tok::verbatim_block_end); 4677acbf00fSDmitri Gribenko T.setVerbatimBlockID(Traits.getCommandInfo(Name)->getID()); 4685188c4b9SDmitri Gribenko State = LS_Normal; 4695188c4b9SDmitri Gribenko return; 4705188c4b9SDmitri Gribenko } else { 4715188c4b9SDmitri Gribenko // There is some text, followed by end command. Extract text first. 472ec92531cSDmitri Gribenko TextEnd = BufferPtr + Pos; 473ec92531cSDmitri Gribenko NextLine = TextEnd; 474e4a3997dSDmitri Gribenko // If there is only whitespace before end command, skip whitespace. 475e4a3997dSDmitri Gribenko if (isWhitespace(BufferPtr, TextEnd)) { 476e4a3997dSDmitri Gribenko BufferPtr = TextEnd; 477e4a3997dSDmitri Gribenko goto again; 478e4a3997dSDmitri Gribenko } 4795188c4b9SDmitri Gribenko } 4805188c4b9SDmitri Gribenko 481ec92531cSDmitri Gribenko StringRef Text(BufferPtr, TextEnd - BufferPtr); 4825188c4b9SDmitri Gribenko formTokenWithChars(T, NextLine, tok::verbatim_block_line); 48360ddd8a1SDmitri Gribenko T.setVerbatimBlockText(Text); 4845188c4b9SDmitri Gribenko 4855188c4b9SDmitri Gribenko State = LS_VerbatimBlockBody; 4865188c4b9SDmitri Gribenko } 4875188c4b9SDmitri Gribenko 4885188c4b9SDmitri Gribenko void Lexer::lexVerbatimBlockBody(Token &T) { 4895188c4b9SDmitri Gribenko assert(State == LS_VerbatimBlockBody); 4905188c4b9SDmitri Gribenko 4915188c4b9SDmitri Gribenko if (CommentState == LCS_InsideCComment) 4925188c4b9SDmitri Gribenko skipLineStartingDecorations(); 4935188c4b9SDmitri Gribenko 4945188c4b9SDmitri Gribenko lexVerbatimBlockFirstLine(T); 4955188c4b9SDmitri Gribenko } 4965188c4b9SDmitri Gribenko 4977acbf00fSDmitri Gribenko void Lexer::setupAndLexVerbatimLine(Token &T, const char *TextBegin, 4987acbf00fSDmitri Gribenko const CommandInfo *Info) { 4997acbf00fSDmitri Gribenko assert(Info->IsVerbatimLineCommand); 5001669f702SDmitri Gribenko formTokenWithChars(T, TextBegin, tok::verbatim_line_name); 5017acbf00fSDmitri Gribenko T.setVerbatimLineID(Info->getID()); 5021669f702SDmitri Gribenko 5031669f702SDmitri Gribenko State = LS_VerbatimLineText; 5041669f702SDmitri Gribenko } 5051669f702SDmitri Gribenko 5061669f702SDmitri Gribenko void Lexer::lexVerbatimLineText(Token &T) { 5071669f702SDmitri Gribenko assert(State == LS_VerbatimLineText); 5081669f702SDmitri Gribenko 5095188c4b9SDmitri Gribenko // Extract current line. 5105188c4b9SDmitri Gribenko const char *Newline = findNewline(BufferPtr, CommentEnd); 5111669f702SDmitri Gribenko const StringRef Text(BufferPtr, Newline - BufferPtr); 5121669f702SDmitri Gribenko formTokenWithChars(T, Newline, tok::verbatim_line_text); 5135188c4b9SDmitri Gribenko T.setVerbatimLineText(Text); 5141669f702SDmitri Gribenko 5151669f702SDmitri Gribenko State = LS_Normal; 5165188c4b9SDmitri Gribenko } 5175188c4b9SDmitri Gribenko 5184586df76SDmitri Gribenko void Lexer::lexHTMLCharacterReference(Token &T) { 5194586df76SDmitri Gribenko const char *TokenPtr = BufferPtr; 5204586df76SDmitri Gribenko assert(*TokenPtr == '&'); 5214586df76SDmitri Gribenko TokenPtr++; 5224586df76SDmitri Gribenko if (TokenPtr == CommentEnd) { 5234586df76SDmitri Gribenko formTextToken(T, TokenPtr); 5244586df76SDmitri Gribenko return; 5254586df76SDmitri Gribenko } 5264586df76SDmitri Gribenko const char *NamePtr; 5274586df76SDmitri Gribenko bool isNamed = false; 5284586df76SDmitri Gribenko bool isDecimal = false; 5294586df76SDmitri Gribenko char C = *TokenPtr; 5304586df76SDmitri Gribenko if (isHTMLNamedCharacterReferenceCharacter(C)) { 5314586df76SDmitri Gribenko NamePtr = TokenPtr; 5324586df76SDmitri Gribenko TokenPtr = skipNamedCharacterReference(TokenPtr, CommentEnd); 5334586df76SDmitri Gribenko isNamed = true; 5344586df76SDmitri Gribenko } else if (C == '#') { 5354586df76SDmitri Gribenko TokenPtr++; 5364586df76SDmitri Gribenko if (TokenPtr == CommentEnd) { 5374586df76SDmitri Gribenko formTextToken(T, TokenPtr); 5384586df76SDmitri Gribenko return; 5394586df76SDmitri Gribenko } 5404586df76SDmitri Gribenko C = *TokenPtr; 5414586df76SDmitri Gribenko if (isHTMLDecimalCharacterReferenceCharacter(C)) { 5424586df76SDmitri Gribenko NamePtr = TokenPtr; 5434586df76SDmitri Gribenko TokenPtr = skipDecimalCharacterReference(TokenPtr, CommentEnd); 5444586df76SDmitri Gribenko isDecimal = true; 5454586df76SDmitri Gribenko } else if (C == 'x' || C == 'X') { 5464586df76SDmitri Gribenko TokenPtr++; 5474586df76SDmitri Gribenko NamePtr = TokenPtr; 5484586df76SDmitri Gribenko TokenPtr = skipHexCharacterReference(TokenPtr, CommentEnd); 5494586df76SDmitri Gribenko } else { 5504586df76SDmitri Gribenko formTextToken(T, TokenPtr); 5514586df76SDmitri Gribenko return; 5524586df76SDmitri Gribenko } 5534586df76SDmitri Gribenko } else { 5544586df76SDmitri Gribenko formTextToken(T, TokenPtr); 5554586df76SDmitri Gribenko return; 5564586df76SDmitri Gribenko } 5574586df76SDmitri Gribenko if (NamePtr == TokenPtr || TokenPtr == CommentEnd || 5584586df76SDmitri Gribenko *TokenPtr != ';') { 5594586df76SDmitri Gribenko formTextToken(T, TokenPtr); 5604586df76SDmitri Gribenko return; 5614586df76SDmitri Gribenko } 5624586df76SDmitri Gribenko StringRef Name(NamePtr, TokenPtr - NamePtr); 5634586df76SDmitri Gribenko TokenPtr++; // Skip semicolon. 5644586df76SDmitri Gribenko StringRef Resolved; 56528800da1SDmitri Gribenko if (isNamed) 5664586df76SDmitri Gribenko Resolved = resolveHTMLNamedCharacterReference(Name); 5674586df76SDmitri Gribenko else if (isDecimal) 5684586df76SDmitri Gribenko Resolved = resolveHTMLDecimalCharacterReference(Name); 5694586df76SDmitri Gribenko else 5704586df76SDmitri Gribenko Resolved = resolveHTMLHexCharacterReference(Name); 5714586df76SDmitri Gribenko 5724586df76SDmitri Gribenko if (Resolved.empty()) { 5734586df76SDmitri Gribenko formTextToken(T, TokenPtr); 5744586df76SDmitri Gribenko return; 5754586df76SDmitri Gribenko } 5764586df76SDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::text); 5774586df76SDmitri Gribenko T.setText(Resolved); 5784586df76SDmitri Gribenko return; 5794586df76SDmitri Gribenko } 5804586df76SDmitri Gribenko 581e00ffc7bSDmitri Gribenko void Lexer::setupAndLexHTMLStartTag(Token &T) { 58217709ae8SDmitri Gribenko assert(BufferPtr[0] == '<' && 58317709ae8SDmitri Gribenko isHTMLIdentifierStartingCharacter(BufferPtr[1])); 5845188c4b9SDmitri Gribenko const char *TagNameEnd = skipHTMLIdentifier(BufferPtr + 2, CommentEnd); 58560ddd8a1SDmitri Gribenko StringRef Name(BufferPtr + 1, TagNameEnd - (BufferPtr + 1)); 586107618a6SDmitri Gribenko if (!isHTMLTagName(Name)) { 587107618a6SDmitri Gribenko formTextToken(T, TagNameEnd); 588107618a6SDmitri Gribenko return; 589107618a6SDmitri Gribenko } 590107618a6SDmitri Gribenko 591e00ffc7bSDmitri Gribenko formTokenWithChars(T, TagNameEnd, tok::html_start_tag); 592e00ffc7bSDmitri Gribenko T.setHTMLTagStartName(Name); 5935188c4b9SDmitri Gribenko 5945188c4b9SDmitri Gribenko BufferPtr = skipWhitespace(BufferPtr, CommentEnd); 5955188c4b9SDmitri Gribenko 59617709ae8SDmitri Gribenko const char C = *BufferPtr; 59717709ae8SDmitri Gribenko if (BufferPtr != CommentEnd && 598f26054f0SDmitri Gribenko (C == '>' || C == '/' || isHTMLIdentifierStartingCharacter(C))) 599e00ffc7bSDmitri Gribenko State = LS_HTMLStartTag; 6005188c4b9SDmitri Gribenko } 6015188c4b9SDmitri Gribenko 602e00ffc7bSDmitri Gribenko void Lexer::lexHTMLStartTag(Token &T) { 603e00ffc7bSDmitri Gribenko assert(State == LS_HTMLStartTag); 6045188c4b9SDmitri Gribenko 6055188c4b9SDmitri Gribenko const char *TokenPtr = BufferPtr; 6065188c4b9SDmitri Gribenko char C = *TokenPtr; 6075188c4b9SDmitri Gribenko if (isHTMLIdentifierCharacter(C)) { 6085188c4b9SDmitri Gribenko TokenPtr = skipHTMLIdentifier(TokenPtr, CommentEnd); 60960ddd8a1SDmitri Gribenko StringRef Ident(BufferPtr, TokenPtr - BufferPtr); 6105188c4b9SDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::html_ident); 61160ddd8a1SDmitri Gribenko T.setHTMLIdent(Ident); 6125188c4b9SDmitri Gribenko } else { 6135188c4b9SDmitri Gribenko switch (C) { 6145188c4b9SDmitri Gribenko case '=': 6155188c4b9SDmitri Gribenko TokenPtr++; 6165188c4b9SDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::html_equals); 6175188c4b9SDmitri Gribenko break; 6185188c4b9SDmitri Gribenko case '\"': 6195188c4b9SDmitri Gribenko case '\'': { 6205188c4b9SDmitri Gribenko const char *OpenQuote = TokenPtr; 6215188c4b9SDmitri Gribenko TokenPtr = skipHTMLQuotedString(TokenPtr, CommentEnd); 6225188c4b9SDmitri Gribenko const char *ClosingQuote = TokenPtr; 6235188c4b9SDmitri Gribenko if (TokenPtr != CommentEnd) // Skip closing quote. 6245188c4b9SDmitri Gribenko TokenPtr++; 6255188c4b9SDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::html_quoted_string); 6265188c4b9SDmitri Gribenko T.setHTMLQuotedString(StringRef(OpenQuote + 1, 6275188c4b9SDmitri Gribenko ClosingQuote - (OpenQuote + 1))); 6285188c4b9SDmitri Gribenko break; 6295188c4b9SDmitri Gribenko } 6305188c4b9SDmitri Gribenko case '>': 6315188c4b9SDmitri Gribenko TokenPtr++; 6325188c4b9SDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::html_greater); 63317709ae8SDmitri Gribenko State = LS_Normal; 63417709ae8SDmitri Gribenko return; 635f26054f0SDmitri Gribenko case '/': 636f26054f0SDmitri Gribenko TokenPtr++; 637f26054f0SDmitri Gribenko if (TokenPtr != CommentEnd && *TokenPtr == '>') { 638f26054f0SDmitri Gribenko TokenPtr++; 639f26054f0SDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::html_slash_greater); 6404586df76SDmitri Gribenko } else 6414586df76SDmitri Gribenko formTextToken(T, TokenPtr); 6424586df76SDmitri Gribenko 643f26054f0SDmitri Gribenko State = LS_Normal; 644f26054f0SDmitri Gribenko return; 6455188c4b9SDmitri Gribenko } 6465188c4b9SDmitri Gribenko } 6475188c4b9SDmitri Gribenko 6485188c4b9SDmitri Gribenko // Now look ahead and return to normal state if we don't see any HTML tokens 6495188c4b9SDmitri Gribenko // ahead. 6505188c4b9SDmitri Gribenko BufferPtr = skipWhitespace(BufferPtr, CommentEnd); 6515188c4b9SDmitri Gribenko if (BufferPtr == CommentEnd) { 6525188c4b9SDmitri Gribenko State = LS_Normal; 6535188c4b9SDmitri Gribenko return; 6545188c4b9SDmitri Gribenko } 6555188c4b9SDmitri Gribenko 6565188c4b9SDmitri Gribenko C = *BufferPtr; 65717709ae8SDmitri Gribenko if (!isHTMLIdentifierStartingCharacter(C) && 6585188c4b9SDmitri Gribenko C != '=' && C != '\"' && C != '\'' && C != '>') { 6595188c4b9SDmitri Gribenko State = LS_Normal; 6605188c4b9SDmitri Gribenko return; 6615188c4b9SDmitri Gribenko } 6625188c4b9SDmitri Gribenko } 6635188c4b9SDmitri Gribenko 664e00ffc7bSDmitri Gribenko void Lexer::setupAndLexHTMLEndTag(Token &T) { 6655188c4b9SDmitri Gribenko assert(BufferPtr[0] == '<' && BufferPtr[1] == '/'); 6665188c4b9SDmitri Gribenko 6675188c4b9SDmitri Gribenko const char *TagNameBegin = skipWhitespace(BufferPtr + 2, CommentEnd); 6685188c4b9SDmitri Gribenko const char *TagNameEnd = skipHTMLIdentifier(TagNameBegin, CommentEnd); 669107618a6SDmitri Gribenko StringRef Name(TagNameBegin, TagNameEnd - TagNameBegin); 670107618a6SDmitri Gribenko if (!isHTMLTagName(Name)) { 671107618a6SDmitri Gribenko formTextToken(T, TagNameEnd); 672107618a6SDmitri Gribenko return; 673107618a6SDmitri Gribenko } 6745188c4b9SDmitri Gribenko 6755188c4b9SDmitri Gribenko const char *End = skipWhitespace(TagNameEnd, CommentEnd); 6765188c4b9SDmitri Gribenko 677e00ffc7bSDmitri Gribenko formTokenWithChars(T, End, tok::html_end_tag); 678107618a6SDmitri Gribenko T.setHTMLTagEndName(Name); 679ec92531cSDmitri Gribenko 680ec92531cSDmitri Gribenko if (BufferPtr != CommentEnd && *BufferPtr == '>') 681e00ffc7bSDmitri Gribenko State = LS_HTMLEndTag; 682ec92531cSDmitri Gribenko } 683ec92531cSDmitri Gribenko 684e00ffc7bSDmitri Gribenko void Lexer::lexHTMLEndTag(Token &T) { 685ec92531cSDmitri Gribenko assert(BufferPtr != CommentEnd && *BufferPtr == '>'); 686ec92531cSDmitri Gribenko 687ec92531cSDmitri Gribenko formTokenWithChars(T, BufferPtr + 1, tok::html_greater); 688ec92531cSDmitri Gribenko State = LS_Normal; 6895188c4b9SDmitri Gribenko } 6905188c4b9SDmitri Gribenko 691*5b637078SFariborz Jahanian Lexer::Lexer(llvm::BumpPtrAllocator &Allocator, DiagnosticsEngine &Diags, 692*5b637078SFariborz Jahanian const CommandTraits &Traits, 6936bab9113SDmitri Gribenko SourceLocation FileLoc, 6945188c4b9SDmitri Gribenko const char *BufferStart, const char *BufferEnd): 695*5b637078SFariborz Jahanian Allocator(Allocator), Diags(Diags), Traits(Traits), 6965188c4b9SDmitri Gribenko BufferStart(BufferStart), BufferEnd(BufferEnd), 6976bab9113SDmitri Gribenko FileLoc(FileLoc), BufferPtr(BufferStart), 6985188c4b9SDmitri Gribenko CommentState(LCS_BeforeComment), State(LS_Normal) { 6995188c4b9SDmitri Gribenko } 7005188c4b9SDmitri Gribenko 7015188c4b9SDmitri Gribenko void Lexer::lex(Token &T) { 7025188c4b9SDmitri Gribenko again: 7035188c4b9SDmitri Gribenko switch (CommentState) { 7045188c4b9SDmitri Gribenko case LCS_BeforeComment: 7055188c4b9SDmitri Gribenko if (BufferPtr == BufferEnd) { 7065188c4b9SDmitri Gribenko formTokenWithChars(T, BufferPtr, tok::eof); 7075188c4b9SDmitri Gribenko return; 7085188c4b9SDmitri Gribenko } 7095188c4b9SDmitri Gribenko 7105188c4b9SDmitri Gribenko assert(*BufferPtr == '/'); 7115188c4b9SDmitri Gribenko BufferPtr++; // Skip first slash. 7125188c4b9SDmitri Gribenko switch(*BufferPtr) { 7135188c4b9SDmitri Gribenko case '/': { // BCPL comment. 7145188c4b9SDmitri Gribenko BufferPtr++; // Skip second slash. 7155188c4b9SDmitri Gribenko 7165188c4b9SDmitri Gribenko if (BufferPtr != BufferEnd) { 7175188c4b9SDmitri Gribenko // Skip Doxygen magic marker, if it is present. 7185188c4b9SDmitri Gribenko // It might be missing because of a typo //< or /*<, or because we 7195188c4b9SDmitri Gribenko // merged this non-Doxygen comment into a bunch of Doxygen comments 7205188c4b9SDmitri Gribenko // around it: /** ... */ /* ... */ /** ... */ 7215188c4b9SDmitri Gribenko const char C = *BufferPtr; 7225188c4b9SDmitri Gribenko if (C == '/' || C == '!') 7235188c4b9SDmitri Gribenko BufferPtr++; 7245188c4b9SDmitri Gribenko } 7255188c4b9SDmitri Gribenko 7265188c4b9SDmitri Gribenko // Skip less-than symbol that marks trailing comments. 7275188c4b9SDmitri Gribenko // Skip it even if the comment is not a Doxygen one, because //< and /*< 7285188c4b9SDmitri Gribenko // are frequent typos. 7295188c4b9SDmitri Gribenko if (BufferPtr != BufferEnd && *BufferPtr == '<') 7305188c4b9SDmitri Gribenko BufferPtr++; 7315188c4b9SDmitri Gribenko 7325188c4b9SDmitri Gribenko CommentState = LCS_InsideBCPLComment; 733ec92531cSDmitri Gribenko if (State != LS_VerbatimBlockBody && State != LS_VerbatimBlockFirstLine) 7345188c4b9SDmitri Gribenko State = LS_Normal; 7355188c4b9SDmitri Gribenko CommentEnd = findBCPLCommentEnd(BufferPtr, BufferEnd); 7365188c4b9SDmitri Gribenko goto again; 7375188c4b9SDmitri Gribenko } 7385188c4b9SDmitri Gribenko case '*': { // C comment. 7395188c4b9SDmitri Gribenko BufferPtr++; // Skip star. 7405188c4b9SDmitri Gribenko 7415188c4b9SDmitri Gribenko // Skip Doxygen magic marker. 7425188c4b9SDmitri Gribenko const char C = *BufferPtr; 7435188c4b9SDmitri Gribenko if ((C == '*' && *(BufferPtr + 1) != '/') || C == '!') 7445188c4b9SDmitri Gribenko BufferPtr++; 7455188c4b9SDmitri Gribenko 7465188c4b9SDmitri Gribenko // Skip less-than symbol that marks trailing comments. 7475188c4b9SDmitri Gribenko if (BufferPtr != BufferEnd && *BufferPtr == '<') 7485188c4b9SDmitri Gribenko BufferPtr++; 7495188c4b9SDmitri Gribenko 7505188c4b9SDmitri Gribenko CommentState = LCS_InsideCComment; 7515188c4b9SDmitri Gribenko State = LS_Normal; 7525188c4b9SDmitri Gribenko CommentEnd = findCCommentEnd(BufferPtr, BufferEnd); 7535188c4b9SDmitri Gribenko goto again; 7545188c4b9SDmitri Gribenko } 7555188c4b9SDmitri Gribenko default: 7565188c4b9SDmitri Gribenko llvm_unreachable("second character of comment should be '/' or '*'"); 7575188c4b9SDmitri Gribenko } 7585188c4b9SDmitri Gribenko 7595188c4b9SDmitri Gribenko case LCS_BetweenComments: { 7605188c4b9SDmitri Gribenko // Consecutive comments are extracted only if there is only whitespace 7615188c4b9SDmitri Gribenko // between them. So we can search for the start of the next comment. 7625188c4b9SDmitri Gribenko const char *EndWhitespace = BufferPtr; 7635188c4b9SDmitri Gribenko while(EndWhitespace != BufferEnd && *EndWhitespace != '/') 7645188c4b9SDmitri Gribenko EndWhitespace++; 7655188c4b9SDmitri Gribenko 7665188c4b9SDmitri Gribenko // Turn any whitespace between comments (and there is only whitespace 76717709ae8SDmitri Gribenko // between them -- guaranteed by comment extraction) into a newline. We 76817709ae8SDmitri Gribenko // have two newlines between C comments in total (first one was synthesized 76917709ae8SDmitri Gribenko // after a comment). 7705188c4b9SDmitri Gribenko formTokenWithChars(T, EndWhitespace, tok::newline); 7715188c4b9SDmitri Gribenko 7725188c4b9SDmitri Gribenko CommentState = LCS_BeforeComment; 7735188c4b9SDmitri Gribenko break; 7745188c4b9SDmitri Gribenko } 7755188c4b9SDmitri Gribenko 7765188c4b9SDmitri Gribenko case LCS_InsideBCPLComment: 7775188c4b9SDmitri Gribenko case LCS_InsideCComment: 7785188c4b9SDmitri Gribenko if (BufferPtr != CommentEnd) { 7795188c4b9SDmitri Gribenko lexCommentText(T); 7805188c4b9SDmitri Gribenko break; 7815188c4b9SDmitri Gribenko } else { 7825188c4b9SDmitri Gribenko // Skip C comment closing sequence. 7835188c4b9SDmitri Gribenko if (CommentState == LCS_InsideCComment) { 7845188c4b9SDmitri Gribenko assert(BufferPtr[0] == '*' && BufferPtr[1] == '/'); 7855188c4b9SDmitri Gribenko BufferPtr += 2; 7865188c4b9SDmitri Gribenko assert(BufferPtr <= BufferEnd); 7875188c4b9SDmitri Gribenko 7885188c4b9SDmitri Gribenko // Synthenize newline just after the C comment, regardless if there is 7895188c4b9SDmitri Gribenko // actually a newline. 7905188c4b9SDmitri Gribenko formTokenWithChars(T, BufferPtr, tok::newline); 7915188c4b9SDmitri Gribenko 7925188c4b9SDmitri Gribenko CommentState = LCS_BetweenComments; 7935188c4b9SDmitri Gribenko break; 7945188c4b9SDmitri Gribenko } else { 7955188c4b9SDmitri Gribenko // Don't synthesized a newline after BCPL comment. 7965188c4b9SDmitri Gribenko CommentState = LCS_BetweenComments; 7975188c4b9SDmitri Gribenko goto again; 7985188c4b9SDmitri Gribenko } 7995188c4b9SDmitri Gribenko } 8005188c4b9SDmitri Gribenko } 8015188c4b9SDmitri Gribenko } 8025188c4b9SDmitri Gribenko 8035188c4b9SDmitri Gribenko StringRef Lexer::getSpelling(const Token &Tok, 8045188c4b9SDmitri Gribenko const SourceManager &SourceMgr, 8055188c4b9SDmitri Gribenko bool *Invalid) const { 8065188c4b9SDmitri Gribenko SourceLocation Loc = Tok.getLocation(); 8075188c4b9SDmitri Gribenko std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedLoc(Loc); 8085188c4b9SDmitri Gribenko 8095188c4b9SDmitri Gribenko bool InvalidTemp = false; 8105188c4b9SDmitri Gribenko StringRef File = SourceMgr.getBufferData(LocInfo.first, &InvalidTemp); 8115188c4b9SDmitri Gribenko if (InvalidTemp) { 8125188c4b9SDmitri Gribenko *Invalid = true; 8135188c4b9SDmitri Gribenko return StringRef(); 8145188c4b9SDmitri Gribenko } 8155188c4b9SDmitri Gribenko 8165188c4b9SDmitri Gribenko const char *Begin = File.data() + LocInfo.second; 8175188c4b9SDmitri Gribenko return StringRef(Begin, Tok.getLength()); 8185188c4b9SDmitri Gribenko } 8195188c4b9SDmitri Gribenko 8205188c4b9SDmitri Gribenko } // end namespace comments 8215188c4b9SDmitri Gribenko } // end namespace clang 8225188c4b9SDmitri Gribenko 823