1*f4a2713aSLionel Sambuc //===--- CommentBriefParser.cpp - Dumb comment parser ---------------------===// 2*f4a2713aSLionel Sambuc // 3*f4a2713aSLionel Sambuc // The LLVM Compiler Infrastructure 4*f4a2713aSLionel Sambuc // 5*f4a2713aSLionel Sambuc // This file is distributed under the University of Illinois Open Source 6*f4a2713aSLionel Sambuc // License. See LICENSE.TXT for details. 7*f4a2713aSLionel Sambuc // 8*f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===// 9*f4a2713aSLionel Sambuc 10*f4a2713aSLionel Sambuc #include "clang/AST/CommentBriefParser.h" 11*f4a2713aSLionel Sambuc #include "clang/AST/CommentCommandTraits.h" 12*f4a2713aSLionel Sambuc #include "llvm/ADT/StringSwitch.h" 13*f4a2713aSLionel Sambuc 14*f4a2713aSLionel Sambuc namespace clang { 15*f4a2713aSLionel Sambuc namespace comments { 16*f4a2713aSLionel Sambuc 17*f4a2713aSLionel Sambuc namespace { isWhitespace(char C)18*f4a2713aSLionel Sambucinline bool isWhitespace(char C) { 19*f4a2713aSLionel Sambuc return C == ' ' || C == '\n' || C == '\r' || 20*f4a2713aSLionel Sambuc C == '\t' || C == '\f' || C == '\v'; 21*f4a2713aSLionel Sambuc } 22*f4a2713aSLionel Sambuc 23*f4a2713aSLionel Sambuc /// Convert all whitespace into spaces, remove leading and trailing spaces, 24*f4a2713aSLionel Sambuc /// compress multiple spaces into one. cleanupBrief(std::string & S)25*f4a2713aSLionel Sambucvoid cleanupBrief(std::string &S) { 26*f4a2713aSLionel Sambuc bool PrevWasSpace = true; 27*f4a2713aSLionel Sambuc std::string::iterator O = S.begin(); 28*f4a2713aSLionel Sambuc for (std::string::iterator I = S.begin(), E = S.end(); 29*f4a2713aSLionel Sambuc I != E; ++I) { 30*f4a2713aSLionel Sambuc const char C = *I; 31*f4a2713aSLionel Sambuc if (isWhitespace(C)) { 32*f4a2713aSLionel Sambuc if (!PrevWasSpace) { 33*f4a2713aSLionel Sambuc *O++ = ' '; 34*f4a2713aSLionel Sambuc PrevWasSpace = true; 35*f4a2713aSLionel Sambuc } 36*f4a2713aSLionel Sambuc continue; 37*f4a2713aSLionel Sambuc } else { 38*f4a2713aSLionel Sambuc *O++ = C; 39*f4a2713aSLionel Sambuc PrevWasSpace = false; 40*f4a2713aSLionel Sambuc } 41*f4a2713aSLionel Sambuc } 42*f4a2713aSLionel Sambuc if (O != S.begin() && *(O - 1) == ' ') 43*f4a2713aSLionel Sambuc --O; 44*f4a2713aSLionel Sambuc 45*f4a2713aSLionel Sambuc S.resize(O - S.begin()); 46*f4a2713aSLionel Sambuc } 47*f4a2713aSLionel Sambuc isWhitespace(StringRef Text)48*f4a2713aSLionel Sambucbool isWhitespace(StringRef Text) { 49*f4a2713aSLionel Sambuc for (StringRef::const_iterator I = Text.begin(), E = Text.end(); 50*f4a2713aSLionel Sambuc I != E; ++I) { 51*f4a2713aSLionel Sambuc if (!isWhitespace(*I)) 52*f4a2713aSLionel Sambuc return false; 53*f4a2713aSLionel Sambuc } 54*f4a2713aSLionel Sambuc return true; 55*f4a2713aSLionel Sambuc } 56*f4a2713aSLionel Sambuc } // unnamed namespace 57*f4a2713aSLionel Sambuc BriefParser(Lexer & L,const CommandTraits & Traits)58*f4a2713aSLionel SambucBriefParser::BriefParser(Lexer &L, const CommandTraits &Traits) : 59*f4a2713aSLionel Sambuc L(L), Traits(Traits) { 60*f4a2713aSLionel Sambuc // Get lookahead token. 61*f4a2713aSLionel Sambuc ConsumeToken(); 62*f4a2713aSLionel Sambuc } 63*f4a2713aSLionel Sambuc Parse()64*f4a2713aSLionel Sambucstd::string BriefParser::Parse() { 65*f4a2713aSLionel Sambuc std::string FirstParagraphOrBrief; 66*f4a2713aSLionel Sambuc std::string ReturnsParagraph; 67*f4a2713aSLionel Sambuc bool InFirstParagraph = true; 68*f4a2713aSLionel Sambuc bool InBrief = false; 69*f4a2713aSLionel Sambuc bool InReturns = false; 70*f4a2713aSLionel Sambuc 71*f4a2713aSLionel Sambuc while (Tok.isNot(tok::eof)) { 72*f4a2713aSLionel Sambuc if (Tok.is(tok::text)) { 73*f4a2713aSLionel Sambuc if (InFirstParagraph || InBrief) 74*f4a2713aSLionel Sambuc FirstParagraphOrBrief += Tok.getText(); 75*f4a2713aSLionel Sambuc else if (InReturns) 76*f4a2713aSLionel Sambuc ReturnsParagraph += Tok.getText(); 77*f4a2713aSLionel Sambuc ConsumeToken(); 78*f4a2713aSLionel Sambuc continue; 79*f4a2713aSLionel Sambuc } 80*f4a2713aSLionel Sambuc 81*f4a2713aSLionel Sambuc if (Tok.is(tok::backslash_command) || Tok.is(tok::at_command)) { 82*f4a2713aSLionel Sambuc const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID()); 83*f4a2713aSLionel Sambuc if (Info->IsBriefCommand) { 84*f4a2713aSLionel Sambuc FirstParagraphOrBrief.clear(); 85*f4a2713aSLionel Sambuc InBrief = true; 86*f4a2713aSLionel Sambuc ConsumeToken(); 87*f4a2713aSLionel Sambuc continue; 88*f4a2713aSLionel Sambuc } 89*f4a2713aSLionel Sambuc if (Info->IsReturnsCommand) { 90*f4a2713aSLionel Sambuc InReturns = true; 91*f4a2713aSLionel Sambuc InBrief = false; 92*f4a2713aSLionel Sambuc InFirstParagraph = false; 93*f4a2713aSLionel Sambuc ReturnsParagraph += "Returns "; 94*f4a2713aSLionel Sambuc ConsumeToken(); 95*f4a2713aSLionel Sambuc continue; 96*f4a2713aSLionel Sambuc } 97*f4a2713aSLionel Sambuc // Block commands implicitly start a new paragraph. 98*f4a2713aSLionel Sambuc if (Info->IsBlockCommand) { 99*f4a2713aSLionel Sambuc // We found an implicit paragraph end. 100*f4a2713aSLionel Sambuc InFirstParagraph = false; 101*f4a2713aSLionel Sambuc if (InBrief) 102*f4a2713aSLionel Sambuc break; 103*f4a2713aSLionel Sambuc } 104*f4a2713aSLionel Sambuc } 105*f4a2713aSLionel Sambuc 106*f4a2713aSLionel Sambuc if (Tok.is(tok::newline)) { 107*f4a2713aSLionel Sambuc if (InFirstParagraph || InBrief) 108*f4a2713aSLionel Sambuc FirstParagraphOrBrief += ' '; 109*f4a2713aSLionel Sambuc else if (InReturns) 110*f4a2713aSLionel Sambuc ReturnsParagraph += ' '; 111*f4a2713aSLionel Sambuc ConsumeToken(); 112*f4a2713aSLionel Sambuc 113*f4a2713aSLionel Sambuc // If the next token is a whitespace only text, ignore it. Thus we allow 114*f4a2713aSLionel Sambuc // two paragraphs to be separated by line that has only whitespace in it. 115*f4a2713aSLionel Sambuc // 116*f4a2713aSLionel Sambuc // We don't need to add a space to the parsed text because we just added 117*f4a2713aSLionel Sambuc // a space for the newline. 118*f4a2713aSLionel Sambuc if (Tok.is(tok::text)) { 119*f4a2713aSLionel Sambuc if (isWhitespace(Tok.getText())) 120*f4a2713aSLionel Sambuc ConsumeToken(); 121*f4a2713aSLionel Sambuc } 122*f4a2713aSLionel Sambuc 123*f4a2713aSLionel Sambuc if (Tok.is(tok::newline)) { 124*f4a2713aSLionel Sambuc ConsumeToken(); 125*f4a2713aSLionel Sambuc // We found a paragraph end. This ends the brief description if 126*f4a2713aSLionel Sambuc // \\brief command or its equivalent was explicitly used. 127*f4a2713aSLionel Sambuc // Stop scanning text because an explicit \\brief paragraph is the 128*f4a2713aSLionel Sambuc // preffered one. 129*f4a2713aSLionel Sambuc if (InBrief) 130*f4a2713aSLionel Sambuc break; 131*f4a2713aSLionel Sambuc // End first paragraph if we found some non-whitespace text. 132*f4a2713aSLionel Sambuc if (InFirstParagraph && !isWhitespace(FirstParagraphOrBrief)) 133*f4a2713aSLionel Sambuc InFirstParagraph = false; 134*f4a2713aSLionel Sambuc // End the \\returns paragraph because we found the paragraph end. 135*f4a2713aSLionel Sambuc InReturns = false; 136*f4a2713aSLionel Sambuc } 137*f4a2713aSLionel Sambuc continue; 138*f4a2713aSLionel Sambuc } 139*f4a2713aSLionel Sambuc 140*f4a2713aSLionel Sambuc // We didn't handle this token, so just drop it. 141*f4a2713aSLionel Sambuc ConsumeToken(); 142*f4a2713aSLionel Sambuc } 143*f4a2713aSLionel Sambuc 144*f4a2713aSLionel Sambuc cleanupBrief(FirstParagraphOrBrief); 145*f4a2713aSLionel Sambuc if (!FirstParagraphOrBrief.empty()) 146*f4a2713aSLionel Sambuc return FirstParagraphOrBrief; 147*f4a2713aSLionel Sambuc 148*f4a2713aSLionel Sambuc cleanupBrief(ReturnsParagraph); 149*f4a2713aSLionel Sambuc return ReturnsParagraph; 150*f4a2713aSLionel Sambuc } 151*f4a2713aSLionel Sambuc 152*f4a2713aSLionel Sambuc } // end namespace comments 153*f4a2713aSLionel Sambuc } // end namespace clang 154*f4a2713aSLionel Sambuc 155*f4a2713aSLionel Sambuc 156