10a4f3f43SEugene Zelenko //===--- CommentLexer.cpp -------------------------------------------------===// 20a4f3f43SEugene Zelenko // 3*2946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*2946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 5*2946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60a4f3f43SEugene Zelenko // 70a4f3f43SEugene Zelenko //===----------------------------------------------------------------------===// 80a4f3f43SEugene Zelenko 95188c4b9SDmitri Gribenko #include "clang/AST/CommentLexer.h" 10ca7f80adSDmitri Gribenko #include "clang/AST/CommentCommandTraits.h" 116738e430SFariborz Jahanian #include "clang/AST/CommentDiagnostic.h" 12bcef3411SDmitri Gribenko #include "clang/Basic/CharInfo.h" 13b2e5482bSDmitri Gribenko #include "llvm/ADT/StringExtras.h" 145188c4b9SDmitri Gribenko #include "llvm/ADT/StringSwitch.h" 159feeef40SDmitri Gribenko #include "llvm/Support/ConvertUTF.h" 165188c4b9SDmitri Gribenko #include "llvm/Support/ErrorHandling.h" 175188c4b9SDmitri Gribenko 185188c4b9SDmitri Gribenko namespace clang { 195188c4b9SDmitri Gribenko namespace comments { 205188c4b9SDmitri Gribenko 215188c4b9SDmitri Gribenko void Token::dump(const Lexer &L, const SourceManager &SM) const { 225188c4b9SDmitri Gribenko llvm::errs() << "comments::Token Kind=" << Kind << " "; 233124ce72SStephen Kelly Loc.print(llvm::errs(), SM); 245188c4b9SDmitri Gribenko llvm::errs() << " " << Length << " \"" << L.getSpelling(*this, SM) << "\"\n"; 255188c4b9SDmitri Gribenko } 265188c4b9SDmitri Gribenko 2774f4d028SDmitri Gribenko static inline bool isHTMLNamedCharacterReferenceCharacter(char C) { 28bcef3411SDmitri Gribenko return isLetter(C); 294586df76SDmitri Gribenko } 304586df76SDmitri Gribenko 3174f4d028SDmitri Gribenko static inline bool isHTMLDecimalCharacterReferenceCharacter(char C) { 32bcef3411SDmitri Gribenko return isDigit(C); 334586df76SDmitri Gribenko } 344586df76SDmitri Gribenko 3574f4d028SDmitri Gribenko static inline bool isHTMLHexCharacterReferenceCharacter(char C) { 36bcef3411SDmitri Gribenko return isHexDigit(C); 374586df76SDmitri Gribenko } 38107618a6SDmitri Gribenko 3974f4d028SDmitri Gribenko static inline StringRef convertCodePointToUTF8( 4074f4d028SDmitri Gribenko llvm::BumpPtrAllocator &Allocator, 4128800da1SDmitri Gribenko unsigned CodePoint) { 424586df76SDmitri Gribenko char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT); 434586df76SDmitri Gribenko char *ResolvedPtr = Resolved; 449feeef40SDmitri Gribenko if (llvm::ConvertCodePointToUTF8(CodePoint, ResolvedPtr)) 454586df76SDmitri Gribenko return StringRef(Resolved, ResolvedPtr - Resolved); 464586df76SDmitri Gribenko else 474586df76SDmitri Gribenko return StringRef(); 484586df76SDmitri Gribenko } 494586df76SDmitri Gribenko 5074f4d028SDmitri Gribenko namespace { 5174f4d028SDmitri Gribenko 5228800da1SDmitri Gribenko #include "clang/AST/CommentHTMLTags.inc" 5328800da1SDmitri Gribenko #include "clang/AST/CommentHTMLNamedCharacterReferences.inc" 5428800da1SDmitri Gribenko 550a4f3f43SEugene Zelenko } // end anonymous namespace 567b3ae190SFariborz Jahanian 577b3ae190SFariborz Jahanian StringRef Lexer::resolveHTMLNamedCharacterReference(StringRef Name) const { 5828800da1SDmitri Gribenko // Fast path, first check a few most widely used named character references. 597b3ae190SFariborz Jahanian return llvm::StringSwitch<StringRef>(Name) 607b3ae190SFariborz Jahanian .Case("amp", "&") 617b3ae190SFariborz Jahanian .Case("lt", "<") 627b3ae190SFariborz Jahanian .Case("gt", ">") 637b3ae190SFariborz Jahanian .Case("quot", "\"") 647b3ae190SFariborz Jahanian .Case("apos", "\'") 6528800da1SDmitri Gribenko // Slow path. 6628800da1SDmitri Gribenko .Default(translateHTMLNamedCharacterReferenceToUTF8(Name)); 677b3ae190SFariborz Jahanian } 687b3ae190SFariborz Jahanian 697b3ae190SFariborz Jahanian StringRef Lexer::resolveHTMLDecimalCharacterReference(StringRef Name) const { 704586df76SDmitri Gribenko unsigned CodePoint = 0; 714586df76SDmitri Gribenko for (unsigned i = 0, e = Name.size(); i != e; ++i) { 727b3ae190SFariborz Jahanian assert(isHTMLDecimalCharacterReferenceCharacter(Name[i])); 737b3ae190SFariborz Jahanian CodePoint *= 10; 747b3ae190SFariborz Jahanian CodePoint += Name[i] - '0'; 754586df76SDmitri Gribenko } 7628800da1SDmitri Gribenko return convertCodePointToUTF8(Allocator, CodePoint); 7728800da1SDmitri Gribenko } 784586df76SDmitri Gribenko 7928800da1SDmitri Gribenko StringRef Lexer::resolveHTMLHexCharacterReference(StringRef Name) const { 8028800da1SDmitri Gribenko unsigned CodePoint = 0; 8128800da1SDmitri Gribenko for (unsigned i = 0, e = Name.size(); i != e; ++i) { 8228800da1SDmitri Gribenko CodePoint *= 16; 8328800da1SDmitri Gribenko const char C = Name[i]; 8428800da1SDmitri Gribenko assert(isHTMLHexCharacterReferenceCharacter(C)); 8528800da1SDmitri Gribenko CodePoint += llvm::hexDigitValue(C); 8628800da1SDmitri Gribenko } 8728800da1SDmitri Gribenko return convertCodePointToUTF8(Allocator, CodePoint); 884586df76SDmitri Gribenko } 894586df76SDmitri Gribenko 905188c4b9SDmitri Gribenko void Lexer::skipLineStartingDecorations() { 915188c4b9SDmitri Gribenko // This function should be called only for C comments 925188c4b9SDmitri Gribenko assert(CommentState == LCS_InsideCComment); 935188c4b9SDmitri Gribenko 945188c4b9SDmitri Gribenko if (BufferPtr == CommentEnd) 955188c4b9SDmitri Gribenko return; 965188c4b9SDmitri Gribenko 975188c4b9SDmitri Gribenko switch (*BufferPtr) { 985188c4b9SDmitri Gribenko case ' ': 995188c4b9SDmitri Gribenko case '\t': 1005188c4b9SDmitri Gribenko case '\f': 1015188c4b9SDmitri Gribenko case '\v': { 1025188c4b9SDmitri Gribenko const char *NewBufferPtr = BufferPtr; 1035188c4b9SDmitri Gribenko NewBufferPtr++; 1045188c4b9SDmitri Gribenko if (NewBufferPtr == CommentEnd) 1055188c4b9SDmitri Gribenko return; 1065188c4b9SDmitri Gribenko 1075188c4b9SDmitri Gribenko char C = *NewBufferPtr; 108bcef3411SDmitri Gribenko while (isHorizontalWhitespace(C)) { 1095188c4b9SDmitri Gribenko NewBufferPtr++; 1105188c4b9SDmitri Gribenko if (NewBufferPtr == CommentEnd) 1115188c4b9SDmitri Gribenko return; 1125188c4b9SDmitri Gribenko C = *NewBufferPtr; 1135188c4b9SDmitri Gribenko } 1145188c4b9SDmitri Gribenko if (C == '*') 1155188c4b9SDmitri Gribenko BufferPtr = NewBufferPtr + 1; 1165188c4b9SDmitri Gribenko break; 1175188c4b9SDmitri Gribenko } 1185188c4b9SDmitri Gribenko case '*': 1195188c4b9SDmitri Gribenko BufferPtr++; 1205188c4b9SDmitri Gribenko break; 1215188c4b9SDmitri Gribenko } 1225188c4b9SDmitri Gribenko } 1235188c4b9SDmitri Gribenko 1245188c4b9SDmitri Gribenko namespace { 125ec92531cSDmitri Gribenko /// Returns pointer to the first newline character in the string. 1265188c4b9SDmitri Gribenko const char *findNewline(const char *BufferPtr, const char *BufferEnd) { 1275188c4b9SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 128bcef3411SDmitri Gribenko if (isVerticalWhitespace(*BufferPtr)) 1295188c4b9SDmitri Gribenko return BufferPtr; 1305188c4b9SDmitri Gribenko } 1315188c4b9SDmitri Gribenko return BufferEnd; 1325188c4b9SDmitri Gribenko } 1335188c4b9SDmitri Gribenko 1345188c4b9SDmitri Gribenko const char *skipNewline(const char *BufferPtr, const char *BufferEnd) { 1355188c4b9SDmitri Gribenko if (BufferPtr == BufferEnd) 1365188c4b9SDmitri Gribenko return BufferPtr; 1375188c4b9SDmitri Gribenko 1385188c4b9SDmitri Gribenko if (*BufferPtr == '\n') 1395188c4b9SDmitri Gribenko BufferPtr++; 1405188c4b9SDmitri Gribenko else { 1415188c4b9SDmitri Gribenko assert(*BufferPtr == '\r'); 1425188c4b9SDmitri Gribenko BufferPtr++; 1435188c4b9SDmitri Gribenko if (BufferPtr != BufferEnd && *BufferPtr == '\n') 1445188c4b9SDmitri Gribenko BufferPtr++; 1455188c4b9SDmitri Gribenko } 1465188c4b9SDmitri Gribenko return BufferPtr; 1475188c4b9SDmitri Gribenko } 1485188c4b9SDmitri Gribenko 1494586df76SDmitri Gribenko const char *skipNamedCharacterReference(const char *BufferPtr, 1504586df76SDmitri Gribenko const char *BufferEnd) { 1514586df76SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 1524586df76SDmitri Gribenko if (!isHTMLNamedCharacterReferenceCharacter(*BufferPtr)) 1534586df76SDmitri Gribenko return BufferPtr; 1544586df76SDmitri Gribenko } 1554586df76SDmitri Gribenko return BufferEnd; 1564586df76SDmitri Gribenko } 1574586df76SDmitri Gribenko 1584586df76SDmitri Gribenko const char *skipDecimalCharacterReference(const char *BufferPtr, 1594586df76SDmitri Gribenko const char *BufferEnd) { 1604586df76SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 1614586df76SDmitri Gribenko if (!isHTMLDecimalCharacterReferenceCharacter(*BufferPtr)) 1624586df76SDmitri Gribenko return BufferPtr; 1634586df76SDmitri Gribenko } 1644586df76SDmitri Gribenko return BufferEnd; 1654586df76SDmitri Gribenko } 1664586df76SDmitri Gribenko 1674586df76SDmitri Gribenko const char *skipHexCharacterReference(const char *BufferPtr, 1684586df76SDmitri Gribenko const char *BufferEnd) { 1694586df76SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 1704586df76SDmitri Gribenko if (!isHTMLHexCharacterReferenceCharacter(*BufferPtr)) 1714586df76SDmitri Gribenko return BufferPtr; 1724586df76SDmitri Gribenko } 1734586df76SDmitri Gribenko return BufferEnd; 1744586df76SDmitri Gribenko } 1754586df76SDmitri Gribenko 17617709ae8SDmitri Gribenko bool isHTMLIdentifierStartingCharacter(char C) { 177bcef3411SDmitri Gribenko return isLetter(C); 17817709ae8SDmitri Gribenko } 17917709ae8SDmitri Gribenko 1805188c4b9SDmitri Gribenko bool isHTMLIdentifierCharacter(char C) { 181bcef3411SDmitri Gribenko return isAlphanumeric(C); 1825188c4b9SDmitri Gribenko } 1835188c4b9SDmitri Gribenko 1845188c4b9SDmitri Gribenko const char *skipHTMLIdentifier(const char *BufferPtr, const char *BufferEnd) { 1855188c4b9SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 1865188c4b9SDmitri Gribenko if (!isHTMLIdentifierCharacter(*BufferPtr)) 1875188c4b9SDmitri Gribenko return BufferPtr; 1885188c4b9SDmitri Gribenko } 1895188c4b9SDmitri Gribenko return BufferEnd; 1905188c4b9SDmitri Gribenko } 1915188c4b9SDmitri Gribenko 1925188c4b9SDmitri Gribenko /// Skip HTML string quoted in single or double quotes. Escaping quotes inside 1935188c4b9SDmitri Gribenko /// string allowed. 1945188c4b9SDmitri Gribenko /// 1955188c4b9SDmitri Gribenko /// Returns pointer to closing quote. 1965188c4b9SDmitri Gribenko const char *skipHTMLQuotedString(const char *BufferPtr, const char *BufferEnd) 1975188c4b9SDmitri Gribenko { 1985188c4b9SDmitri Gribenko const char Quote = *BufferPtr; 1995188c4b9SDmitri Gribenko assert(Quote == '\"' || Quote == '\''); 2005188c4b9SDmitri Gribenko 2015188c4b9SDmitri Gribenko BufferPtr++; 2025188c4b9SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 2035188c4b9SDmitri Gribenko const char C = *BufferPtr; 2045188c4b9SDmitri Gribenko if (C == Quote && BufferPtr[-1] != '\\') 2055188c4b9SDmitri Gribenko return BufferPtr; 2065188c4b9SDmitri Gribenko } 2075188c4b9SDmitri Gribenko return BufferEnd; 2085188c4b9SDmitri Gribenko } 2095188c4b9SDmitri Gribenko 2105188c4b9SDmitri Gribenko const char *skipWhitespace(const char *BufferPtr, const char *BufferEnd) { 2115188c4b9SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 2125188c4b9SDmitri Gribenko if (!isWhitespace(*BufferPtr)) 2135188c4b9SDmitri Gribenko return BufferPtr; 2145188c4b9SDmitri Gribenko } 2155188c4b9SDmitri Gribenko return BufferEnd; 2165188c4b9SDmitri Gribenko } 2175188c4b9SDmitri Gribenko 218e4a3997dSDmitri Gribenko bool isWhitespace(const char *BufferPtr, const char *BufferEnd) { 219e4a3997dSDmitri Gribenko return skipWhitespace(BufferPtr, BufferEnd) == BufferEnd; 220e4a3997dSDmitri Gribenko } 221e4a3997dSDmitri Gribenko 222ad45ad6fSDmitri Gribenko bool isCommandNameStartCharacter(char C) { 223bcef3411SDmitri Gribenko return isLetter(C); 224ad45ad6fSDmitri Gribenko } 225ad45ad6fSDmitri Gribenko 2265188c4b9SDmitri Gribenko bool isCommandNameCharacter(char C) { 227bcef3411SDmitri Gribenko return isAlphanumeric(C); 2285188c4b9SDmitri Gribenko } 2295188c4b9SDmitri Gribenko 2305188c4b9SDmitri Gribenko const char *skipCommandName(const char *BufferPtr, const char *BufferEnd) { 2315188c4b9SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 2325188c4b9SDmitri Gribenko if (!isCommandNameCharacter(*BufferPtr)) 2335188c4b9SDmitri Gribenko return BufferPtr; 2345188c4b9SDmitri Gribenko } 2355188c4b9SDmitri Gribenko return BufferEnd; 2365188c4b9SDmitri Gribenko } 2375188c4b9SDmitri Gribenko 2385188c4b9SDmitri Gribenko /// Return the one past end pointer for BCPL comments. 2395188c4b9SDmitri Gribenko /// Handles newlines escaped with backslash or trigraph for backslahs. 2405188c4b9SDmitri Gribenko const char *findBCPLCommentEnd(const char *BufferPtr, const char *BufferEnd) { 2415188c4b9SDmitri Gribenko const char *CurPtr = BufferPtr; 2425188c4b9SDmitri Gribenko while (CurPtr != BufferEnd) { 243bcef3411SDmitri Gribenko while (!isVerticalWhitespace(*CurPtr)) { 2445188c4b9SDmitri Gribenko CurPtr++; 2455188c4b9SDmitri Gribenko if (CurPtr == BufferEnd) 2465188c4b9SDmitri Gribenko return BufferEnd; 2475188c4b9SDmitri Gribenko } 2485188c4b9SDmitri Gribenko // We found a newline, check if it is escaped. 2495188c4b9SDmitri Gribenko const char *EscapePtr = CurPtr - 1; 2505188c4b9SDmitri Gribenko while(isHorizontalWhitespace(*EscapePtr)) 2515188c4b9SDmitri Gribenko EscapePtr--; 2525188c4b9SDmitri Gribenko 2535188c4b9SDmitri Gribenko if (*EscapePtr == '\\' || 2545188c4b9SDmitri Gribenko (EscapePtr - 2 >= BufferPtr && EscapePtr[0] == '/' && 2555188c4b9SDmitri Gribenko EscapePtr[-1] == '?' && EscapePtr[-2] == '?')) { 2565188c4b9SDmitri Gribenko // We found an escaped newline. 2575188c4b9SDmitri Gribenko CurPtr = skipNewline(CurPtr, BufferEnd); 2585188c4b9SDmitri Gribenko } else 2595188c4b9SDmitri Gribenko return CurPtr; // Not an escaped newline. 2605188c4b9SDmitri Gribenko } 2615188c4b9SDmitri Gribenko return BufferEnd; 2625188c4b9SDmitri Gribenko } 2635188c4b9SDmitri Gribenko 2645188c4b9SDmitri Gribenko /// Return the one past end pointer for C comments. 2655188c4b9SDmitri Gribenko /// Very dumb, does not handle escaped newlines or trigraphs. 2665188c4b9SDmitri Gribenko const char *findCCommentEnd(const char *BufferPtr, const char *BufferEnd) { 2675188c4b9SDmitri Gribenko for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 2685188c4b9SDmitri Gribenko if (*BufferPtr == '*') { 2695188c4b9SDmitri Gribenko assert(BufferPtr + 1 != BufferEnd); 2705188c4b9SDmitri Gribenko if (*(BufferPtr + 1) == '/') 2715188c4b9SDmitri Gribenko return BufferPtr; 2725188c4b9SDmitri Gribenko } 2735188c4b9SDmitri Gribenko } 2745188c4b9SDmitri Gribenko llvm_unreachable("buffer end hit before '*/' was seen"); 2755188c4b9SDmitri Gribenko } 2766c7a1666SFariborz Jahanian 2770a4f3f43SEugene Zelenko } // end anonymous namespace 2785188c4b9SDmitri Gribenko 2793ffab05fSAlp Toker void Lexer::formTokenWithChars(Token &Result, const char *TokEnd, 2803ffab05fSAlp Toker tok::TokenKind Kind) { 2813ffab05fSAlp Toker const unsigned TokLen = TokEnd - BufferPtr; 2823ffab05fSAlp Toker Result.setLocation(getSourceLocation(BufferPtr)); 2833ffab05fSAlp Toker Result.setKind(Kind); 2843ffab05fSAlp Toker Result.setLength(TokLen); 2853ffab05fSAlp Toker #ifndef NDEBUG 2863ffab05fSAlp Toker Result.TextPtr = "<UNSET>"; 2873ffab05fSAlp Toker Result.IntVal = 7; 2883ffab05fSAlp Toker #endif 2893ffab05fSAlp Toker BufferPtr = TokEnd; 2903ffab05fSAlp Toker } 2913ffab05fSAlp Toker 2925188c4b9SDmitri Gribenko void Lexer::lexCommentText(Token &T) { 2935188c4b9SDmitri Gribenko assert(CommentState == LCS_InsideBCPLComment || 2945188c4b9SDmitri Gribenko CommentState == LCS_InsideCComment); 2955188c4b9SDmitri Gribenko 2961ff7c32fSIlya Biryukov // Handles lexing non-command text, i.e. text and newline. 2971ff7c32fSIlya Biryukov auto HandleNonCommandToken = [&]() -> void { 2981ff7c32fSIlya Biryukov assert(State == LS_Normal); 2991ff7c32fSIlya Biryukov 3001ff7c32fSIlya Biryukov const char *TokenPtr = BufferPtr; 3011ff7c32fSIlya Biryukov assert(TokenPtr < CommentEnd); 3021ff7c32fSIlya Biryukov switch (*TokenPtr) { 3031ff7c32fSIlya Biryukov case '\n': 3041ff7c32fSIlya Biryukov case '\r': 3051ff7c32fSIlya Biryukov TokenPtr = skipNewline(TokenPtr, CommentEnd); 3061ff7c32fSIlya Biryukov formTokenWithChars(T, TokenPtr, tok::newline); 3071ff7c32fSIlya Biryukov 3081ff7c32fSIlya Biryukov if (CommentState == LCS_InsideCComment) 3091ff7c32fSIlya Biryukov skipLineStartingDecorations(); 3101ff7c32fSIlya Biryukov return; 3111ff7c32fSIlya Biryukov 3121ff7c32fSIlya Biryukov default: { 3131ff7c32fSIlya Biryukov StringRef TokStartSymbols = ParseCommands ? "\n\r\\@&<" : "\n\r"; 3141ff7c32fSIlya Biryukov size_t End = StringRef(TokenPtr, CommentEnd - TokenPtr) 3151ff7c32fSIlya Biryukov .find_first_of(TokStartSymbols); 3161ff7c32fSIlya Biryukov if (End != StringRef::npos) 3171ff7c32fSIlya Biryukov TokenPtr += End; 3181ff7c32fSIlya Biryukov else 3191ff7c32fSIlya Biryukov TokenPtr = CommentEnd; 3201ff7c32fSIlya Biryukov formTextToken(T, TokenPtr); 3211ff7c32fSIlya Biryukov return; 3221ff7c32fSIlya Biryukov } 3231ff7c32fSIlya Biryukov } 3241ff7c32fSIlya Biryukov }; 3251ff7c32fSIlya Biryukov 3261ff7c32fSIlya Biryukov if (!ParseCommands) 3271ff7c32fSIlya Biryukov return HandleNonCommandToken(); 3281ff7c32fSIlya Biryukov 3295188c4b9SDmitri Gribenko switch (State) { 3305188c4b9SDmitri Gribenko case LS_Normal: 3315188c4b9SDmitri Gribenko break; 3325188c4b9SDmitri Gribenko case LS_VerbatimBlockFirstLine: 3335188c4b9SDmitri Gribenko lexVerbatimBlockFirstLine(T); 3345188c4b9SDmitri Gribenko return; 3355188c4b9SDmitri Gribenko case LS_VerbatimBlockBody: 3365188c4b9SDmitri Gribenko lexVerbatimBlockBody(T); 3375188c4b9SDmitri Gribenko return; 3381669f702SDmitri Gribenko case LS_VerbatimLineText: 3391669f702SDmitri Gribenko lexVerbatimLineText(T); 3401669f702SDmitri Gribenko return; 341e00ffc7bSDmitri Gribenko case LS_HTMLStartTag: 342e00ffc7bSDmitri Gribenko lexHTMLStartTag(T); 3435188c4b9SDmitri Gribenko return; 344e00ffc7bSDmitri Gribenko case LS_HTMLEndTag: 345e00ffc7bSDmitri Gribenko lexHTMLEndTag(T); 346ec92531cSDmitri Gribenko return; 3475188c4b9SDmitri Gribenko } 3485188c4b9SDmitri Gribenko 3495188c4b9SDmitri Gribenko assert(State == LS_Normal); 3505188c4b9SDmitri Gribenko const char *TokenPtr = BufferPtr; 3515188c4b9SDmitri Gribenko assert(TokenPtr < CommentEnd); 3525188c4b9SDmitri Gribenko switch(*TokenPtr) { 3535188c4b9SDmitri Gribenko case '\\': 3545188c4b9SDmitri Gribenko case '@': { 355bcf7f4d3SDmitri Gribenko // Commands that start with a backslash and commands that start with 356bcf7f4d3SDmitri Gribenko // 'at' have equivalent semantics. But we keep information about the 357bcf7f4d3SDmitri Gribenko // exact syntax in AST for comments. 358bcf7f4d3SDmitri Gribenko tok::TokenKind CommandKind = 359bcf7f4d3SDmitri Gribenko (*TokenPtr == '@') ? tok::at_command : tok::backslash_command; 3605188c4b9SDmitri Gribenko TokenPtr++; 3615188c4b9SDmitri Gribenko if (TokenPtr == CommentEnd) { 3624586df76SDmitri Gribenko formTextToken(T, TokenPtr); 3635188c4b9SDmitri Gribenko return; 3645188c4b9SDmitri Gribenko } 3655188c4b9SDmitri Gribenko char C = *TokenPtr; 3665188c4b9SDmitri Gribenko switch (C) { 3675188c4b9SDmitri Gribenko default: 3685188c4b9SDmitri Gribenko break; 3695188c4b9SDmitri Gribenko 3705188c4b9SDmitri Gribenko case '\\': case '@': case '&': case '$': 3715188c4b9SDmitri Gribenko case '#': case '<': case '>': case '%': 3725188c4b9SDmitri Gribenko case '\"': case '.': case ':': 3735188c4b9SDmitri Gribenko // This is one of \\ \@ \& \$ etc escape sequences. 3745188c4b9SDmitri Gribenko TokenPtr++; 3755188c4b9SDmitri Gribenko if (C == ':' && TokenPtr != CommentEnd && *TokenPtr == ':') { 3765188c4b9SDmitri Gribenko // This is the \:: escape sequence. 3775188c4b9SDmitri Gribenko TokenPtr++; 3785188c4b9SDmitri Gribenko } 37960ddd8a1SDmitri Gribenko StringRef UnescapedText(BufferPtr + 1, TokenPtr - (BufferPtr + 1)); 3805188c4b9SDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::text); 38160ddd8a1SDmitri Gribenko T.setText(UnescapedText); 3825188c4b9SDmitri Gribenko return; 3835188c4b9SDmitri Gribenko } 3845188c4b9SDmitri Gribenko 3855188c4b9SDmitri Gribenko // Don't make zero-length commands. 386ad45ad6fSDmitri Gribenko if (!isCommandNameStartCharacter(*TokenPtr)) { 3874586df76SDmitri Gribenko formTextToken(T, TokenPtr); 3885188c4b9SDmitri Gribenko return; 3895188c4b9SDmitri Gribenko } 3905188c4b9SDmitri Gribenko 3915188c4b9SDmitri Gribenko TokenPtr = skipCommandName(TokenPtr, CommentEnd); 3925188c4b9SDmitri Gribenko unsigned Length = TokenPtr - (BufferPtr + 1); 3935188c4b9SDmitri Gribenko 3945188c4b9SDmitri Gribenko // Hardcoded support for lexing LaTeX formula commands 3955188c4b9SDmitri Gribenko // \f$ \f[ \f] \f{ \f} as a single command. 3965188c4b9SDmitri Gribenko if (Length == 1 && TokenPtr[-1] == 'f' && TokenPtr != CommentEnd) { 3975188c4b9SDmitri Gribenko C = *TokenPtr; 3985188c4b9SDmitri Gribenko if (C == '$' || C == '[' || C == ']' || C == '{' || C == '}') { 3995188c4b9SDmitri Gribenko TokenPtr++; 4005188c4b9SDmitri Gribenko Length++; 4015188c4b9SDmitri Gribenko } 4025188c4b9SDmitri Gribenko } 4035188c4b9SDmitri Gribenko 404bf3e3270SCraig Topper StringRef CommandName(BufferPtr + 1, Length); 4055188c4b9SDmitri Gribenko 4067acbf00fSDmitri Gribenko const CommandInfo *Info = Traits.getCommandInfoOrNULL(CommandName); 4077acbf00fSDmitri Gribenko if (!Info) { 408ebb262f7SFariborz Jahanian if ((Info = Traits.getTypoCorrectCommandInfo(CommandName))) { 4096c7a1666SFariborz Jahanian StringRef CorrectedName = Info->Name; 4102dece574SBenjamin Kramer SourceLocation Loc = getSourceLocation(BufferPtr); 41149082374SErik Verbruggen SourceLocation EndLoc = getSourceLocation(TokenPtr); 41249082374SErik Verbruggen SourceRange FullRange = SourceRange(Loc, EndLoc); 41349082374SErik Verbruggen SourceRange CommandRange(Loc.getLocWithOffset(1), EndLoc); 4142dece574SBenjamin Kramer Diag(Loc, diag::warn_correct_comment_command_name) 41549082374SErik Verbruggen << FullRange << CommandName << CorrectedName 4166c7a1666SFariborz Jahanian << FixItHint::CreateReplacement(CommandRange, CorrectedName); 4176c7a1666SFariborz Jahanian } else { 4182dece574SBenjamin Kramer formTokenWithChars(T, TokenPtr, tok::unknown_command); 4192dece574SBenjamin Kramer T.setUnknownCommandName(CommandName); 42049082374SErik Verbruggen Diag(T.getLocation(), diag::warn_unknown_comment_command_name) 42149082374SErik Verbruggen << SourceRange(T.getLocation(), T.getEndLocation()); 4225188c4b9SDmitri Gribenko return; 4235188c4b9SDmitri Gribenko } 4246c7a1666SFariborz Jahanian } 4257acbf00fSDmitri Gribenko if (Info->IsVerbatimBlockCommand) { 4267acbf00fSDmitri Gribenko setupAndLexVerbatimBlock(T, TokenPtr, *BufferPtr, Info); 4277acbf00fSDmitri Gribenko return; 4287acbf00fSDmitri Gribenko } 4297acbf00fSDmitri Gribenko if (Info->IsVerbatimLineCommand) { 4307acbf00fSDmitri Gribenko setupAndLexVerbatimLine(T, TokenPtr, Info); 4315188c4b9SDmitri Gribenko return; 4325188c4b9SDmitri Gribenko } 433bcf7f4d3SDmitri Gribenko formTokenWithChars(T, TokenPtr, CommandKind); 4347acbf00fSDmitri Gribenko T.setCommandID(Info->getID()); 4355188c4b9SDmitri Gribenko return; 4365188c4b9SDmitri Gribenko } 4375188c4b9SDmitri Gribenko 4384586df76SDmitri Gribenko case '&': 4394586df76SDmitri Gribenko lexHTMLCharacterReference(T); 4404586df76SDmitri Gribenko return; 4414586df76SDmitri Gribenko 4425188c4b9SDmitri Gribenko case '<': { 4435188c4b9SDmitri Gribenko TokenPtr++; 4445188c4b9SDmitri Gribenko if (TokenPtr == CommentEnd) { 4454586df76SDmitri Gribenko formTextToken(T, TokenPtr); 4465188c4b9SDmitri Gribenko return; 4475188c4b9SDmitri Gribenko } 4485188c4b9SDmitri Gribenko const char C = *TokenPtr; 44917709ae8SDmitri Gribenko if (isHTMLIdentifierStartingCharacter(C)) 450e00ffc7bSDmitri Gribenko setupAndLexHTMLStartTag(T); 4515188c4b9SDmitri Gribenko else if (C == '/') 452e00ffc7bSDmitri Gribenko setupAndLexHTMLEndTag(T); 4534586df76SDmitri Gribenko else 4544586df76SDmitri Gribenko formTextToken(T, TokenPtr); 4555188c4b9SDmitri Gribenko return; 4565188c4b9SDmitri Gribenko } 4575188c4b9SDmitri Gribenko 4581ff7c32fSIlya Biryukov default: 4591ff7c32fSIlya Biryukov return HandleNonCommandToken(); 4605188c4b9SDmitri Gribenko } 4615188c4b9SDmitri Gribenko } 4625188c4b9SDmitri Gribenko 4635188c4b9SDmitri Gribenko void Lexer::setupAndLexVerbatimBlock(Token &T, 4645188c4b9SDmitri Gribenko const char *TextBegin, 4657acbf00fSDmitri Gribenko char Marker, const CommandInfo *Info) { 4667acbf00fSDmitri Gribenko assert(Info->IsVerbatimBlockCommand); 4677acbf00fSDmitri Gribenko 4685188c4b9SDmitri Gribenko VerbatimBlockEndCommandName.clear(); 4695188c4b9SDmitri Gribenko VerbatimBlockEndCommandName.append(Marker == '\\' ? "\\" : "@"); 4707acbf00fSDmitri Gribenko VerbatimBlockEndCommandName.append(Info->EndCommandName); 4715188c4b9SDmitri Gribenko 4725188c4b9SDmitri Gribenko formTokenWithChars(T, TextBegin, tok::verbatim_block_begin); 4737acbf00fSDmitri Gribenko T.setVerbatimBlockID(Info->getID()); 4745188c4b9SDmitri Gribenko 475ec92531cSDmitri Gribenko // If there is a newline following the verbatim opening command, skip the 476ec92531cSDmitri Gribenko // newline so that we don't create an tok::verbatim_block_line with empty 477ec92531cSDmitri Gribenko // text content. 478bcef3411SDmitri Gribenko if (BufferPtr != CommentEnd && 479bcef3411SDmitri Gribenko isVerticalWhitespace(*BufferPtr)) { 480ec92531cSDmitri Gribenko BufferPtr = skipNewline(BufferPtr, CommentEnd); 481ec92531cSDmitri Gribenko State = LS_VerbatimBlockBody; 482ec92531cSDmitri Gribenko return; 483ec92531cSDmitri Gribenko } 484ec92531cSDmitri Gribenko 4855188c4b9SDmitri Gribenko State = LS_VerbatimBlockFirstLine; 4865188c4b9SDmitri Gribenko } 4875188c4b9SDmitri Gribenko 4885188c4b9SDmitri Gribenko void Lexer::lexVerbatimBlockFirstLine(Token &T) { 489e4a3997dSDmitri Gribenko again: 4905188c4b9SDmitri Gribenko assert(BufferPtr < CommentEnd); 4915188c4b9SDmitri Gribenko 4925188c4b9SDmitri Gribenko // FIXME: It would be better to scan the text once, finding either the block 4935188c4b9SDmitri Gribenko // end command or newline. 4945188c4b9SDmitri Gribenko // 4955188c4b9SDmitri Gribenko // Extract current line. 4965188c4b9SDmitri Gribenko const char *Newline = findNewline(BufferPtr, CommentEnd); 4975188c4b9SDmitri Gribenko StringRef Line(BufferPtr, Newline - BufferPtr); 4985188c4b9SDmitri Gribenko 4995188c4b9SDmitri Gribenko // Look for end command in current line. 5005188c4b9SDmitri Gribenko size_t Pos = Line.find(VerbatimBlockEndCommandName); 501ec92531cSDmitri Gribenko const char *TextEnd; 5025188c4b9SDmitri Gribenko const char *NextLine; 5035188c4b9SDmitri Gribenko if (Pos == StringRef::npos) { 5045188c4b9SDmitri Gribenko // Current line is completely verbatim. 505ec92531cSDmitri Gribenko TextEnd = Newline; 5065188c4b9SDmitri Gribenko NextLine = skipNewline(Newline, CommentEnd); 5075188c4b9SDmitri Gribenko } else if (Pos == 0) { 5085188c4b9SDmitri Gribenko // Current line contains just an end command. 5095188c4b9SDmitri Gribenko const char *End = BufferPtr + VerbatimBlockEndCommandName.size(); 51060ddd8a1SDmitri Gribenko StringRef Name(BufferPtr + 1, End - (BufferPtr + 1)); 5115188c4b9SDmitri Gribenko formTokenWithChars(T, End, tok::verbatim_block_end); 5127acbf00fSDmitri Gribenko T.setVerbatimBlockID(Traits.getCommandInfo(Name)->getID()); 5135188c4b9SDmitri Gribenko State = LS_Normal; 5145188c4b9SDmitri Gribenko return; 5155188c4b9SDmitri Gribenko } else { 5165188c4b9SDmitri Gribenko // There is some text, followed by end command. Extract text first. 517ec92531cSDmitri Gribenko TextEnd = BufferPtr + Pos; 518ec92531cSDmitri Gribenko NextLine = TextEnd; 519e4a3997dSDmitri Gribenko // If there is only whitespace before end command, skip whitespace. 520e4a3997dSDmitri Gribenko if (isWhitespace(BufferPtr, TextEnd)) { 521e4a3997dSDmitri Gribenko BufferPtr = TextEnd; 522e4a3997dSDmitri Gribenko goto again; 523e4a3997dSDmitri Gribenko } 5245188c4b9SDmitri Gribenko } 5255188c4b9SDmitri Gribenko 526ec92531cSDmitri Gribenko StringRef Text(BufferPtr, TextEnd - BufferPtr); 5275188c4b9SDmitri Gribenko formTokenWithChars(T, NextLine, tok::verbatim_block_line); 52860ddd8a1SDmitri Gribenko T.setVerbatimBlockText(Text); 5295188c4b9SDmitri Gribenko 5305188c4b9SDmitri Gribenko State = LS_VerbatimBlockBody; 5315188c4b9SDmitri Gribenko } 5325188c4b9SDmitri Gribenko 5335188c4b9SDmitri Gribenko void Lexer::lexVerbatimBlockBody(Token &T) { 5345188c4b9SDmitri Gribenko assert(State == LS_VerbatimBlockBody); 5355188c4b9SDmitri Gribenko 5365188c4b9SDmitri Gribenko if (CommentState == LCS_InsideCComment) 5375188c4b9SDmitri Gribenko skipLineStartingDecorations(); 5385188c4b9SDmitri Gribenko 5398b720625SDmitri Gribenko if (BufferPtr == CommentEnd) { 5408b720625SDmitri Gribenko formTokenWithChars(T, BufferPtr, tok::verbatim_block_line); 5418b720625SDmitri Gribenko T.setVerbatimBlockText(""); 5428b720625SDmitri Gribenko return; 5438b720625SDmitri Gribenko } 5448b720625SDmitri Gribenko 5455188c4b9SDmitri Gribenko lexVerbatimBlockFirstLine(T); 5465188c4b9SDmitri Gribenko } 5475188c4b9SDmitri Gribenko 5487acbf00fSDmitri Gribenko void Lexer::setupAndLexVerbatimLine(Token &T, const char *TextBegin, 5497acbf00fSDmitri Gribenko const CommandInfo *Info) { 5507acbf00fSDmitri Gribenko assert(Info->IsVerbatimLineCommand); 5511669f702SDmitri Gribenko formTokenWithChars(T, TextBegin, tok::verbatim_line_name); 5527acbf00fSDmitri Gribenko T.setVerbatimLineID(Info->getID()); 5531669f702SDmitri Gribenko 5541669f702SDmitri Gribenko State = LS_VerbatimLineText; 5551669f702SDmitri Gribenko } 5561669f702SDmitri Gribenko 5571669f702SDmitri Gribenko void Lexer::lexVerbatimLineText(Token &T) { 5581669f702SDmitri Gribenko assert(State == LS_VerbatimLineText); 5591669f702SDmitri Gribenko 5605188c4b9SDmitri Gribenko // Extract current line. 5615188c4b9SDmitri Gribenko const char *Newline = findNewline(BufferPtr, CommentEnd); 562bf3e3270SCraig Topper StringRef Text(BufferPtr, Newline - BufferPtr); 5631669f702SDmitri Gribenko formTokenWithChars(T, Newline, tok::verbatim_line_text); 5645188c4b9SDmitri Gribenko T.setVerbatimLineText(Text); 5651669f702SDmitri Gribenko 5661669f702SDmitri Gribenko State = LS_Normal; 5675188c4b9SDmitri Gribenko } 5685188c4b9SDmitri Gribenko 5694586df76SDmitri Gribenko void Lexer::lexHTMLCharacterReference(Token &T) { 5704586df76SDmitri Gribenko const char *TokenPtr = BufferPtr; 5714586df76SDmitri Gribenko assert(*TokenPtr == '&'); 5724586df76SDmitri Gribenko TokenPtr++; 5734586df76SDmitri Gribenko if (TokenPtr == CommentEnd) { 5744586df76SDmitri Gribenko formTextToken(T, TokenPtr); 5754586df76SDmitri Gribenko return; 5764586df76SDmitri Gribenko } 5774586df76SDmitri Gribenko const char *NamePtr; 5784586df76SDmitri Gribenko bool isNamed = false; 5794586df76SDmitri Gribenko bool isDecimal = false; 5804586df76SDmitri Gribenko char C = *TokenPtr; 5814586df76SDmitri Gribenko if (isHTMLNamedCharacterReferenceCharacter(C)) { 5824586df76SDmitri Gribenko NamePtr = TokenPtr; 5834586df76SDmitri Gribenko TokenPtr = skipNamedCharacterReference(TokenPtr, CommentEnd); 5844586df76SDmitri Gribenko isNamed = true; 5854586df76SDmitri Gribenko } else if (C == '#') { 5864586df76SDmitri Gribenko TokenPtr++; 5874586df76SDmitri Gribenko if (TokenPtr == CommentEnd) { 5884586df76SDmitri Gribenko formTextToken(T, TokenPtr); 5894586df76SDmitri Gribenko return; 5904586df76SDmitri Gribenko } 5914586df76SDmitri Gribenko C = *TokenPtr; 5924586df76SDmitri Gribenko if (isHTMLDecimalCharacterReferenceCharacter(C)) { 5934586df76SDmitri Gribenko NamePtr = TokenPtr; 5944586df76SDmitri Gribenko TokenPtr = skipDecimalCharacterReference(TokenPtr, CommentEnd); 5954586df76SDmitri Gribenko isDecimal = true; 5964586df76SDmitri Gribenko } else if (C == 'x' || C == 'X') { 5974586df76SDmitri Gribenko TokenPtr++; 5984586df76SDmitri Gribenko NamePtr = TokenPtr; 5994586df76SDmitri Gribenko TokenPtr = skipHexCharacterReference(TokenPtr, CommentEnd); 6004586df76SDmitri Gribenko } else { 6014586df76SDmitri Gribenko formTextToken(T, TokenPtr); 6024586df76SDmitri Gribenko return; 6034586df76SDmitri Gribenko } 6044586df76SDmitri Gribenko } else { 6054586df76SDmitri Gribenko formTextToken(T, TokenPtr); 6064586df76SDmitri Gribenko return; 6074586df76SDmitri Gribenko } 6084586df76SDmitri Gribenko if (NamePtr == TokenPtr || TokenPtr == CommentEnd || 6094586df76SDmitri Gribenko *TokenPtr != ';') { 6104586df76SDmitri Gribenko formTextToken(T, TokenPtr); 6114586df76SDmitri Gribenko return; 6124586df76SDmitri Gribenko } 6134586df76SDmitri Gribenko StringRef Name(NamePtr, TokenPtr - NamePtr); 6144586df76SDmitri Gribenko TokenPtr++; // Skip semicolon. 6154586df76SDmitri Gribenko StringRef Resolved; 61628800da1SDmitri Gribenko if (isNamed) 6174586df76SDmitri Gribenko Resolved = resolveHTMLNamedCharacterReference(Name); 6184586df76SDmitri Gribenko else if (isDecimal) 6194586df76SDmitri Gribenko Resolved = resolveHTMLDecimalCharacterReference(Name); 6204586df76SDmitri Gribenko else 6214586df76SDmitri Gribenko Resolved = resolveHTMLHexCharacterReference(Name); 6224586df76SDmitri Gribenko 6234586df76SDmitri Gribenko if (Resolved.empty()) { 6244586df76SDmitri Gribenko formTextToken(T, TokenPtr); 6254586df76SDmitri Gribenko return; 6264586df76SDmitri Gribenko } 6274586df76SDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::text); 6284586df76SDmitri Gribenko T.setText(Resolved); 6294586df76SDmitri Gribenko } 6304586df76SDmitri Gribenko 631e00ffc7bSDmitri Gribenko void Lexer::setupAndLexHTMLStartTag(Token &T) { 63217709ae8SDmitri Gribenko assert(BufferPtr[0] == '<' && 63317709ae8SDmitri Gribenko isHTMLIdentifierStartingCharacter(BufferPtr[1])); 6345188c4b9SDmitri Gribenko const char *TagNameEnd = skipHTMLIdentifier(BufferPtr + 2, CommentEnd); 63560ddd8a1SDmitri Gribenko StringRef Name(BufferPtr + 1, TagNameEnd - (BufferPtr + 1)); 636107618a6SDmitri Gribenko if (!isHTMLTagName(Name)) { 637107618a6SDmitri Gribenko formTextToken(T, TagNameEnd); 638107618a6SDmitri Gribenko return; 639107618a6SDmitri Gribenko } 640107618a6SDmitri Gribenko 641e00ffc7bSDmitri Gribenko formTokenWithChars(T, TagNameEnd, tok::html_start_tag); 642e00ffc7bSDmitri Gribenko T.setHTMLTagStartName(Name); 6435188c4b9SDmitri Gribenko 6445188c4b9SDmitri Gribenko BufferPtr = skipWhitespace(BufferPtr, CommentEnd); 6455188c4b9SDmitri Gribenko 64617709ae8SDmitri Gribenko const char C = *BufferPtr; 64717709ae8SDmitri Gribenko if (BufferPtr != CommentEnd && 648f26054f0SDmitri Gribenko (C == '>' || C == '/' || isHTMLIdentifierStartingCharacter(C))) 649e00ffc7bSDmitri Gribenko State = LS_HTMLStartTag; 6505188c4b9SDmitri Gribenko } 6515188c4b9SDmitri Gribenko 652e00ffc7bSDmitri Gribenko void Lexer::lexHTMLStartTag(Token &T) { 653e00ffc7bSDmitri Gribenko assert(State == LS_HTMLStartTag); 6545188c4b9SDmitri Gribenko 6555188c4b9SDmitri Gribenko const char *TokenPtr = BufferPtr; 6565188c4b9SDmitri Gribenko char C = *TokenPtr; 6575188c4b9SDmitri Gribenko if (isHTMLIdentifierCharacter(C)) { 6585188c4b9SDmitri Gribenko TokenPtr = skipHTMLIdentifier(TokenPtr, CommentEnd); 65960ddd8a1SDmitri Gribenko StringRef Ident(BufferPtr, TokenPtr - BufferPtr); 6605188c4b9SDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::html_ident); 66160ddd8a1SDmitri Gribenko T.setHTMLIdent(Ident); 6625188c4b9SDmitri Gribenko } else { 6635188c4b9SDmitri Gribenko switch (C) { 6645188c4b9SDmitri Gribenko case '=': 6655188c4b9SDmitri Gribenko TokenPtr++; 6665188c4b9SDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::html_equals); 6675188c4b9SDmitri Gribenko break; 6685188c4b9SDmitri Gribenko case '\"': 6695188c4b9SDmitri Gribenko case '\'': { 6705188c4b9SDmitri Gribenko const char *OpenQuote = TokenPtr; 6715188c4b9SDmitri Gribenko TokenPtr = skipHTMLQuotedString(TokenPtr, CommentEnd); 6725188c4b9SDmitri Gribenko const char *ClosingQuote = TokenPtr; 6735188c4b9SDmitri Gribenko if (TokenPtr != CommentEnd) // Skip closing quote. 6745188c4b9SDmitri Gribenko TokenPtr++; 6755188c4b9SDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::html_quoted_string); 6765188c4b9SDmitri Gribenko T.setHTMLQuotedString(StringRef(OpenQuote + 1, 6775188c4b9SDmitri Gribenko ClosingQuote - (OpenQuote + 1))); 6785188c4b9SDmitri Gribenko break; 6795188c4b9SDmitri Gribenko } 6805188c4b9SDmitri Gribenko case '>': 6815188c4b9SDmitri Gribenko TokenPtr++; 6825188c4b9SDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::html_greater); 68317709ae8SDmitri Gribenko State = LS_Normal; 68417709ae8SDmitri Gribenko return; 685f26054f0SDmitri Gribenko case '/': 686f26054f0SDmitri Gribenko TokenPtr++; 687f26054f0SDmitri Gribenko if (TokenPtr != CommentEnd && *TokenPtr == '>') { 688f26054f0SDmitri Gribenko TokenPtr++; 689f26054f0SDmitri Gribenko formTokenWithChars(T, TokenPtr, tok::html_slash_greater); 6904586df76SDmitri Gribenko } else 6914586df76SDmitri Gribenko formTextToken(T, TokenPtr); 6924586df76SDmitri Gribenko 693f26054f0SDmitri Gribenko State = LS_Normal; 694f26054f0SDmitri Gribenko return; 6955188c4b9SDmitri Gribenko } 6965188c4b9SDmitri Gribenko } 6975188c4b9SDmitri Gribenko 6985188c4b9SDmitri Gribenko // Now look ahead and return to normal state if we don't see any HTML tokens 6995188c4b9SDmitri Gribenko // ahead. 7005188c4b9SDmitri Gribenko BufferPtr = skipWhitespace(BufferPtr, CommentEnd); 7015188c4b9SDmitri Gribenko if (BufferPtr == CommentEnd) { 7025188c4b9SDmitri Gribenko State = LS_Normal; 7035188c4b9SDmitri Gribenko return; 7045188c4b9SDmitri Gribenko } 7055188c4b9SDmitri Gribenko 7065188c4b9SDmitri Gribenko C = *BufferPtr; 70717709ae8SDmitri Gribenko if (!isHTMLIdentifierStartingCharacter(C) && 7085188c4b9SDmitri Gribenko C != '=' && C != '\"' && C != '\'' && C != '>') { 7095188c4b9SDmitri Gribenko State = LS_Normal; 7105188c4b9SDmitri Gribenko return; 7115188c4b9SDmitri Gribenko } 7125188c4b9SDmitri Gribenko } 7135188c4b9SDmitri Gribenko 714e00ffc7bSDmitri Gribenko void Lexer::setupAndLexHTMLEndTag(Token &T) { 7155188c4b9SDmitri Gribenko assert(BufferPtr[0] == '<' && BufferPtr[1] == '/'); 7165188c4b9SDmitri Gribenko 7175188c4b9SDmitri Gribenko const char *TagNameBegin = skipWhitespace(BufferPtr + 2, CommentEnd); 7185188c4b9SDmitri Gribenko const char *TagNameEnd = skipHTMLIdentifier(TagNameBegin, CommentEnd); 719107618a6SDmitri Gribenko StringRef Name(TagNameBegin, TagNameEnd - TagNameBegin); 720107618a6SDmitri Gribenko if (!isHTMLTagName(Name)) { 721107618a6SDmitri Gribenko formTextToken(T, TagNameEnd); 722107618a6SDmitri Gribenko return; 723107618a6SDmitri Gribenko } 7245188c4b9SDmitri Gribenko 7255188c4b9SDmitri Gribenko const char *End = skipWhitespace(TagNameEnd, CommentEnd); 7265188c4b9SDmitri Gribenko 727e00ffc7bSDmitri Gribenko formTokenWithChars(T, End, tok::html_end_tag); 728107618a6SDmitri Gribenko T.setHTMLTagEndName(Name); 729ec92531cSDmitri Gribenko 730ec92531cSDmitri Gribenko if (BufferPtr != CommentEnd && *BufferPtr == '>') 731e00ffc7bSDmitri Gribenko State = LS_HTMLEndTag; 732ec92531cSDmitri Gribenko } 733ec92531cSDmitri Gribenko 734e00ffc7bSDmitri Gribenko void Lexer::lexHTMLEndTag(Token &T) { 735ec92531cSDmitri Gribenko assert(BufferPtr != CommentEnd && *BufferPtr == '>'); 736ec92531cSDmitri Gribenko 737ec92531cSDmitri Gribenko formTokenWithChars(T, BufferPtr + 1, tok::html_greater); 738ec92531cSDmitri Gribenko State = LS_Normal; 7395188c4b9SDmitri Gribenko } 7405188c4b9SDmitri Gribenko 7415b637078SFariborz Jahanian Lexer::Lexer(llvm::BumpPtrAllocator &Allocator, DiagnosticsEngine &Diags, 7421ff7c32fSIlya Biryukov const CommandTraits &Traits, SourceLocation FileLoc, 7431ff7c32fSIlya Biryukov const char *BufferStart, const char *BufferEnd, 7441ff7c32fSIlya Biryukov bool ParseCommands) 7451ff7c32fSIlya Biryukov : Allocator(Allocator), Diags(Diags), Traits(Traits), 7461ff7c32fSIlya Biryukov BufferStart(BufferStart), BufferEnd(BufferEnd), FileLoc(FileLoc), 7471ff7c32fSIlya Biryukov BufferPtr(BufferStart), CommentState(LCS_BeforeComment), State(LS_Normal), 7481ff7c32fSIlya Biryukov ParseCommands(ParseCommands) {} 7495188c4b9SDmitri Gribenko 7505188c4b9SDmitri Gribenko void Lexer::lex(Token &T) { 7515188c4b9SDmitri Gribenko again: 7525188c4b9SDmitri Gribenko switch (CommentState) { 7535188c4b9SDmitri Gribenko case LCS_BeforeComment: 7545188c4b9SDmitri Gribenko if (BufferPtr == BufferEnd) { 7555188c4b9SDmitri Gribenko formTokenWithChars(T, BufferPtr, tok::eof); 7565188c4b9SDmitri Gribenko return; 7575188c4b9SDmitri Gribenko } 7585188c4b9SDmitri Gribenko 7595188c4b9SDmitri Gribenko assert(*BufferPtr == '/'); 7605188c4b9SDmitri Gribenko BufferPtr++; // Skip first slash. 7615188c4b9SDmitri Gribenko switch(*BufferPtr) { 7625188c4b9SDmitri Gribenko case '/': { // BCPL comment. 7635188c4b9SDmitri Gribenko BufferPtr++; // Skip second slash. 7645188c4b9SDmitri Gribenko 7655188c4b9SDmitri Gribenko if (BufferPtr != BufferEnd) { 7665188c4b9SDmitri Gribenko // Skip Doxygen magic marker, if it is present. 7675188c4b9SDmitri Gribenko // It might be missing because of a typo //< or /*<, or because we 7685188c4b9SDmitri Gribenko // merged this non-Doxygen comment into a bunch of Doxygen comments 7695188c4b9SDmitri Gribenko // around it: /** ... */ /* ... */ /** ... */ 7705188c4b9SDmitri Gribenko const char C = *BufferPtr; 7715188c4b9SDmitri Gribenko if (C == '/' || C == '!') 7725188c4b9SDmitri Gribenko BufferPtr++; 7735188c4b9SDmitri Gribenko } 7745188c4b9SDmitri Gribenko 7755188c4b9SDmitri Gribenko // Skip less-than symbol that marks trailing comments. 7765188c4b9SDmitri Gribenko // Skip it even if the comment is not a Doxygen one, because //< and /*< 7775188c4b9SDmitri Gribenko // are frequent typos. 7785188c4b9SDmitri Gribenko if (BufferPtr != BufferEnd && *BufferPtr == '<') 7795188c4b9SDmitri Gribenko BufferPtr++; 7805188c4b9SDmitri Gribenko 7815188c4b9SDmitri Gribenko CommentState = LCS_InsideBCPLComment; 782ec92531cSDmitri Gribenko if (State != LS_VerbatimBlockBody && State != LS_VerbatimBlockFirstLine) 7835188c4b9SDmitri Gribenko State = LS_Normal; 7845188c4b9SDmitri Gribenko CommentEnd = findBCPLCommentEnd(BufferPtr, BufferEnd); 7855188c4b9SDmitri Gribenko goto again; 7865188c4b9SDmitri Gribenko } 7875188c4b9SDmitri Gribenko case '*': { // C comment. 7885188c4b9SDmitri Gribenko BufferPtr++; // Skip star. 7895188c4b9SDmitri Gribenko 7905188c4b9SDmitri Gribenko // Skip Doxygen magic marker. 7915188c4b9SDmitri Gribenko const char C = *BufferPtr; 7925188c4b9SDmitri Gribenko if ((C == '*' && *(BufferPtr + 1) != '/') || C == '!') 7935188c4b9SDmitri Gribenko BufferPtr++; 7945188c4b9SDmitri Gribenko 7955188c4b9SDmitri Gribenko // Skip less-than symbol that marks trailing comments. 7965188c4b9SDmitri Gribenko if (BufferPtr != BufferEnd && *BufferPtr == '<') 7975188c4b9SDmitri Gribenko BufferPtr++; 7985188c4b9SDmitri Gribenko 7995188c4b9SDmitri Gribenko CommentState = LCS_InsideCComment; 8005188c4b9SDmitri Gribenko State = LS_Normal; 8015188c4b9SDmitri Gribenko CommentEnd = findCCommentEnd(BufferPtr, BufferEnd); 8025188c4b9SDmitri Gribenko goto again; 8035188c4b9SDmitri Gribenko } 8045188c4b9SDmitri Gribenko default: 8055188c4b9SDmitri Gribenko llvm_unreachable("second character of comment should be '/' or '*'"); 8065188c4b9SDmitri Gribenko } 8075188c4b9SDmitri Gribenko 8085188c4b9SDmitri Gribenko case LCS_BetweenComments: { 8095188c4b9SDmitri Gribenko // Consecutive comments are extracted only if there is only whitespace 8105188c4b9SDmitri Gribenko // between them. So we can search for the start of the next comment. 8115188c4b9SDmitri Gribenko const char *EndWhitespace = BufferPtr; 8125188c4b9SDmitri Gribenko while(EndWhitespace != BufferEnd && *EndWhitespace != '/') 8135188c4b9SDmitri Gribenko EndWhitespace++; 8145188c4b9SDmitri Gribenko 8155188c4b9SDmitri Gribenko // Turn any whitespace between comments (and there is only whitespace 81617709ae8SDmitri Gribenko // between them -- guaranteed by comment extraction) into a newline. We 81717709ae8SDmitri Gribenko // have two newlines between C comments in total (first one was synthesized 81817709ae8SDmitri Gribenko // after a comment). 8195188c4b9SDmitri Gribenko formTokenWithChars(T, EndWhitespace, tok::newline); 8205188c4b9SDmitri Gribenko 8215188c4b9SDmitri Gribenko CommentState = LCS_BeforeComment; 8225188c4b9SDmitri Gribenko break; 8235188c4b9SDmitri Gribenko } 8245188c4b9SDmitri Gribenko 8255188c4b9SDmitri Gribenko case LCS_InsideBCPLComment: 8265188c4b9SDmitri Gribenko case LCS_InsideCComment: 8275188c4b9SDmitri Gribenko if (BufferPtr != CommentEnd) { 8285188c4b9SDmitri Gribenko lexCommentText(T); 8295188c4b9SDmitri Gribenko break; 8305188c4b9SDmitri Gribenko } else { 8315188c4b9SDmitri Gribenko // Skip C comment closing sequence. 8325188c4b9SDmitri Gribenko if (CommentState == LCS_InsideCComment) { 8335188c4b9SDmitri Gribenko assert(BufferPtr[0] == '*' && BufferPtr[1] == '/'); 8345188c4b9SDmitri Gribenko BufferPtr += 2; 8355188c4b9SDmitri Gribenko assert(BufferPtr <= BufferEnd); 8365188c4b9SDmitri Gribenko 8375188c4b9SDmitri Gribenko // Synthenize newline just after the C comment, regardless if there is 8385188c4b9SDmitri Gribenko // actually a newline. 8395188c4b9SDmitri Gribenko formTokenWithChars(T, BufferPtr, tok::newline); 8405188c4b9SDmitri Gribenko 8415188c4b9SDmitri Gribenko CommentState = LCS_BetweenComments; 8425188c4b9SDmitri Gribenko break; 8435188c4b9SDmitri Gribenko } else { 8445188c4b9SDmitri Gribenko // Don't synthesized a newline after BCPL comment. 8455188c4b9SDmitri Gribenko CommentState = LCS_BetweenComments; 8465188c4b9SDmitri Gribenko goto again; 8475188c4b9SDmitri Gribenko } 8485188c4b9SDmitri Gribenko } 8495188c4b9SDmitri Gribenko } 8505188c4b9SDmitri Gribenko } 8515188c4b9SDmitri Gribenko 8525188c4b9SDmitri Gribenko StringRef Lexer::getSpelling(const Token &Tok, 8535188c4b9SDmitri Gribenko const SourceManager &SourceMgr, 8545188c4b9SDmitri Gribenko bool *Invalid) const { 8555188c4b9SDmitri Gribenko SourceLocation Loc = Tok.getLocation(); 8565188c4b9SDmitri Gribenko std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedLoc(Loc); 8575188c4b9SDmitri Gribenko 8585188c4b9SDmitri Gribenko bool InvalidTemp = false; 8595188c4b9SDmitri Gribenko StringRef File = SourceMgr.getBufferData(LocInfo.first, &InvalidTemp); 8605188c4b9SDmitri Gribenko if (InvalidTemp) { 8615188c4b9SDmitri Gribenko *Invalid = true; 8625188c4b9SDmitri Gribenko return StringRef(); 8635188c4b9SDmitri Gribenko } 8645188c4b9SDmitri Gribenko 8655188c4b9SDmitri Gribenko const char *Begin = File.data() + LocInfo.second; 8665188c4b9SDmitri Gribenko return StringRef(Begin, Tok.getLength()); 8675188c4b9SDmitri Gribenko } 8685188c4b9SDmitri Gribenko 8695188c4b9SDmitri Gribenko } // end namespace comments 8705188c4b9SDmitri Gribenko } // end namespace clang 871