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