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