12d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko//===--- CommentBriefParser.cpp - Dumb comment parser ---------------------===//
22d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko//
32d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko//                     The LLVM Compiler Infrastructure
42d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko//
52d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko// This file is distributed under the University of Illinois Open Source
62d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko// License. See LICENSE.TXT for details.
72d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko//
82d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko//===----------------------------------------------------------------------===//
92d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko
102d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko#include "clang/AST/CommentBriefParser.h"
11aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko#include "clang/AST/CommentCommandTraits.h"
1255e1808c6064ace116e458ab82abf33d3f83659aDmitri Gribenko#include "llvm/ADT/StringSwitch.h"
132d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko
142d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenkonamespace clang {
152d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenkonamespace comments {
162d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko
17d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenkonamespace {
180ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenkoinline bool isWhitespace(char C) {
190ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko  return C == ' ' || C == '\n' || C == '\r' ||
200ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko         C == '\t' || C == '\f' || C == '\v';
210ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko}
220ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko
23d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko/// Convert all whitespace into spaces, remove leading and trailing spaces,
24d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko/// compress multiple spaces into one.
25d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenkovoid cleanupBrief(std::string &S) {
26d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko  bool PrevWasSpace = true;
27d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko  std::string::iterator O = S.begin();
28d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko  for (std::string::iterator I = S.begin(), E = S.end();
29d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko       I != E; ++I) {
30d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko    const char C = *I;
310ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko    if (isWhitespace(C)) {
32d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko      if (!PrevWasSpace) {
33d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko        *O++ = ' ';
34d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko        PrevWasSpace = true;
35d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko      }
36d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko      continue;
37d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko    } else {
38d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko      *O++ = C;
39d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko      PrevWasSpace = false;
40d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko    }
41d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko  }
42d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko  if (O != S.begin() && *(O - 1) == ' ')
43d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko    --O;
44d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko
45d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko  S.resize(O - S.begin());
46d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko}
470ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko
480ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenkobool isWhitespace(StringRef Text) {
490ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko  for (StringRef::const_iterator I = Text.begin(), E = Text.end();
500ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko       I != E; ++I) {
510ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko    if (!isWhitespace(*I))
520ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko      return false;
530ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko  }
540ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko  return true;
550ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko}
56aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko} // unnamed namespace
5755e1808c6064ace116e458ab82abf33d3f83659aDmitri Gribenko
58aa58081902ad31927df02e8537d972eabe29d6dfDmitri GribenkoBriefParser::BriefParser(Lexer &L, const CommandTraits &Traits) :
59aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko    L(L), Traits(Traits) {
60aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko  // Get lookahead token.
61aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko  ConsumeToken();
6255e1808c6064ace116e458ab82abf33d3f83659aDmitri Gribenko}
63d558b5238df74ef3cb76d7125375a5c28fe0eaa9Dmitri Gribenko
642d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenkostd::string BriefParser::Parse() {
6572021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko  std::string FirstParagraphOrBrief;
6672021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko  std::string ReturnsParagraph;
672d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko  bool InFirstParagraph = true;
682d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko  bool InBrief = false;
6972021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko  bool InReturns = false;
702d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko
712d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko  while (Tok.isNot(tok::eof)) {
722d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko    if (Tok.is(tok::text)) {
73c0b8324d6f68dfc2221257cdb83e39b974431c0bDmitri Gribenko      if (InFirstParagraph || InBrief)
7472021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko        FirstParagraphOrBrief += Tok.getText();
7572021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko      else if (InReturns)
7672021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko        ReturnsParagraph += Tok.getText();
772d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko      ConsumeToken();
782d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko      continue;
792d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko    }
802d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko
818536fa14ee1048e5e2d62cb3dc11fc640c7dc00dFariborz Jahanian    if (Tok.is(tok::backslash_command) || Tok.is(tok::at_command)) {
82e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko      const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
83e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko      if (Info->IsBriefCommand) {
8472021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko        FirstParagraphOrBrief.clear();
85f199b9cd4af562029a3c7b82c4672819b2c39e70Dmitri Gribenko        InBrief = true;
86f199b9cd4af562029a3c7b82c4672819b2c39e70Dmitri Gribenko        ConsumeToken();
87f199b9cd4af562029a3c7b82c4672819b2c39e70Dmitri Gribenko        continue;
88f199b9cd4af562029a3c7b82c4672819b2c39e70Dmitri Gribenko      }
89e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko      if (Info->IsReturnsCommand) {
9072021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko        InReturns = true;
910ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko        InBrief = false;
920ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko        InFirstParagraph = false;
9372021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko        ReturnsParagraph += "Returns ";
940ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko        ConsumeToken();
950ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko        continue;
9672021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko      }
9755e1808c6064ace116e458ab82abf33d3f83659aDmitri Gribenko      // Block commands implicitly start a new paragraph.
98e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko      if (Info->IsBlockCommand) {
99f199b9cd4af562029a3c7b82c4672819b2c39e70Dmitri Gribenko        // We found an implicit paragraph end.
100f199b9cd4af562029a3c7b82c4672819b2c39e70Dmitri Gribenko        InFirstParagraph = false;
10157aceb227d192ce41f5412a53d451354e90dd792Dmitri Gribenko        if (InBrief)
102f199b9cd4af562029a3c7b82c4672819b2c39e70Dmitri Gribenko          break;
103f199b9cd4af562029a3c7b82c4672819b2c39e70Dmitri Gribenko      }
1042d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko    }
1052d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko
1062d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko    if (Tok.is(tok::newline)) {
107c0b8324d6f68dfc2221257cdb83e39b974431c0bDmitri Gribenko      if (InFirstParagraph || InBrief)
10872021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko        FirstParagraphOrBrief += ' ';
10972021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko      else if (InReturns)
11072021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko        ReturnsParagraph += ' ';
1112d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko      ConsumeToken();
1122d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko
1130ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko      // If the next token is a whitespace only text, ignore it.  Thus we allow
1140ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko      // two paragraphs to be separated by line that has only whitespace in it.
1150ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko      //
1160ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko      // We don't need to add a space to the parsed text because we just added
1170ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko      // a space for the newline.
1180ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko      if (Tok.is(tok::text)) {
1190ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko        if (isWhitespace(Tok.getText()))
1200ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko          ConsumeToken();
1210ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko      }
1220ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko
1232d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko      if (Tok.is(tok::newline)) {
1242d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko        ConsumeToken();
1250ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko        // We found a paragraph end.  This ends the brief description if
1260ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko        // \\brief command or its equivalent was explicitly used.
1270ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko        // Stop scanning text because an explicit \\brief paragraph is the
1280ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko        // preffered one.
12957aceb227d192ce41f5412a53d451354e90dd792Dmitri Gribenko        if (InBrief)
130f199b9cd4af562029a3c7b82c4672819b2c39e70Dmitri Gribenko          break;
1310ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko        // End first paragraph if we found some non-whitespace text.
1320ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko        if (InFirstParagraph && !isWhitespace(FirstParagraphOrBrief))
1330ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko          InFirstParagraph = false;
1340ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko        // End the \\returns paragraph because we found the paragraph end.
1350ac4ec7f1c79a13f813fa644997a4b89ac0ba3aaDmitri Gribenko        InReturns = false;
1362d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko      }
1372d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko      continue;
1382d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko    }
1392d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko
1402d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko    // We didn't handle this token, so just drop it.
1412d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko    ConsumeToken();
1422d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko  }
1432d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko
14472021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko  cleanupBrief(FirstParagraphOrBrief);
14572021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko  if (!FirstParagraphOrBrief.empty())
14672021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko    return FirstParagraphOrBrief;
14772021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko
14872021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko  cleanupBrief(ReturnsParagraph);
14972021ff4038cbc48b09a3acb743e319809f086dbDmitri Gribenko  return ReturnsParagraph;
1502d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko}
1512d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko
1522d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko} // end namespace comments
1532d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko} // end namespace clang
1542d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko
1552d44d77fed3200e2eff289f55493317e90d3398cDmitri Gribenko
156