15188c4b9SDmitri Gribenko #include "clang/AST/CommentLexer.h"
2ca7f80adSDmitri Gribenko #include "clang/AST/CommentCommandTraits.h"
3b2e5482bSDmitri Gribenko #include "llvm/ADT/StringExtras.h"
45188c4b9SDmitri Gribenko #include "llvm/ADT/StringSwitch.h"
5*9feeef40SDmitri Gribenko #include "llvm/Support/ConvertUTF.h"
65188c4b9SDmitri Gribenko #include "llvm/Support/ErrorHandling.h"
75188c4b9SDmitri Gribenko 
85188c4b9SDmitri Gribenko namespace clang {
95188c4b9SDmitri Gribenko namespace comments {
105188c4b9SDmitri Gribenko 
115188c4b9SDmitri Gribenko void Token::dump(const Lexer &L, const SourceManager &SM) const {
125188c4b9SDmitri Gribenko   llvm::errs() << "comments::Token Kind=" << Kind << " ";
135188c4b9SDmitri Gribenko   Loc.dump(SM);
145188c4b9SDmitri Gribenko   llvm::errs() << " " << Length << " \"" << L.getSpelling(*this, SM) << "\"\n";
155188c4b9SDmitri Gribenko }
165188c4b9SDmitri Gribenko 
174586df76SDmitri Gribenko namespace {
184586df76SDmitri Gribenko bool isHTMLNamedCharacterReferenceCharacter(char C) {
194586df76SDmitri Gribenko   return (C >= 'a' && C <= 'z') ||
204586df76SDmitri Gribenko          (C >= 'A' && C <= 'Z');
214586df76SDmitri Gribenko }
224586df76SDmitri Gribenko 
234586df76SDmitri Gribenko bool isHTMLDecimalCharacterReferenceCharacter(char C) {
244586df76SDmitri Gribenko   return C >= '0' && C <= '9';
254586df76SDmitri Gribenko }
264586df76SDmitri Gribenko 
274586df76SDmitri Gribenko bool isHTMLHexCharacterReferenceCharacter(char C) {
284586df76SDmitri Gribenko   return (C >= '0' && C <= '9') ||
294586df76SDmitri Gribenko          (C >= 'a' && C <= 'f') ||
304586df76SDmitri Gribenko          (C >= 'A' && C <= 'F');
314586df76SDmitri Gribenko }
32107618a6SDmitri Gribenko 
333ca956f7SDmitri Gribenko #include "clang/AST/CommentHTMLTags.inc"
343ca956f7SDmitri Gribenko 
354586df76SDmitri Gribenko } // unnamed namespace
364586df76SDmitri Gribenko 
377b3ae190SFariborz Jahanian static unsigned getCodePoint(StringRef Name) {
384586df76SDmitri Gribenko   unsigned CodePoint = 0;
394586df76SDmitri Gribenko   for (unsigned i = 0, e = Name.size(); i != e; ++i) {
407b3ae190SFariborz Jahanian     CodePoint *= 16;
417b3ae190SFariborz Jahanian     const char C = Name[i];
427b3ae190SFariborz Jahanian     assert(isHTMLHexCharacterReferenceCharacter(C));
437b3ae190SFariborz Jahanian     CodePoint += llvm::hexDigitValue(C);
447b3ae190SFariborz Jahanian   }
457b3ae190SFariborz Jahanian   return CodePoint;
464586df76SDmitri Gribenko }
474586df76SDmitri Gribenko 
487b3ae190SFariborz Jahanian StringRef Lexer::helperResolveHTMLHexCharacterReference(unsigned CodePoint) const {
494586df76SDmitri Gribenko   char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT);
504586df76SDmitri Gribenko   char *ResolvedPtr = Resolved;
51*9feeef40SDmitri Gribenko   if (llvm::ConvertCodePointToUTF8(CodePoint, ResolvedPtr))
524586df76SDmitri Gribenko     return StringRef(Resolved, ResolvedPtr - Resolved);
534586df76SDmitri Gribenko   else
544586df76SDmitri Gribenko     return StringRef();
554586df76SDmitri Gribenko }
564586df76SDmitri Gribenko 
574586df76SDmitri Gribenko StringRef Lexer::resolveHTMLHexCharacterReference(StringRef Name) const {
587b3ae190SFariborz Jahanian   unsigned CodePoint = getCodePoint(Name);
597b3ae190SFariborz Jahanian   return helperResolveHTMLHexCharacterReference(CodePoint);
607b3ae190SFariborz Jahanian }
617b3ae190SFariborz Jahanian 
627b3ae190SFariborz Jahanian StringRef Lexer::resolveHTMLNamedCharacterReference(StringRef Name) const {
637b3ae190SFariborz Jahanian   return llvm::StringSwitch<StringRef>(Name)
647b3ae190SFariborz Jahanian       .Case("amp", "&")
657b3ae190SFariborz Jahanian       .Case("lt", "<")
667b3ae190SFariborz Jahanian       .Case("gt", ">")
677b3ae190SFariborz Jahanian       .Case("quot", "\"")
687b3ae190SFariborz Jahanian       .Case("apos", "\'")
697b3ae190SFariborz Jahanian       .Default("");
707b3ae190SFariborz Jahanian }
717b3ae190SFariborz Jahanian 
727b3ae190SFariborz Jahanian StringRef Lexer::HTMLDoxygenCharacterReference(StringRef Name) const {
737b3ae190SFariborz Jahanian   return llvm::StringSwitch<StringRef>(Name)
747b3ae190SFariborz Jahanian   .Case("copy", helperResolveHTMLHexCharacterReference(0x000A9))
757b3ae190SFariborz Jahanian   .Case("trade",        helperResolveHTMLHexCharacterReference(0x02122))
767b3ae190SFariborz Jahanian   .Case("reg",  helperResolveHTMLHexCharacterReference(0x000AE))
777b3ae190SFariborz Jahanian   .Case("lt",   helperResolveHTMLHexCharacterReference(0x0003C))
787b3ae190SFariborz Jahanian   .Case("gt",   helperResolveHTMLHexCharacterReference(0x0003C))
797b3ae190SFariborz Jahanian   .Case("amp",  helperResolveHTMLHexCharacterReference(0x00026))
807b3ae190SFariborz Jahanian   .Case("apos", helperResolveHTMLHexCharacterReference(0x00027))
817b3ae190SFariborz Jahanian   .Case("quot", helperResolveHTMLHexCharacterReference(0x00022))
827b3ae190SFariborz Jahanian   .Case("lsquo",        helperResolveHTMLHexCharacterReference(0x02018))
837b3ae190SFariborz Jahanian   .Case("rsquo",        helperResolveHTMLHexCharacterReference(0x02019))
847b3ae190SFariborz Jahanian   .Case("ldquo",        helperResolveHTMLHexCharacterReference(0x0201C))
857b3ae190SFariborz Jahanian   .Case("rdquo",        helperResolveHTMLHexCharacterReference(0x0201D))
867b3ae190SFariborz Jahanian   .Case("ndash",        helperResolveHTMLHexCharacterReference(0x02013))
877b3ae190SFariborz Jahanian   .Case("mdash",        helperResolveHTMLHexCharacterReference(0x02014))
887b3ae190SFariborz Jahanian   .Case("Auml", helperResolveHTMLHexCharacterReference(0x000C4))
897b3ae190SFariborz Jahanian   .Case("Euml", helperResolveHTMLHexCharacterReference(0x000CB))
907b3ae190SFariborz Jahanian   .Case("Iuml", helperResolveHTMLHexCharacterReference(0x000CF))
917b3ae190SFariborz Jahanian   .Case("Ouml", helperResolveHTMLHexCharacterReference(0x000D6))
927b3ae190SFariborz Jahanian   .Case("Uuml", helperResolveHTMLHexCharacterReference(0x000DC))
937b3ae190SFariborz Jahanian   .Case("Yuml", helperResolveHTMLHexCharacterReference(0x00178))
947b3ae190SFariborz Jahanian   .Case("auml", helperResolveHTMLHexCharacterReference(0x000E4))
957b3ae190SFariborz Jahanian   .Case("euml", helperResolveHTMLHexCharacterReference(0x000EB))
967b3ae190SFariborz Jahanian   .Case("iuml", helperResolveHTMLHexCharacterReference(0x000EF))
977b3ae190SFariborz Jahanian   .Case("ouml", helperResolveHTMLHexCharacterReference(0x000F6))
987b3ae190SFariborz Jahanian   .Case("uuml", helperResolveHTMLHexCharacterReference(0x000FC))
997b3ae190SFariborz Jahanian   .Case("yuml", helperResolveHTMLHexCharacterReference(0x000FF))
1007b3ae190SFariborz Jahanian   .Case("Aacute",       helperResolveHTMLHexCharacterReference(0x000C1))
1017b3ae190SFariborz Jahanian   .Case("Eacute",       helperResolveHTMLHexCharacterReference(0x000C9))
1027b3ae190SFariborz Jahanian   .Case("Iacute",       helperResolveHTMLHexCharacterReference(0x000CD))
1037b3ae190SFariborz Jahanian   .Case("Oacute",       helperResolveHTMLHexCharacterReference(0x000D3))
1047b3ae190SFariborz Jahanian   .Case("Uacute",       helperResolveHTMLHexCharacterReference(0x000DA))
1057b3ae190SFariborz Jahanian   .Case("Yacute",       helperResolveHTMLHexCharacterReference(0x000DD))
1067b3ae190SFariborz Jahanian   .Case("aacute",       helperResolveHTMLHexCharacterReference(0x000E1))
1077b3ae190SFariborz Jahanian   .Case("eacute",       helperResolveHTMLHexCharacterReference(0x000E9))
1087b3ae190SFariborz Jahanian   .Case("iacute",       helperResolveHTMLHexCharacterReference(0x000ED))
1097b3ae190SFariborz Jahanian   .Case("oacute",       helperResolveHTMLHexCharacterReference(0x000F3))
1107b3ae190SFariborz Jahanian   .Case("uacute",       helperResolveHTMLHexCharacterReference(0x000FA))
1117b3ae190SFariborz Jahanian   .Case("yacute",       helperResolveHTMLHexCharacterReference(0x000FD))
1127b3ae190SFariborz Jahanian   .Case("Agrave",       helperResolveHTMLHexCharacterReference(0x000C0))
1137b3ae190SFariborz Jahanian   .Case("Egrave",       helperResolveHTMLHexCharacterReference(0x000C8))
1147b3ae190SFariborz Jahanian   .Case("Igrave",       helperResolveHTMLHexCharacterReference(0x000CC))
1157b3ae190SFariborz Jahanian   .Case("Ograve",       helperResolveHTMLHexCharacterReference(0x000D2))
1167b3ae190SFariborz Jahanian   .Case("Ugrave",       helperResolveHTMLHexCharacterReference(0x000D9))
1177b3ae190SFariborz Jahanian   .Case("agrave",       helperResolveHTMLHexCharacterReference(0x000E0))
1187b3ae190SFariborz Jahanian   .Case("egrave",       helperResolveHTMLHexCharacterReference(0x000E8))
1197b3ae190SFariborz Jahanian   .Case("igrave",       helperResolveHTMLHexCharacterReference(0x000EC))
1207b3ae190SFariborz Jahanian   .Case("ograve",       helperResolveHTMLHexCharacterReference(0x000F2))
1217b3ae190SFariborz Jahanian   .Case("ugrave",       helperResolveHTMLHexCharacterReference(0x000F9))
1227b3ae190SFariborz Jahanian   .Case("ygrave",       helperResolveHTMLHexCharacterReference(0x01EF3))
1237b3ae190SFariborz Jahanian   .Case("Acirc",        helperResolveHTMLHexCharacterReference(0x000C2))
1247b3ae190SFariborz Jahanian   .Case("Ecirc",        helperResolveHTMLHexCharacterReference(0x000CA))
1257b3ae190SFariborz Jahanian   .Case("Icirc",        helperResolveHTMLHexCharacterReference(0x000CE))
1267b3ae190SFariborz Jahanian   .Case("Ocirc",        helperResolveHTMLHexCharacterReference(0x000D4))
1277b3ae190SFariborz Jahanian   .Case("Ucirc",        helperResolveHTMLHexCharacterReference(0x000DB))
1287b3ae190SFariborz Jahanian   .Case("acirc",        helperResolveHTMLHexCharacterReference(0x000E2))
1297b3ae190SFariborz Jahanian   .Case("ecirc",        helperResolveHTMLHexCharacterReference(0x000EA))
1307b3ae190SFariborz Jahanian   .Case("icirc",        helperResolveHTMLHexCharacterReference(0x000EE))
1317b3ae190SFariborz Jahanian   .Case("ocirc",        helperResolveHTMLHexCharacterReference(0x000F4))
1327b3ae190SFariborz Jahanian   .Case("ucirc",        helperResolveHTMLHexCharacterReference(0x000FB))
1337b3ae190SFariborz Jahanian   .Case("ycirc",        helperResolveHTMLHexCharacterReference(0x00177))
1347b3ae190SFariborz Jahanian   .Case("Atilde",       helperResolveHTMLHexCharacterReference(0x000C3))
1357b3ae190SFariborz Jahanian   .Case("Ntilde",       helperResolveHTMLHexCharacterReference(0x000D1))
1367b3ae190SFariborz Jahanian   .Case("Otilde",       helperResolveHTMLHexCharacterReference(0x000D5))
1377b3ae190SFariborz Jahanian   .Case("atilde",       helperResolveHTMLHexCharacterReference(0x000E3))
1387b3ae190SFariborz Jahanian   .Case("ntilde",       helperResolveHTMLHexCharacterReference(0x000F1))
1397b3ae190SFariborz Jahanian   .Case("otilde",       helperResolveHTMLHexCharacterReference(0x000F5))
1407b3ae190SFariborz Jahanian   .Case("szlig",        helperResolveHTMLHexCharacterReference(0x000DF))
1417b3ae190SFariborz Jahanian   .Case("ccedil",       helperResolveHTMLHexCharacterReference(0x000E7))
1427b3ae190SFariborz Jahanian   .Case("Ccedil",       helperResolveHTMLHexCharacterReference(0x000C7))
1437b3ae190SFariborz Jahanian   .Case("aring",        helperResolveHTMLHexCharacterReference(0x000E5))
1447b3ae190SFariborz Jahanian   .Case("Aring",        helperResolveHTMLHexCharacterReference(0x000C5))
1457b3ae190SFariborz Jahanian   .Case("nbsp", helperResolveHTMLHexCharacterReference(0x000A0))
1467b3ae190SFariborz Jahanian   .Case("Gamma",        helperResolveHTMLHexCharacterReference(0x00393))
1477b3ae190SFariborz Jahanian   .Case("Delta",        helperResolveHTMLHexCharacterReference(0x00394))
1487b3ae190SFariborz Jahanian   .Case("Theta",        helperResolveHTMLHexCharacterReference(0x00398))
1497b3ae190SFariborz Jahanian   .Case("Lambda",       helperResolveHTMLHexCharacterReference(0x0039B))
1507b3ae190SFariborz Jahanian   .Case("Xi",   helperResolveHTMLHexCharacterReference(0x0039E))
1517b3ae190SFariborz Jahanian   .Case("Pi",   helperResolveHTMLHexCharacterReference(0x003A0))
1527b3ae190SFariborz Jahanian   .Case("Sigma",        helperResolveHTMLHexCharacterReference(0x003A3))
1537b3ae190SFariborz Jahanian   .Case("Upsilon",      helperResolveHTMLHexCharacterReference(0x003A5))
1547b3ae190SFariborz Jahanian   .Case("Phi",  helperResolveHTMLHexCharacterReference(0x003A6))
1557b3ae190SFariborz Jahanian   .Case("Psi",  helperResolveHTMLHexCharacterReference(0x003A8))
1567b3ae190SFariborz Jahanian   .Case("Omega",        helperResolveHTMLHexCharacterReference(0x003A9))
1577b3ae190SFariborz Jahanian   .Case("alpha",        helperResolveHTMLHexCharacterReference(0x003B1))
1587b3ae190SFariborz Jahanian   .Case("beta", helperResolveHTMLHexCharacterReference(0x003B2))
1597b3ae190SFariborz Jahanian   .Case("gamma",        helperResolveHTMLHexCharacterReference(0x003B3))
1607b3ae190SFariborz Jahanian   .Case("delta",        helperResolveHTMLHexCharacterReference(0x003B4))
1617b3ae190SFariborz Jahanian   .Case("epsilon",      helperResolveHTMLHexCharacterReference(0x003B5))
1627b3ae190SFariborz Jahanian   .Case("zeta", helperResolveHTMLHexCharacterReference(0x003B6))
1637b3ae190SFariborz Jahanian   .Case("eta",  helperResolveHTMLHexCharacterReference(0x003B7))
1647b3ae190SFariborz Jahanian   .Case("theta",        helperResolveHTMLHexCharacterReference(0x003B8))
1657b3ae190SFariborz Jahanian   .Case("iota", helperResolveHTMLHexCharacterReference(0x003B9))
1667b3ae190SFariborz Jahanian   .Case("kappa",        helperResolveHTMLHexCharacterReference(0x003BA))
1677b3ae190SFariborz Jahanian   .Case("lambda",       helperResolveHTMLHexCharacterReference(0x003BB))
1687b3ae190SFariborz Jahanian   .Case("mu",   helperResolveHTMLHexCharacterReference(0x003BC))
1697b3ae190SFariborz Jahanian   .Case("nu",   helperResolveHTMLHexCharacterReference(0x003BD))
1707b3ae190SFariborz Jahanian   .Case("xi",   helperResolveHTMLHexCharacterReference(0x003BE))
1717b3ae190SFariborz Jahanian   .Case("pi",   helperResolveHTMLHexCharacterReference(0x003C0))
1727b3ae190SFariborz Jahanian   .Case("rho",  helperResolveHTMLHexCharacterReference(0x003C1))
1737b3ae190SFariborz Jahanian   .Case("sigma",        helperResolveHTMLHexCharacterReference(0x003C3))
1747b3ae190SFariborz Jahanian   .Case("tau",  helperResolveHTMLHexCharacterReference(0x003C4))
1757b3ae190SFariborz Jahanian   .Case("upsilon",      helperResolveHTMLHexCharacterReference(0x003C5))
1767b3ae190SFariborz Jahanian   .Case("phi",  helperResolveHTMLHexCharacterReference(0x003C6))
1777b3ae190SFariborz Jahanian   .Case("chi",  helperResolveHTMLHexCharacterReference(0x003C7))
1787b3ae190SFariborz Jahanian   .Case("psi",  helperResolveHTMLHexCharacterReference(0x003C8))
1797b3ae190SFariborz Jahanian   .Case("omega",        helperResolveHTMLHexCharacterReference(0x003C9))
1807b3ae190SFariborz Jahanian   .Case("sigmaf",       helperResolveHTMLHexCharacterReference(0x003C2))
1817b3ae190SFariborz Jahanian   .Case("sect", helperResolveHTMLHexCharacterReference(0x000A7))
1827b3ae190SFariborz Jahanian   .Case("deg",  helperResolveHTMLHexCharacterReference(0x000B0))
1837b3ae190SFariborz Jahanian   .Case("prime",        helperResolveHTMLHexCharacterReference(0x02032))
1847b3ae190SFariborz Jahanian   .Case("Prime",        helperResolveHTMLHexCharacterReference(0x02033))
1857b3ae190SFariborz Jahanian   .Case("infin",        helperResolveHTMLHexCharacterReference(0x0221E))
1867b3ae190SFariborz Jahanian   .Case("empty",        helperResolveHTMLHexCharacterReference(0x02205))
1877b3ae190SFariborz Jahanian   .Case("plusmn",       helperResolveHTMLHexCharacterReference(0x000B1))
1887b3ae190SFariborz Jahanian   .Case("times",        helperResolveHTMLHexCharacterReference(0x000D7))
1897b3ae190SFariborz Jahanian   .Case("minus",        helperResolveHTMLHexCharacterReference(0x02212))
1907b3ae190SFariborz Jahanian   .Case("sdot", helperResolveHTMLHexCharacterReference(0x022C5))
1917b3ae190SFariborz Jahanian   .Case("part", helperResolveHTMLHexCharacterReference(0x02202))
1927b3ae190SFariborz Jahanian   .Case("nabla",        helperResolveHTMLHexCharacterReference(0x02207))
1937b3ae190SFariborz Jahanian   .Case("radic",        helperResolveHTMLHexCharacterReference(0x0221A))
1947b3ae190SFariborz Jahanian   .Case("perp", helperResolveHTMLHexCharacterReference(0x022A5))
1957b3ae190SFariborz Jahanian   .Case("sum",  helperResolveHTMLHexCharacterReference(0x02211))
1967b3ae190SFariborz Jahanian   .Case("int",  helperResolveHTMLHexCharacterReference(0x0222B))
1977b3ae190SFariborz Jahanian   .Case("prod", helperResolveHTMLHexCharacterReference(0x0220F))
1987b3ae190SFariborz Jahanian   .Case("sim",  helperResolveHTMLHexCharacterReference(0x0223C))
1997b3ae190SFariborz Jahanian   .Case("asymp",        helperResolveHTMLHexCharacterReference(0x02248))
2007b3ae190SFariborz Jahanian   .Case("ne",   helperResolveHTMLHexCharacterReference(0x02260))
2017b3ae190SFariborz Jahanian   .Case("equiv",        helperResolveHTMLHexCharacterReference(0x02261))
2027b3ae190SFariborz Jahanian   .Case("prop", helperResolveHTMLHexCharacterReference(0x0221D))
2037b3ae190SFariborz Jahanian   .Case("le",   helperResolveHTMLHexCharacterReference(0x02264))
2047b3ae190SFariborz Jahanian   .Case("ge",   helperResolveHTMLHexCharacterReference(0x02265))
2057b3ae190SFariborz Jahanian   .Case("larr", helperResolveHTMLHexCharacterReference(0x02190))
2067b3ae190SFariborz Jahanian   .Case("rarr", helperResolveHTMLHexCharacterReference(0x02192))
2077b3ae190SFariborz Jahanian   .Case("isin", helperResolveHTMLHexCharacterReference(0x02208))
2087b3ae190SFariborz Jahanian   .Case("notin",        helperResolveHTMLHexCharacterReference(0x02209))
2097b3ae190SFariborz Jahanian   .Case("lceil",        helperResolveHTMLHexCharacterReference(0x02308))
2107b3ae190SFariborz Jahanian   .Case("rceil",        helperResolveHTMLHexCharacterReference(0x02309))
2117b3ae190SFariborz Jahanian   .Case("lfloor",       helperResolveHTMLHexCharacterReference(0x0230A))
2127b3ae190SFariborz Jahanian   .Case("rfloor",       helperResolveHTMLHexCharacterReference(0x0230B))
2137b3ae190SFariborz Jahanian   .Default("");
2147b3ae190SFariborz Jahanian }
2157b3ae190SFariborz Jahanian 
2167b3ae190SFariborz Jahanian StringRef Lexer::resolveHTMLDecimalCharacterReference(StringRef Name) const {
2174586df76SDmitri Gribenko   unsigned CodePoint = 0;
2184586df76SDmitri Gribenko   for (unsigned i = 0, e = Name.size(); i != e; ++i) {
2197b3ae190SFariborz Jahanian     assert(isHTMLDecimalCharacterReferenceCharacter(Name[i]));
2207b3ae190SFariborz Jahanian     CodePoint *= 10;
2217b3ae190SFariborz Jahanian     CodePoint += Name[i] - '0';
2224586df76SDmitri Gribenko   }
2234586df76SDmitri Gribenko 
2244586df76SDmitri Gribenko   char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT);
2254586df76SDmitri Gribenko   char *ResolvedPtr = Resolved;
226*9feeef40SDmitri Gribenko   if (llvm::ConvertCodePointToUTF8(CodePoint, ResolvedPtr))
2274586df76SDmitri Gribenko     return StringRef(Resolved, ResolvedPtr - Resolved);
2284586df76SDmitri Gribenko   else
2294586df76SDmitri Gribenko     return StringRef();
2304586df76SDmitri Gribenko }
2314586df76SDmitri Gribenko 
2325188c4b9SDmitri Gribenko void Lexer::skipLineStartingDecorations() {
2335188c4b9SDmitri Gribenko   // This function should be called only for C comments
2345188c4b9SDmitri Gribenko   assert(CommentState == LCS_InsideCComment);
2355188c4b9SDmitri Gribenko 
2365188c4b9SDmitri Gribenko   if (BufferPtr == CommentEnd)
2375188c4b9SDmitri Gribenko     return;
2385188c4b9SDmitri Gribenko 
2395188c4b9SDmitri Gribenko   switch (*BufferPtr) {
2405188c4b9SDmitri Gribenko   case ' ':
2415188c4b9SDmitri Gribenko   case '\t':
2425188c4b9SDmitri Gribenko   case '\f':
2435188c4b9SDmitri Gribenko   case '\v': {
2445188c4b9SDmitri Gribenko     const char *NewBufferPtr = BufferPtr;
2455188c4b9SDmitri Gribenko     NewBufferPtr++;
2465188c4b9SDmitri Gribenko     if (NewBufferPtr == CommentEnd)
2475188c4b9SDmitri Gribenko       return;
2485188c4b9SDmitri Gribenko 
2495188c4b9SDmitri Gribenko     char C = *NewBufferPtr;
2505188c4b9SDmitri Gribenko     while (C == ' ' || C == '\t' || C == '\f' || C == '\v') {
2515188c4b9SDmitri Gribenko       NewBufferPtr++;
2525188c4b9SDmitri Gribenko       if (NewBufferPtr == CommentEnd)
2535188c4b9SDmitri Gribenko         return;
2545188c4b9SDmitri Gribenko       C = *NewBufferPtr;
2555188c4b9SDmitri Gribenko     }
2565188c4b9SDmitri Gribenko     if (C == '*')
2575188c4b9SDmitri Gribenko       BufferPtr = NewBufferPtr + 1;
2585188c4b9SDmitri Gribenko     break;
2595188c4b9SDmitri Gribenko   }
2605188c4b9SDmitri Gribenko   case '*':
2615188c4b9SDmitri Gribenko     BufferPtr++;
2625188c4b9SDmitri Gribenko     break;
2635188c4b9SDmitri Gribenko   }
2645188c4b9SDmitri Gribenko }
2655188c4b9SDmitri Gribenko 
2665188c4b9SDmitri Gribenko namespace {
267ec92531cSDmitri Gribenko /// Returns pointer to the first newline character in the string.
2685188c4b9SDmitri Gribenko const char *findNewline(const char *BufferPtr, const char *BufferEnd) {
2695188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
2705188c4b9SDmitri Gribenko     const char C = *BufferPtr;
2715188c4b9SDmitri Gribenko     if (C == '\n' || C == '\r')
2725188c4b9SDmitri Gribenko       return BufferPtr;
2735188c4b9SDmitri Gribenko   }
2745188c4b9SDmitri Gribenko   return BufferEnd;
2755188c4b9SDmitri Gribenko }
2765188c4b9SDmitri Gribenko 
2775188c4b9SDmitri Gribenko const char *skipNewline(const char *BufferPtr, const char *BufferEnd) {
2785188c4b9SDmitri Gribenko   if (BufferPtr == BufferEnd)
2795188c4b9SDmitri Gribenko     return BufferPtr;
2805188c4b9SDmitri Gribenko 
2815188c4b9SDmitri Gribenko   if (*BufferPtr == '\n')
2825188c4b9SDmitri Gribenko     BufferPtr++;
2835188c4b9SDmitri Gribenko   else {
2845188c4b9SDmitri Gribenko     assert(*BufferPtr == '\r');
2855188c4b9SDmitri Gribenko     BufferPtr++;
2865188c4b9SDmitri Gribenko     if (BufferPtr != BufferEnd && *BufferPtr == '\n')
2875188c4b9SDmitri Gribenko       BufferPtr++;
2885188c4b9SDmitri Gribenko   }
2895188c4b9SDmitri Gribenko   return BufferPtr;
2905188c4b9SDmitri Gribenko }
2915188c4b9SDmitri Gribenko 
2924586df76SDmitri Gribenko const char *skipNamedCharacterReference(const char *BufferPtr,
2934586df76SDmitri Gribenko                                         const char *BufferEnd) {
2944586df76SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
2954586df76SDmitri Gribenko     if (!isHTMLNamedCharacterReferenceCharacter(*BufferPtr))
2964586df76SDmitri Gribenko       return BufferPtr;
2974586df76SDmitri Gribenko   }
2984586df76SDmitri Gribenko   return BufferEnd;
2994586df76SDmitri Gribenko }
3004586df76SDmitri Gribenko 
3014586df76SDmitri Gribenko const char *skipDecimalCharacterReference(const char *BufferPtr,
3024586df76SDmitri Gribenko                                           const char *BufferEnd) {
3034586df76SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
3044586df76SDmitri Gribenko     if (!isHTMLDecimalCharacterReferenceCharacter(*BufferPtr))
3054586df76SDmitri Gribenko       return BufferPtr;
3064586df76SDmitri Gribenko   }
3074586df76SDmitri Gribenko   return BufferEnd;
3084586df76SDmitri Gribenko }
3094586df76SDmitri Gribenko 
3104586df76SDmitri Gribenko const char *skipHexCharacterReference(const char *BufferPtr,
3114586df76SDmitri Gribenko                                           const char *BufferEnd) {
3124586df76SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
3134586df76SDmitri Gribenko     if (!isHTMLHexCharacterReferenceCharacter(*BufferPtr))
3144586df76SDmitri Gribenko       return BufferPtr;
3154586df76SDmitri Gribenko   }
3164586df76SDmitri Gribenko   return BufferEnd;
3174586df76SDmitri Gribenko }
3184586df76SDmitri Gribenko 
31917709ae8SDmitri Gribenko bool isHTMLIdentifierStartingCharacter(char C) {
32017709ae8SDmitri Gribenko   return (C >= 'a' && C <= 'z') ||
32117709ae8SDmitri Gribenko          (C >= 'A' && C <= 'Z');
32217709ae8SDmitri Gribenko }
32317709ae8SDmitri Gribenko 
3245188c4b9SDmitri Gribenko bool isHTMLIdentifierCharacter(char C) {
3255188c4b9SDmitri Gribenko   return (C >= 'a' && C <= 'z') ||
3265188c4b9SDmitri Gribenko          (C >= 'A' && C <= 'Z') ||
3275188c4b9SDmitri Gribenko          (C >= '0' && C <= '9');
3285188c4b9SDmitri Gribenko }
3295188c4b9SDmitri Gribenko 
3305188c4b9SDmitri Gribenko const char *skipHTMLIdentifier(const char *BufferPtr, const char *BufferEnd) {
3315188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
3325188c4b9SDmitri Gribenko     if (!isHTMLIdentifierCharacter(*BufferPtr))
3335188c4b9SDmitri Gribenko       return BufferPtr;
3345188c4b9SDmitri Gribenko   }
3355188c4b9SDmitri Gribenko   return BufferEnd;
3365188c4b9SDmitri Gribenko }
3375188c4b9SDmitri Gribenko 
3385188c4b9SDmitri Gribenko /// Skip HTML string quoted in single or double quotes.  Escaping quotes inside
3395188c4b9SDmitri Gribenko /// string allowed.
3405188c4b9SDmitri Gribenko ///
3415188c4b9SDmitri Gribenko /// Returns pointer to closing quote.
3425188c4b9SDmitri Gribenko const char *skipHTMLQuotedString(const char *BufferPtr, const char *BufferEnd)
3435188c4b9SDmitri Gribenko {
3445188c4b9SDmitri Gribenko   const char Quote = *BufferPtr;
3455188c4b9SDmitri Gribenko   assert(Quote == '\"' || Quote == '\'');
3465188c4b9SDmitri Gribenko 
3475188c4b9SDmitri Gribenko   BufferPtr++;
3485188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
3495188c4b9SDmitri Gribenko     const char C = *BufferPtr;
3505188c4b9SDmitri Gribenko     if (C == Quote && BufferPtr[-1] != '\\')
3515188c4b9SDmitri Gribenko       return BufferPtr;
3525188c4b9SDmitri Gribenko   }
3535188c4b9SDmitri Gribenko   return BufferEnd;
3545188c4b9SDmitri Gribenko }
3555188c4b9SDmitri Gribenko 
3565188c4b9SDmitri Gribenko bool isHorizontalWhitespace(char C) {
3575188c4b9SDmitri Gribenko   return C == ' ' || C == '\t' || C == '\f' || C == '\v';
3585188c4b9SDmitri Gribenko }
3595188c4b9SDmitri Gribenko 
3605188c4b9SDmitri Gribenko bool isWhitespace(char C) {
3615188c4b9SDmitri Gribenko   return C == ' ' || C == '\n' || C == '\r' ||
3625188c4b9SDmitri Gribenko          C == '\t' || C == '\f' || C == '\v';
3635188c4b9SDmitri Gribenko }
3645188c4b9SDmitri Gribenko 
3655188c4b9SDmitri Gribenko const char *skipWhitespace(const char *BufferPtr, const char *BufferEnd) {
3665188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
3675188c4b9SDmitri Gribenko     if (!isWhitespace(*BufferPtr))
3685188c4b9SDmitri Gribenko       return BufferPtr;
3695188c4b9SDmitri Gribenko   }
3705188c4b9SDmitri Gribenko   return BufferEnd;
3715188c4b9SDmitri Gribenko }
3725188c4b9SDmitri Gribenko 
373e4a3997dSDmitri Gribenko bool isWhitespace(const char *BufferPtr, const char *BufferEnd) {
374e4a3997dSDmitri Gribenko   return skipWhitespace(BufferPtr, BufferEnd) == BufferEnd;
375e4a3997dSDmitri Gribenko }
376e4a3997dSDmitri Gribenko 
377ad45ad6fSDmitri Gribenko bool isCommandNameStartCharacter(char C) {
378ad45ad6fSDmitri Gribenko   return (C >= 'a' && C <= 'z') ||
379ad45ad6fSDmitri Gribenko          (C >= 'A' && C <= 'Z');
380ad45ad6fSDmitri Gribenko }
381ad45ad6fSDmitri Gribenko 
3825188c4b9SDmitri Gribenko bool isCommandNameCharacter(char C) {
3835188c4b9SDmitri Gribenko   return (C >= 'a' && C <= 'z') ||
3845188c4b9SDmitri Gribenko          (C >= 'A' && C <= 'Z') ||
3855188c4b9SDmitri Gribenko          (C >= '0' && C <= '9');
3865188c4b9SDmitri Gribenko }
3875188c4b9SDmitri Gribenko 
3885188c4b9SDmitri Gribenko const char *skipCommandName(const char *BufferPtr, const char *BufferEnd) {
3895188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
3905188c4b9SDmitri Gribenko     if (!isCommandNameCharacter(*BufferPtr))
3915188c4b9SDmitri Gribenko       return BufferPtr;
3925188c4b9SDmitri Gribenko   }
3935188c4b9SDmitri Gribenko   return BufferEnd;
3945188c4b9SDmitri Gribenko }
3955188c4b9SDmitri Gribenko 
3965188c4b9SDmitri Gribenko /// Return the one past end pointer for BCPL comments.
3975188c4b9SDmitri Gribenko /// Handles newlines escaped with backslash or trigraph for backslahs.
3985188c4b9SDmitri Gribenko const char *findBCPLCommentEnd(const char *BufferPtr, const char *BufferEnd) {
3995188c4b9SDmitri Gribenko   const char *CurPtr = BufferPtr;
4005188c4b9SDmitri Gribenko   while (CurPtr != BufferEnd) {
4015188c4b9SDmitri Gribenko     char C = *CurPtr;
4025188c4b9SDmitri Gribenko     while (C != '\n' && C != '\r') {
4035188c4b9SDmitri Gribenko       CurPtr++;
4045188c4b9SDmitri Gribenko       if (CurPtr == BufferEnd)
4055188c4b9SDmitri Gribenko         return BufferEnd;
4065188c4b9SDmitri Gribenko       C = *CurPtr;
4075188c4b9SDmitri Gribenko     }
4085188c4b9SDmitri Gribenko     // We found a newline, check if it is escaped.
4095188c4b9SDmitri Gribenko     const char *EscapePtr = CurPtr - 1;
4105188c4b9SDmitri Gribenko     while(isHorizontalWhitespace(*EscapePtr))
4115188c4b9SDmitri Gribenko       EscapePtr--;
4125188c4b9SDmitri Gribenko 
4135188c4b9SDmitri Gribenko     if (*EscapePtr == '\\' ||
4145188c4b9SDmitri Gribenko         (EscapePtr - 2 >= BufferPtr && EscapePtr[0] == '/' &&
4155188c4b9SDmitri Gribenko          EscapePtr[-1] == '?' && EscapePtr[-2] == '?')) {
4165188c4b9SDmitri Gribenko       // We found an escaped newline.
4175188c4b9SDmitri Gribenko       CurPtr = skipNewline(CurPtr, BufferEnd);
4185188c4b9SDmitri Gribenko     } else
4195188c4b9SDmitri Gribenko       return CurPtr; // Not an escaped newline.
4205188c4b9SDmitri Gribenko   }
4215188c4b9SDmitri Gribenko   return BufferEnd;
4225188c4b9SDmitri Gribenko }
4235188c4b9SDmitri Gribenko 
4245188c4b9SDmitri Gribenko /// Return the one past end pointer for C comments.
4255188c4b9SDmitri Gribenko /// Very dumb, does not handle escaped newlines or trigraphs.
4265188c4b9SDmitri Gribenko const char *findCCommentEnd(const char *BufferPtr, const char *BufferEnd) {
4275188c4b9SDmitri Gribenko   for ( ; BufferPtr != BufferEnd; ++BufferPtr) {
4285188c4b9SDmitri Gribenko     if (*BufferPtr == '*') {
4295188c4b9SDmitri Gribenko       assert(BufferPtr + 1 != BufferEnd);
4305188c4b9SDmitri Gribenko       if (*(BufferPtr + 1) == '/')
4315188c4b9SDmitri Gribenko         return BufferPtr;
4325188c4b9SDmitri Gribenko     }
4335188c4b9SDmitri Gribenko   }
4345188c4b9SDmitri Gribenko   llvm_unreachable("buffer end hit before '*/' was seen");
4355188c4b9SDmitri Gribenko }
4365188c4b9SDmitri Gribenko } // unnamed namespace
4375188c4b9SDmitri Gribenko 
4385188c4b9SDmitri Gribenko void Lexer::lexCommentText(Token &T) {
4395188c4b9SDmitri Gribenko   assert(CommentState == LCS_InsideBCPLComment ||
4405188c4b9SDmitri Gribenko          CommentState == LCS_InsideCComment);
4415188c4b9SDmitri Gribenko 
4425188c4b9SDmitri Gribenko   switch (State) {
4435188c4b9SDmitri Gribenko   case LS_Normal:
4445188c4b9SDmitri Gribenko     break;
4455188c4b9SDmitri Gribenko   case LS_VerbatimBlockFirstLine:
4465188c4b9SDmitri Gribenko     lexVerbatimBlockFirstLine(T);
4475188c4b9SDmitri Gribenko     return;
4485188c4b9SDmitri Gribenko   case LS_VerbatimBlockBody:
4495188c4b9SDmitri Gribenko     lexVerbatimBlockBody(T);
4505188c4b9SDmitri Gribenko     return;
4511669f702SDmitri Gribenko   case LS_VerbatimLineText:
4521669f702SDmitri Gribenko     lexVerbatimLineText(T);
4531669f702SDmitri Gribenko     return;
454e00ffc7bSDmitri Gribenko   case LS_HTMLStartTag:
455e00ffc7bSDmitri Gribenko     lexHTMLStartTag(T);
4565188c4b9SDmitri Gribenko     return;
457e00ffc7bSDmitri Gribenko   case LS_HTMLEndTag:
458e00ffc7bSDmitri Gribenko     lexHTMLEndTag(T);
459ec92531cSDmitri Gribenko     return;
4605188c4b9SDmitri Gribenko   }
4615188c4b9SDmitri Gribenko 
4625188c4b9SDmitri Gribenko   assert(State == LS_Normal);
4635188c4b9SDmitri Gribenko 
4645188c4b9SDmitri Gribenko   const char *TokenPtr = BufferPtr;
4655188c4b9SDmitri Gribenko   assert(TokenPtr < CommentEnd);
4665188c4b9SDmitri Gribenko   while (TokenPtr != CommentEnd) {
4675188c4b9SDmitri Gribenko     switch(*TokenPtr) {
4685188c4b9SDmitri Gribenko       case '\\':
4695188c4b9SDmitri Gribenko       case '@': {
4705188c4b9SDmitri Gribenko         TokenPtr++;
4715188c4b9SDmitri Gribenko         if (TokenPtr == CommentEnd) {
4724586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
4735188c4b9SDmitri Gribenko           return;
4745188c4b9SDmitri Gribenko         }
4755188c4b9SDmitri Gribenko         char C = *TokenPtr;
4765188c4b9SDmitri Gribenko         switch (C) {
4775188c4b9SDmitri Gribenko         default:
4785188c4b9SDmitri Gribenko           break;
4795188c4b9SDmitri Gribenko 
4805188c4b9SDmitri Gribenko         case '\\': case '@': case '&': case '$':
4815188c4b9SDmitri Gribenko         case '#':  case '<': case '>': case '%':
4825188c4b9SDmitri Gribenko         case '\"': case '.': case ':':
4835188c4b9SDmitri Gribenko           // This is one of \\ \@ \& \$ etc escape sequences.
4845188c4b9SDmitri Gribenko           TokenPtr++;
4855188c4b9SDmitri Gribenko           if (C == ':' && TokenPtr != CommentEnd && *TokenPtr == ':') {
4865188c4b9SDmitri Gribenko             // This is the \:: escape sequence.
4875188c4b9SDmitri Gribenko             TokenPtr++;
4885188c4b9SDmitri Gribenko           }
48960ddd8a1SDmitri Gribenko           StringRef UnescapedText(BufferPtr + 1, TokenPtr - (BufferPtr + 1));
4905188c4b9SDmitri Gribenko           formTokenWithChars(T, TokenPtr, tok::text);
49160ddd8a1SDmitri Gribenko           T.setText(UnescapedText);
4925188c4b9SDmitri Gribenko           return;
4935188c4b9SDmitri Gribenko         }
4945188c4b9SDmitri Gribenko 
4955188c4b9SDmitri Gribenko         // Don't make zero-length commands.
496ad45ad6fSDmitri Gribenko         if (!isCommandNameStartCharacter(*TokenPtr)) {
4974586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
4985188c4b9SDmitri Gribenko           return;
4995188c4b9SDmitri Gribenko         }
5005188c4b9SDmitri Gribenko 
5015188c4b9SDmitri Gribenko         TokenPtr = skipCommandName(TokenPtr, CommentEnd);
5025188c4b9SDmitri Gribenko         unsigned Length = TokenPtr - (BufferPtr + 1);
5035188c4b9SDmitri Gribenko 
5045188c4b9SDmitri Gribenko         // Hardcoded support for lexing LaTeX formula commands
5055188c4b9SDmitri Gribenko         // \f$ \f[ \f] \f{ \f} as a single command.
5065188c4b9SDmitri Gribenko         if (Length == 1 && TokenPtr[-1] == 'f' && TokenPtr != CommentEnd) {
5075188c4b9SDmitri Gribenko           C = *TokenPtr;
5085188c4b9SDmitri Gribenko           if (C == '$' || C == '[' || C == ']' || C == '{' || C == '}') {
5095188c4b9SDmitri Gribenko             TokenPtr++;
5105188c4b9SDmitri Gribenko             Length++;
5115188c4b9SDmitri Gribenko           }
5125188c4b9SDmitri Gribenko         }
5135188c4b9SDmitri Gribenko 
5145188c4b9SDmitri Gribenko         const StringRef CommandName(BufferPtr + 1, Length);
5155188c4b9SDmitri Gribenko 
5167acbf00fSDmitri Gribenko         const CommandInfo *Info = Traits.getCommandInfoOrNULL(CommandName);
5177acbf00fSDmitri Gribenko         if (!Info) {
5187acbf00fSDmitri Gribenko           formTokenWithChars(T, TokenPtr, tok::unknown_command);
5197acbf00fSDmitri Gribenko           T.setUnknownCommandName(CommandName);
5205188c4b9SDmitri Gribenko           return;
5215188c4b9SDmitri Gribenko         }
5227acbf00fSDmitri Gribenko         if (Info->IsVerbatimBlockCommand) {
5237acbf00fSDmitri Gribenko           setupAndLexVerbatimBlock(T, TokenPtr, *BufferPtr, Info);
5247acbf00fSDmitri Gribenko           return;
5257acbf00fSDmitri Gribenko         }
5267acbf00fSDmitri Gribenko         if (Info->IsVerbatimLineCommand) {
5277acbf00fSDmitri Gribenko           setupAndLexVerbatimLine(T, TokenPtr, Info);
5285188c4b9SDmitri Gribenko           return;
5295188c4b9SDmitri Gribenko         }
5305188c4b9SDmitri Gribenko         formTokenWithChars(T, TokenPtr, tok::command);
5317acbf00fSDmitri Gribenko         T.setCommandID(Info->getID());
5325188c4b9SDmitri Gribenko         return;
5335188c4b9SDmitri Gribenko       }
5345188c4b9SDmitri Gribenko 
5354586df76SDmitri Gribenko       case '&':
5364586df76SDmitri Gribenko         lexHTMLCharacterReference(T);
5374586df76SDmitri Gribenko         return;
5384586df76SDmitri Gribenko 
5395188c4b9SDmitri Gribenko       case '<': {
5405188c4b9SDmitri Gribenko         TokenPtr++;
5415188c4b9SDmitri Gribenko         if (TokenPtr == CommentEnd) {
5424586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
5435188c4b9SDmitri Gribenko           return;
5445188c4b9SDmitri Gribenko         }
5455188c4b9SDmitri Gribenko         const char C = *TokenPtr;
54617709ae8SDmitri Gribenko         if (isHTMLIdentifierStartingCharacter(C))
547e00ffc7bSDmitri Gribenko           setupAndLexHTMLStartTag(T);
5485188c4b9SDmitri Gribenko         else if (C == '/')
549e00ffc7bSDmitri Gribenko           setupAndLexHTMLEndTag(T);
5504586df76SDmitri Gribenko         else
5514586df76SDmitri Gribenko           formTextToken(T, TokenPtr);
5524586df76SDmitri Gribenko 
5535188c4b9SDmitri Gribenko         return;
5545188c4b9SDmitri Gribenko       }
5555188c4b9SDmitri Gribenko 
5565188c4b9SDmitri Gribenko       case '\n':
5575188c4b9SDmitri Gribenko       case '\r':
5585188c4b9SDmitri Gribenko         TokenPtr = skipNewline(TokenPtr, CommentEnd);
5595188c4b9SDmitri Gribenko         formTokenWithChars(T, TokenPtr, tok::newline);
5605188c4b9SDmitri Gribenko 
5615188c4b9SDmitri Gribenko         if (CommentState == LCS_InsideCComment)
5625188c4b9SDmitri Gribenko           skipLineStartingDecorations();
5635188c4b9SDmitri Gribenko         return;
5645188c4b9SDmitri Gribenko 
5655188c4b9SDmitri Gribenko       default: {
56610af67a9SDmitri Gribenko         size_t End = StringRef(TokenPtr, CommentEnd - TokenPtr).
56710af67a9SDmitri Gribenko                          find_first_of("\n\r\\@&<");
56810af67a9SDmitri Gribenko         if (End != StringRef::npos)
56910af67a9SDmitri Gribenko           TokenPtr += End;
57010af67a9SDmitri Gribenko         else
57110af67a9SDmitri Gribenko           TokenPtr = CommentEnd;
5724586df76SDmitri Gribenko         formTextToken(T, TokenPtr);
5735188c4b9SDmitri Gribenko         return;
5745188c4b9SDmitri Gribenko       }
5755188c4b9SDmitri Gribenko     }
5765188c4b9SDmitri Gribenko   }
5775188c4b9SDmitri Gribenko }
5785188c4b9SDmitri Gribenko 
5795188c4b9SDmitri Gribenko void Lexer::setupAndLexVerbatimBlock(Token &T,
5805188c4b9SDmitri Gribenko                                      const char *TextBegin,
5817acbf00fSDmitri Gribenko                                      char Marker, const CommandInfo *Info) {
5827acbf00fSDmitri Gribenko   assert(Info->IsVerbatimBlockCommand);
5837acbf00fSDmitri Gribenko 
5845188c4b9SDmitri Gribenko   VerbatimBlockEndCommandName.clear();
5855188c4b9SDmitri Gribenko   VerbatimBlockEndCommandName.append(Marker == '\\' ? "\\" : "@");
5867acbf00fSDmitri Gribenko   VerbatimBlockEndCommandName.append(Info->EndCommandName);
5875188c4b9SDmitri Gribenko 
5885188c4b9SDmitri Gribenko   formTokenWithChars(T, TextBegin, tok::verbatim_block_begin);
5897acbf00fSDmitri Gribenko   T.setVerbatimBlockID(Info->getID());
5905188c4b9SDmitri Gribenko 
591ec92531cSDmitri Gribenko   // If there is a newline following the verbatim opening command, skip the
592ec92531cSDmitri Gribenko   // newline so that we don't create an tok::verbatim_block_line with empty
593ec92531cSDmitri Gribenko   // text content.
594ec92531cSDmitri Gribenko   if (BufferPtr != CommentEnd) {
595ec92531cSDmitri Gribenko     const char C = *BufferPtr;
596ec92531cSDmitri Gribenko     if (C == '\n' || C == '\r') {
597ec92531cSDmitri Gribenko       BufferPtr = skipNewline(BufferPtr, CommentEnd);
598ec92531cSDmitri Gribenko       State = LS_VerbatimBlockBody;
599ec92531cSDmitri Gribenko       return;
600ec92531cSDmitri Gribenko     }
601ec92531cSDmitri Gribenko   }
602ec92531cSDmitri Gribenko 
6035188c4b9SDmitri Gribenko   State = LS_VerbatimBlockFirstLine;
6045188c4b9SDmitri Gribenko }
6055188c4b9SDmitri Gribenko 
6065188c4b9SDmitri Gribenko void Lexer::lexVerbatimBlockFirstLine(Token &T) {
607e4a3997dSDmitri Gribenko again:
6085188c4b9SDmitri Gribenko   assert(BufferPtr < CommentEnd);
6095188c4b9SDmitri Gribenko 
6105188c4b9SDmitri Gribenko   // FIXME: It would be better to scan the text once, finding either the block
6115188c4b9SDmitri Gribenko   // end command or newline.
6125188c4b9SDmitri Gribenko   //
6135188c4b9SDmitri Gribenko   // Extract current line.
6145188c4b9SDmitri Gribenko   const char *Newline = findNewline(BufferPtr, CommentEnd);
6155188c4b9SDmitri Gribenko   StringRef Line(BufferPtr, Newline - BufferPtr);
6165188c4b9SDmitri Gribenko 
6175188c4b9SDmitri Gribenko   // Look for end command in current line.
6185188c4b9SDmitri Gribenko   size_t Pos = Line.find(VerbatimBlockEndCommandName);
619ec92531cSDmitri Gribenko   const char *TextEnd;
6205188c4b9SDmitri Gribenko   const char *NextLine;
6215188c4b9SDmitri Gribenko   if (Pos == StringRef::npos) {
6225188c4b9SDmitri Gribenko     // Current line is completely verbatim.
623ec92531cSDmitri Gribenko     TextEnd = Newline;
6245188c4b9SDmitri Gribenko     NextLine = skipNewline(Newline, CommentEnd);
6255188c4b9SDmitri Gribenko   } else if (Pos == 0) {
6265188c4b9SDmitri Gribenko     // Current line contains just an end command.
6275188c4b9SDmitri Gribenko     const char *End = BufferPtr + VerbatimBlockEndCommandName.size();
62860ddd8a1SDmitri Gribenko     StringRef Name(BufferPtr + 1, End - (BufferPtr + 1));
6295188c4b9SDmitri Gribenko     formTokenWithChars(T, End, tok::verbatim_block_end);
6307acbf00fSDmitri Gribenko     T.setVerbatimBlockID(Traits.getCommandInfo(Name)->getID());
6315188c4b9SDmitri Gribenko     State = LS_Normal;
6325188c4b9SDmitri Gribenko     return;
6335188c4b9SDmitri Gribenko   } else {
6345188c4b9SDmitri Gribenko     // There is some text, followed by end command.  Extract text first.
635ec92531cSDmitri Gribenko     TextEnd = BufferPtr + Pos;
636ec92531cSDmitri Gribenko     NextLine = TextEnd;
637e4a3997dSDmitri Gribenko     // If there is only whitespace before end command, skip whitespace.
638e4a3997dSDmitri Gribenko     if (isWhitespace(BufferPtr, TextEnd)) {
639e4a3997dSDmitri Gribenko       BufferPtr = TextEnd;
640e4a3997dSDmitri Gribenko       goto again;
641e4a3997dSDmitri Gribenko     }
6425188c4b9SDmitri Gribenko   }
6435188c4b9SDmitri Gribenko 
644ec92531cSDmitri Gribenko   StringRef Text(BufferPtr, TextEnd - BufferPtr);
6455188c4b9SDmitri Gribenko   formTokenWithChars(T, NextLine, tok::verbatim_block_line);
64660ddd8a1SDmitri Gribenko   T.setVerbatimBlockText(Text);
6475188c4b9SDmitri Gribenko 
6485188c4b9SDmitri Gribenko   State = LS_VerbatimBlockBody;
6495188c4b9SDmitri Gribenko }
6505188c4b9SDmitri Gribenko 
6515188c4b9SDmitri Gribenko void Lexer::lexVerbatimBlockBody(Token &T) {
6525188c4b9SDmitri Gribenko   assert(State == LS_VerbatimBlockBody);
6535188c4b9SDmitri Gribenko 
6545188c4b9SDmitri Gribenko   if (CommentState == LCS_InsideCComment)
6555188c4b9SDmitri Gribenko     skipLineStartingDecorations();
6565188c4b9SDmitri Gribenko 
6575188c4b9SDmitri Gribenko   lexVerbatimBlockFirstLine(T);
6585188c4b9SDmitri Gribenko }
6595188c4b9SDmitri Gribenko 
6607acbf00fSDmitri Gribenko void Lexer::setupAndLexVerbatimLine(Token &T, const char *TextBegin,
6617acbf00fSDmitri Gribenko                                     const CommandInfo *Info) {
6627acbf00fSDmitri Gribenko   assert(Info->IsVerbatimLineCommand);
6631669f702SDmitri Gribenko   formTokenWithChars(T, TextBegin, tok::verbatim_line_name);
6647acbf00fSDmitri Gribenko   T.setVerbatimLineID(Info->getID());
6651669f702SDmitri Gribenko 
6661669f702SDmitri Gribenko   State = LS_VerbatimLineText;
6671669f702SDmitri Gribenko }
6681669f702SDmitri Gribenko 
6691669f702SDmitri Gribenko void Lexer::lexVerbatimLineText(Token &T) {
6701669f702SDmitri Gribenko   assert(State == LS_VerbatimLineText);
6711669f702SDmitri Gribenko 
6725188c4b9SDmitri Gribenko   // Extract current line.
6735188c4b9SDmitri Gribenko   const char *Newline = findNewline(BufferPtr, CommentEnd);
6741669f702SDmitri Gribenko   const StringRef Text(BufferPtr, Newline - BufferPtr);
6751669f702SDmitri Gribenko   formTokenWithChars(T, Newline, tok::verbatim_line_text);
6765188c4b9SDmitri Gribenko   T.setVerbatimLineText(Text);
6771669f702SDmitri Gribenko 
6781669f702SDmitri Gribenko   State = LS_Normal;
6795188c4b9SDmitri Gribenko }
6805188c4b9SDmitri Gribenko 
6814586df76SDmitri Gribenko void Lexer::lexHTMLCharacterReference(Token &T) {
6824586df76SDmitri Gribenko   const char *TokenPtr = BufferPtr;
6834586df76SDmitri Gribenko   assert(*TokenPtr == '&');
6844586df76SDmitri Gribenko   TokenPtr++;
6854586df76SDmitri Gribenko   if (TokenPtr == CommentEnd) {
6864586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
6874586df76SDmitri Gribenko     return;
6884586df76SDmitri Gribenko   }
6894586df76SDmitri Gribenko   const char *NamePtr;
6904586df76SDmitri Gribenko   bool isNamed = false;
6914586df76SDmitri Gribenko   bool isDecimal = false;
6924586df76SDmitri Gribenko   char C = *TokenPtr;
6934586df76SDmitri Gribenko   if (isHTMLNamedCharacterReferenceCharacter(C)) {
6944586df76SDmitri Gribenko     NamePtr = TokenPtr;
6954586df76SDmitri Gribenko     TokenPtr = skipNamedCharacterReference(TokenPtr, CommentEnd);
6964586df76SDmitri Gribenko     isNamed = true;
6974586df76SDmitri Gribenko   } else if (C == '#') {
6984586df76SDmitri Gribenko     TokenPtr++;
6994586df76SDmitri Gribenko     if (TokenPtr == CommentEnd) {
7004586df76SDmitri Gribenko       formTextToken(T, TokenPtr);
7014586df76SDmitri Gribenko       return;
7024586df76SDmitri Gribenko     }
7034586df76SDmitri Gribenko     C = *TokenPtr;
7044586df76SDmitri Gribenko     if (isHTMLDecimalCharacterReferenceCharacter(C)) {
7054586df76SDmitri Gribenko       NamePtr = TokenPtr;
7064586df76SDmitri Gribenko       TokenPtr = skipDecimalCharacterReference(TokenPtr, CommentEnd);
7074586df76SDmitri Gribenko       isDecimal = true;
7084586df76SDmitri Gribenko     } else if (C == 'x' || C == 'X') {
7094586df76SDmitri Gribenko       TokenPtr++;
7104586df76SDmitri Gribenko       NamePtr = TokenPtr;
7114586df76SDmitri Gribenko       TokenPtr = skipHexCharacterReference(TokenPtr, CommentEnd);
7124586df76SDmitri Gribenko     } else {
7134586df76SDmitri Gribenko       formTextToken(T, TokenPtr);
7144586df76SDmitri Gribenko       return;
7154586df76SDmitri Gribenko     }
7164586df76SDmitri Gribenko   } else {
7174586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
7184586df76SDmitri Gribenko     return;
7194586df76SDmitri Gribenko   }
7204586df76SDmitri Gribenko   if (NamePtr == TokenPtr || TokenPtr == CommentEnd ||
7214586df76SDmitri Gribenko       *TokenPtr != ';') {
7224586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
7234586df76SDmitri Gribenko     return;
7244586df76SDmitri Gribenko   }
7254586df76SDmitri Gribenko   StringRef Name(NamePtr, TokenPtr - NamePtr);
7264586df76SDmitri Gribenko   TokenPtr++; // Skip semicolon.
7274586df76SDmitri Gribenko   StringRef Resolved;
7287b3ae190SFariborz Jahanian   if (isNamed) {
7294586df76SDmitri Gribenko     Resolved = resolveHTMLNamedCharacterReference(Name);
7307b3ae190SFariborz Jahanian     if (Resolved.empty()) {
7317b3ae190SFariborz Jahanian       Resolved = HTMLDoxygenCharacterReference(Name);
7327b3ae190SFariborz Jahanian       if (!Resolved.empty()) {
7337b3ae190SFariborz Jahanian         formTokenWithChars(T, TokenPtr, tok::text);
7347b3ae190SFariborz Jahanian         T.setText(Resolved);
7357b3ae190SFariborz Jahanian         return;
7367b3ae190SFariborz Jahanian       }
7377b3ae190SFariborz Jahanian     }
7387b3ae190SFariborz Jahanian   }
7394586df76SDmitri Gribenko   else if (isDecimal)
7404586df76SDmitri Gribenko     Resolved = resolveHTMLDecimalCharacterReference(Name);
7414586df76SDmitri Gribenko   else
7424586df76SDmitri Gribenko     Resolved = resolveHTMLHexCharacterReference(Name);
7434586df76SDmitri Gribenko 
7444586df76SDmitri Gribenko   if (Resolved.empty()) {
7454586df76SDmitri Gribenko     formTextToken(T, TokenPtr);
7464586df76SDmitri Gribenko     return;
7474586df76SDmitri Gribenko   }
7484586df76SDmitri Gribenko   formTokenWithChars(T, TokenPtr, tok::text);
7494586df76SDmitri Gribenko   T.setText(Resolved);
7504586df76SDmitri Gribenko   return;
7514586df76SDmitri Gribenko }
7524586df76SDmitri Gribenko 
753e00ffc7bSDmitri Gribenko void Lexer::setupAndLexHTMLStartTag(Token &T) {
75417709ae8SDmitri Gribenko   assert(BufferPtr[0] == '<' &&
75517709ae8SDmitri Gribenko          isHTMLIdentifierStartingCharacter(BufferPtr[1]));
7565188c4b9SDmitri Gribenko   const char *TagNameEnd = skipHTMLIdentifier(BufferPtr + 2, CommentEnd);
75760ddd8a1SDmitri Gribenko   StringRef Name(BufferPtr + 1, TagNameEnd - (BufferPtr + 1));
758107618a6SDmitri Gribenko   if (!isHTMLTagName(Name)) {
759107618a6SDmitri Gribenko     formTextToken(T, TagNameEnd);
760107618a6SDmitri Gribenko     return;
761107618a6SDmitri Gribenko   }
762107618a6SDmitri Gribenko 
763e00ffc7bSDmitri Gribenko   formTokenWithChars(T, TagNameEnd, tok::html_start_tag);
764e00ffc7bSDmitri Gribenko   T.setHTMLTagStartName(Name);
7655188c4b9SDmitri Gribenko 
7665188c4b9SDmitri Gribenko   BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
7675188c4b9SDmitri Gribenko 
76817709ae8SDmitri Gribenko   const char C = *BufferPtr;
76917709ae8SDmitri Gribenko   if (BufferPtr != CommentEnd &&
770f26054f0SDmitri Gribenko       (C == '>' || C == '/' || isHTMLIdentifierStartingCharacter(C)))
771e00ffc7bSDmitri Gribenko     State = LS_HTMLStartTag;
7725188c4b9SDmitri Gribenko }
7735188c4b9SDmitri Gribenko 
774e00ffc7bSDmitri Gribenko void Lexer::lexHTMLStartTag(Token &T) {
775e00ffc7bSDmitri Gribenko   assert(State == LS_HTMLStartTag);
7765188c4b9SDmitri Gribenko 
7775188c4b9SDmitri Gribenko   const char *TokenPtr = BufferPtr;
7785188c4b9SDmitri Gribenko   char C = *TokenPtr;
7795188c4b9SDmitri Gribenko   if (isHTMLIdentifierCharacter(C)) {
7805188c4b9SDmitri Gribenko     TokenPtr = skipHTMLIdentifier(TokenPtr, CommentEnd);
78160ddd8a1SDmitri Gribenko     StringRef Ident(BufferPtr, TokenPtr - BufferPtr);
7825188c4b9SDmitri Gribenko     formTokenWithChars(T, TokenPtr, tok::html_ident);
78360ddd8a1SDmitri Gribenko     T.setHTMLIdent(Ident);
7845188c4b9SDmitri Gribenko   } else {
7855188c4b9SDmitri Gribenko     switch (C) {
7865188c4b9SDmitri Gribenko     case '=':
7875188c4b9SDmitri Gribenko       TokenPtr++;
7885188c4b9SDmitri Gribenko       formTokenWithChars(T, TokenPtr, tok::html_equals);
7895188c4b9SDmitri Gribenko       break;
7905188c4b9SDmitri Gribenko     case '\"':
7915188c4b9SDmitri Gribenko     case '\'': {
7925188c4b9SDmitri Gribenko       const char *OpenQuote = TokenPtr;
7935188c4b9SDmitri Gribenko       TokenPtr = skipHTMLQuotedString(TokenPtr, CommentEnd);
7945188c4b9SDmitri Gribenko       const char *ClosingQuote = TokenPtr;
7955188c4b9SDmitri Gribenko       if (TokenPtr != CommentEnd) // Skip closing quote.
7965188c4b9SDmitri Gribenko         TokenPtr++;
7975188c4b9SDmitri Gribenko       formTokenWithChars(T, TokenPtr, tok::html_quoted_string);
7985188c4b9SDmitri Gribenko       T.setHTMLQuotedString(StringRef(OpenQuote + 1,
7995188c4b9SDmitri Gribenko                                       ClosingQuote - (OpenQuote + 1)));
8005188c4b9SDmitri Gribenko       break;
8015188c4b9SDmitri Gribenko     }
8025188c4b9SDmitri Gribenko     case '>':
8035188c4b9SDmitri Gribenko       TokenPtr++;
8045188c4b9SDmitri Gribenko       formTokenWithChars(T, TokenPtr, tok::html_greater);
80517709ae8SDmitri Gribenko       State = LS_Normal;
80617709ae8SDmitri Gribenko       return;
807f26054f0SDmitri Gribenko     case '/':
808f26054f0SDmitri Gribenko       TokenPtr++;
809f26054f0SDmitri Gribenko       if (TokenPtr != CommentEnd && *TokenPtr == '>') {
810f26054f0SDmitri Gribenko         TokenPtr++;
811f26054f0SDmitri Gribenko         formTokenWithChars(T, TokenPtr, tok::html_slash_greater);
8124586df76SDmitri Gribenko       } else
8134586df76SDmitri Gribenko         formTextToken(T, TokenPtr);
8144586df76SDmitri Gribenko 
815f26054f0SDmitri Gribenko       State = LS_Normal;
816f26054f0SDmitri Gribenko       return;
8175188c4b9SDmitri Gribenko     }
8185188c4b9SDmitri Gribenko   }
8195188c4b9SDmitri Gribenko 
8205188c4b9SDmitri Gribenko   // Now look ahead and return to normal state if we don't see any HTML tokens
8215188c4b9SDmitri Gribenko   // ahead.
8225188c4b9SDmitri Gribenko   BufferPtr = skipWhitespace(BufferPtr, CommentEnd);
8235188c4b9SDmitri Gribenko   if (BufferPtr == CommentEnd) {
8245188c4b9SDmitri Gribenko     State = LS_Normal;
8255188c4b9SDmitri Gribenko     return;
8265188c4b9SDmitri Gribenko   }
8275188c4b9SDmitri Gribenko 
8285188c4b9SDmitri Gribenko   C = *BufferPtr;
82917709ae8SDmitri Gribenko   if (!isHTMLIdentifierStartingCharacter(C) &&
8305188c4b9SDmitri Gribenko       C != '=' && C != '\"' && C != '\'' && C != '>') {
8315188c4b9SDmitri Gribenko     State = LS_Normal;
8325188c4b9SDmitri Gribenko     return;
8335188c4b9SDmitri Gribenko   }
8345188c4b9SDmitri Gribenko }
8355188c4b9SDmitri Gribenko 
836e00ffc7bSDmitri Gribenko void Lexer::setupAndLexHTMLEndTag(Token &T) {
8375188c4b9SDmitri Gribenko   assert(BufferPtr[0] == '<' && BufferPtr[1] == '/');
8385188c4b9SDmitri Gribenko 
8395188c4b9SDmitri Gribenko   const char *TagNameBegin = skipWhitespace(BufferPtr + 2, CommentEnd);
8405188c4b9SDmitri Gribenko   const char *TagNameEnd = skipHTMLIdentifier(TagNameBegin, CommentEnd);
841107618a6SDmitri Gribenko   StringRef Name(TagNameBegin, TagNameEnd - TagNameBegin);
842107618a6SDmitri Gribenko   if (!isHTMLTagName(Name)) {
843107618a6SDmitri Gribenko     formTextToken(T, TagNameEnd);
844107618a6SDmitri Gribenko     return;
845107618a6SDmitri Gribenko   }
8465188c4b9SDmitri Gribenko 
8475188c4b9SDmitri Gribenko   const char *End = skipWhitespace(TagNameEnd, CommentEnd);
8485188c4b9SDmitri Gribenko 
849e00ffc7bSDmitri Gribenko   formTokenWithChars(T, End, tok::html_end_tag);
850107618a6SDmitri Gribenko   T.setHTMLTagEndName(Name);
851ec92531cSDmitri Gribenko 
852ec92531cSDmitri Gribenko   if (BufferPtr != CommentEnd && *BufferPtr == '>')
853e00ffc7bSDmitri Gribenko     State = LS_HTMLEndTag;
854ec92531cSDmitri Gribenko }
855ec92531cSDmitri Gribenko 
856e00ffc7bSDmitri Gribenko void Lexer::lexHTMLEndTag(Token &T) {
857ec92531cSDmitri Gribenko   assert(BufferPtr != CommentEnd && *BufferPtr == '>');
858ec92531cSDmitri Gribenko 
859ec92531cSDmitri Gribenko   formTokenWithChars(T, BufferPtr + 1, tok::html_greater);
860ec92531cSDmitri Gribenko   State = LS_Normal;
8615188c4b9SDmitri Gribenko }
8625188c4b9SDmitri Gribenko 
863ca7f80adSDmitri Gribenko Lexer::Lexer(llvm::BumpPtrAllocator &Allocator, const CommandTraits &Traits,
8646bab9113SDmitri Gribenko              SourceLocation FileLoc,
8655188c4b9SDmitri Gribenko              const char *BufferStart, const char *BufferEnd):
866ca7f80adSDmitri Gribenko     Allocator(Allocator), Traits(Traits),
8675188c4b9SDmitri Gribenko     BufferStart(BufferStart), BufferEnd(BufferEnd),
8686bab9113SDmitri Gribenko     FileLoc(FileLoc), BufferPtr(BufferStart),
8695188c4b9SDmitri Gribenko     CommentState(LCS_BeforeComment), State(LS_Normal) {
8705188c4b9SDmitri Gribenko }
8715188c4b9SDmitri Gribenko 
8725188c4b9SDmitri Gribenko void Lexer::lex(Token &T) {
8735188c4b9SDmitri Gribenko again:
8745188c4b9SDmitri Gribenko   switch (CommentState) {
8755188c4b9SDmitri Gribenko   case LCS_BeforeComment:
8765188c4b9SDmitri Gribenko     if (BufferPtr == BufferEnd) {
8775188c4b9SDmitri Gribenko       formTokenWithChars(T, BufferPtr, tok::eof);
8785188c4b9SDmitri Gribenko       return;
8795188c4b9SDmitri Gribenko     }
8805188c4b9SDmitri Gribenko 
8815188c4b9SDmitri Gribenko     assert(*BufferPtr == '/');
8825188c4b9SDmitri Gribenko     BufferPtr++; // Skip first slash.
8835188c4b9SDmitri Gribenko     switch(*BufferPtr) {
8845188c4b9SDmitri Gribenko     case '/': { // BCPL comment.
8855188c4b9SDmitri Gribenko       BufferPtr++; // Skip second slash.
8865188c4b9SDmitri Gribenko 
8875188c4b9SDmitri Gribenko       if (BufferPtr != BufferEnd) {
8885188c4b9SDmitri Gribenko         // Skip Doxygen magic marker, if it is present.
8895188c4b9SDmitri Gribenko         // It might be missing because of a typo //< or /*<, or because we
8905188c4b9SDmitri Gribenko         // merged this non-Doxygen comment into a bunch of Doxygen comments
8915188c4b9SDmitri Gribenko         // around it: /** ... */ /* ... */ /** ... */
8925188c4b9SDmitri Gribenko         const char C = *BufferPtr;
8935188c4b9SDmitri Gribenko         if (C == '/' || C == '!')
8945188c4b9SDmitri Gribenko           BufferPtr++;
8955188c4b9SDmitri Gribenko       }
8965188c4b9SDmitri Gribenko 
8975188c4b9SDmitri Gribenko       // Skip less-than symbol that marks trailing comments.
8985188c4b9SDmitri Gribenko       // Skip it even if the comment is not a Doxygen one, because //< and /*<
8995188c4b9SDmitri Gribenko       // are frequent typos.
9005188c4b9SDmitri Gribenko       if (BufferPtr != BufferEnd && *BufferPtr == '<')
9015188c4b9SDmitri Gribenko         BufferPtr++;
9025188c4b9SDmitri Gribenko 
9035188c4b9SDmitri Gribenko       CommentState = LCS_InsideBCPLComment;
904ec92531cSDmitri Gribenko       if (State != LS_VerbatimBlockBody && State != LS_VerbatimBlockFirstLine)
9055188c4b9SDmitri Gribenko         State = LS_Normal;
9065188c4b9SDmitri Gribenko       CommentEnd = findBCPLCommentEnd(BufferPtr, BufferEnd);
9075188c4b9SDmitri Gribenko       goto again;
9085188c4b9SDmitri Gribenko     }
9095188c4b9SDmitri Gribenko     case '*': { // C comment.
9105188c4b9SDmitri Gribenko       BufferPtr++; // Skip star.
9115188c4b9SDmitri Gribenko 
9125188c4b9SDmitri Gribenko       // Skip Doxygen magic marker.
9135188c4b9SDmitri Gribenko       const char C = *BufferPtr;
9145188c4b9SDmitri Gribenko       if ((C == '*' && *(BufferPtr + 1) != '/') || C == '!')
9155188c4b9SDmitri Gribenko         BufferPtr++;
9165188c4b9SDmitri Gribenko 
9175188c4b9SDmitri Gribenko       // Skip less-than symbol that marks trailing comments.
9185188c4b9SDmitri Gribenko       if (BufferPtr != BufferEnd && *BufferPtr == '<')
9195188c4b9SDmitri Gribenko         BufferPtr++;
9205188c4b9SDmitri Gribenko 
9215188c4b9SDmitri Gribenko       CommentState = LCS_InsideCComment;
9225188c4b9SDmitri Gribenko       State = LS_Normal;
9235188c4b9SDmitri Gribenko       CommentEnd = findCCommentEnd(BufferPtr, BufferEnd);
9245188c4b9SDmitri Gribenko       goto again;
9255188c4b9SDmitri Gribenko     }
9265188c4b9SDmitri Gribenko     default:
9275188c4b9SDmitri Gribenko       llvm_unreachable("second character of comment should be '/' or '*'");
9285188c4b9SDmitri Gribenko     }
9295188c4b9SDmitri Gribenko 
9305188c4b9SDmitri Gribenko   case LCS_BetweenComments: {
9315188c4b9SDmitri Gribenko     // Consecutive comments are extracted only if there is only whitespace
9325188c4b9SDmitri Gribenko     // between them.  So we can search for the start of the next comment.
9335188c4b9SDmitri Gribenko     const char *EndWhitespace = BufferPtr;
9345188c4b9SDmitri Gribenko     while(EndWhitespace != BufferEnd && *EndWhitespace != '/')
9355188c4b9SDmitri Gribenko       EndWhitespace++;
9365188c4b9SDmitri Gribenko 
9375188c4b9SDmitri Gribenko     // Turn any whitespace between comments (and there is only whitespace
93817709ae8SDmitri Gribenko     // between them -- guaranteed by comment extraction) into a newline.  We
93917709ae8SDmitri Gribenko     // have two newlines between C comments in total (first one was synthesized
94017709ae8SDmitri Gribenko     // after a comment).
9415188c4b9SDmitri Gribenko     formTokenWithChars(T, EndWhitespace, tok::newline);
9425188c4b9SDmitri Gribenko 
9435188c4b9SDmitri Gribenko     CommentState = LCS_BeforeComment;
9445188c4b9SDmitri Gribenko     break;
9455188c4b9SDmitri Gribenko   }
9465188c4b9SDmitri Gribenko 
9475188c4b9SDmitri Gribenko   case LCS_InsideBCPLComment:
9485188c4b9SDmitri Gribenko   case LCS_InsideCComment:
9495188c4b9SDmitri Gribenko     if (BufferPtr != CommentEnd) {
9505188c4b9SDmitri Gribenko       lexCommentText(T);
9515188c4b9SDmitri Gribenko       break;
9525188c4b9SDmitri Gribenko     } else {
9535188c4b9SDmitri Gribenko       // Skip C comment closing sequence.
9545188c4b9SDmitri Gribenko       if (CommentState == LCS_InsideCComment) {
9555188c4b9SDmitri Gribenko         assert(BufferPtr[0] == '*' && BufferPtr[1] == '/');
9565188c4b9SDmitri Gribenko         BufferPtr += 2;
9575188c4b9SDmitri Gribenko         assert(BufferPtr <= BufferEnd);
9585188c4b9SDmitri Gribenko 
9595188c4b9SDmitri Gribenko         // Synthenize newline just after the C comment, regardless if there is
9605188c4b9SDmitri Gribenko         // actually a newline.
9615188c4b9SDmitri Gribenko         formTokenWithChars(T, BufferPtr, tok::newline);
9625188c4b9SDmitri Gribenko 
9635188c4b9SDmitri Gribenko         CommentState = LCS_BetweenComments;
9645188c4b9SDmitri Gribenko         break;
9655188c4b9SDmitri Gribenko       } else {
9665188c4b9SDmitri Gribenko         // Don't synthesized a newline after BCPL comment.
9675188c4b9SDmitri Gribenko         CommentState = LCS_BetweenComments;
9685188c4b9SDmitri Gribenko         goto again;
9695188c4b9SDmitri Gribenko       }
9705188c4b9SDmitri Gribenko     }
9715188c4b9SDmitri Gribenko   }
9725188c4b9SDmitri Gribenko }
9735188c4b9SDmitri Gribenko 
9745188c4b9SDmitri Gribenko StringRef Lexer::getSpelling(const Token &Tok,
9755188c4b9SDmitri Gribenko                              const SourceManager &SourceMgr,
9765188c4b9SDmitri Gribenko                              bool *Invalid) const {
9775188c4b9SDmitri Gribenko   SourceLocation Loc = Tok.getLocation();
9785188c4b9SDmitri Gribenko   std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedLoc(Loc);
9795188c4b9SDmitri Gribenko 
9805188c4b9SDmitri Gribenko   bool InvalidTemp = false;
9815188c4b9SDmitri Gribenko   StringRef File = SourceMgr.getBufferData(LocInfo.first, &InvalidTemp);
9825188c4b9SDmitri Gribenko   if (InvalidTemp) {
9835188c4b9SDmitri Gribenko     *Invalid = true;
9845188c4b9SDmitri Gribenko     return StringRef();
9855188c4b9SDmitri Gribenko   }
9865188c4b9SDmitri Gribenko 
9875188c4b9SDmitri Gribenko   const char *Begin = File.data() + LocInfo.second;
9885188c4b9SDmitri Gribenko   return StringRef(Begin, Tok.getLength());
9895188c4b9SDmitri Gribenko }
9905188c4b9SDmitri Gribenko 
9915188c4b9SDmitri Gribenko } // end namespace comments
9925188c4b9SDmitri Gribenko } // end namespace clang
9935188c4b9SDmitri Gribenko 
994