| 1 | //===-- ClangCommentCommandInfoEmitter.cpp - Generate command lists -------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This tablegen backend emits command lists and efficient matchers for command |
| 10 | // names that are used in documentation comments. |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "TableGenBackends.h" |
| 15 | |
| 16 | #include "llvm/TableGen/Record.h" |
| 17 | #include "llvm/TableGen/StringMatcher.h" |
| 18 | #include "llvm/TableGen/TableGenBackend.h" |
| 19 | #include <vector> |
| 20 | |
| 21 | using namespace llvm; |
| 22 | |
| 23 | void clang::EmitClangCommentCommandInfo(const RecordKeeper &Records, |
| 24 | raw_ostream &OS) { |
| 25 | emitSourceFileHeader(Desc: "A list of commands useable in documentation comments" , |
| 26 | OS, Record: Records); |
| 27 | |
| 28 | OS << "namespace {\n" |
| 29 | "const CommandInfo Commands[] = {\n" ; |
| 30 | ArrayRef<const Record *> Tags = Records.getAllDerivedDefinitions(ClassName: "Command" ); |
| 31 | for (size_t i = 0, e = Tags.size(); i != e; ++i) { |
| 32 | const Record &Tag = *Tags[i]; |
| 33 | OS << " { " |
| 34 | << "\"" << Tag.getValueAsString(FieldName: "Name" ) << "\", " |
| 35 | << "\"" << Tag.getValueAsString(FieldName: "EndCommandName" ) << "\", " << i << ", " |
| 36 | << Tag.getValueAsInt(FieldName: "NumArgs" ) << ", " |
| 37 | << Tag.getValueAsBit(FieldName: "IsInlineCommand" ) << ", " |
| 38 | << Tag.getValueAsBit(FieldName: "IsBlockCommand" ) << ", " |
| 39 | << Tag.getValueAsBit(FieldName: "IsBriefCommand" ) << ", " |
| 40 | << Tag.getValueAsBit(FieldName: "IsReturnsCommand" ) << ", " |
| 41 | << Tag.getValueAsBit(FieldName: "IsParamCommand" ) << ", " |
| 42 | << Tag.getValueAsBit(FieldName: "IsTParamCommand" ) << ", " |
| 43 | << Tag.getValueAsBit(FieldName: "IsThrowsCommand" ) << ", " |
| 44 | << Tag.getValueAsBit(FieldName: "IsDeprecatedCommand" ) << ", " |
| 45 | << Tag.getValueAsBit(FieldName: "IsHeaderfileCommand" ) << ", " |
| 46 | << Tag.getValueAsBit(FieldName: "IsParCommand" ) << ", " |
| 47 | << Tag.getValueAsBit(FieldName: "IsEmptyParagraphAllowed" ) << ", " |
| 48 | << Tag.getValueAsBit(FieldName: "IsVerbatimBlockCommand" ) << ", " |
| 49 | << Tag.getValueAsBit(FieldName: "IsVerbatimBlockEndCommand" ) << ", " |
| 50 | << Tag.getValueAsBit(FieldName: "IsVerbatimLineCommand" ) << ", " |
| 51 | << Tag.getValueAsBit(FieldName: "IsDeclarationCommand" ) << ", " |
| 52 | << Tag.getValueAsBit(FieldName: "IsFunctionDeclarationCommand" ) << ", " |
| 53 | << Tag.getValueAsBit(FieldName: "IsRecordLikeDetailCommand" ) << ", " |
| 54 | << Tag.getValueAsBit(FieldName: "IsRecordLikeDeclarationCommand" ) << ", " |
| 55 | << /* IsUnknownCommand = */ "0" << " }" ; |
| 56 | if (i + 1 != e) |
| 57 | OS << "," ; |
| 58 | OS << "\n" ; |
| 59 | } |
| 60 | OS << "};\n" |
| 61 | "} // unnamed namespace\n\n" ; |
| 62 | |
| 63 | std::vector<StringMatcher::StringPair> Matches; |
| 64 | for (size_t i = 0, e = Tags.size(); i != e; ++i) { |
| 65 | const Record &Tag = *Tags[i]; |
| 66 | std::string Name = Tag.getValueAsString(FieldName: "Name" ).str(); |
| 67 | std::string Return; |
| 68 | raw_string_ostream(Return) << "return &Commands[" << i << "];" ; |
| 69 | Matches.emplace_back(args: std::move(Name), args: std::move(Return)); |
| 70 | } |
| 71 | |
| 72 | OS << "const CommandInfo *CommandTraits::getBuiltinCommandInfo(\n" |
| 73 | << " StringRef Name) {\n" ; |
| 74 | StringMatcher("Name" , Matches, OS).Emit(); |
| 75 | OS << " return nullptr;\n" |
| 76 | << "}\n\n" ; |
| 77 | } |
| 78 | |
| 79 | static std::string MangleName(StringRef Str) { |
| 80 | std::string Mangled; |
| 81 | for (char C : Str) { |
| 82 | switch (C) { |
| 83 | default: |
| 84 | Mangled += C; |
| 85 | break; |
| 86 | case '(': |
| 87 | Mangled += "lparen" ; |
| 88 | break; |
| 89 | case ')': |
| 90 | Mangled += "rparen" ; |
| 91 | break; |
| 92 | case '[': |
| 93 | Mangled += "lsquare" ; |
| 94 | break; |
| 95 | case ']': |
| 96 | Mangled += "rsquare" ; |
| 97 | break; |
| 98 | case '{': |
| 99 | Mangled += "lbrace" ; |
| 100 | break; |
| 101 | case '}': |
| 102 | Mangled += "rbrace" ; |
| 103 | break; |
| 104 | case '$': |
| 105 | Mangled += "dollar" ; |
| 106 | break; |
| 107 | case '/': |
| 108 | Mangled += "slash" ; |
| 109 | break; |
| 110 | } |
| 111 | } |
| 112 | return Mangled; |
| 113 | } |
| 114 | |
| 115 | void clang::EmitClangCommentCommandList(const RecordKeeper &Records, |
| 116 | raw_ostream &OS) { |
| 117 | emitSourceFileHeader(Desc: "A list of commands useable in documentation comments" , |
| 118 | OS, Record: Records); |
| 119 | |
| 120 | OS << "#ifndef COMMENT_COMMAND\n" |
| 121 | << "# define COMMENT_COMMAND(NAME)\n" |
| 122 | << "#endif\n" ; |
| 123 | |
| 124 | ArrayRef<const Record *> Tags = Records.getAllDerivedDefinitions(ClassName: "Command" ); |
| 125 | for (const Record *Tag : Tags) { |
| 126 | std::string MangledName = MangleName(Str: Tag->getValueAsString(FieldName: "Name" )); |
| 127 | |
| 128 | OS << "COMMENT_COMMAND(" << MangledName << ")\n" ; |
| 129 | } |
| 130 | } |
| 131 | |