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(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 | std::vector<Record *> Tags = Records.getAllDerivedDefinitions(ClassName: "Command" ); |
31 | for (size_t i = 0, e = Tags.size(); i != e; ++i) { |
32 | 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 | Record &Tag = *Tags[i]; |
66 | std::string Name = std::string(Tag.getValueAsString(FieldName: "Name" )); |
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 (unsigned i = 0, e = Str.size(); i != e; ++i) { |
82 | switch (Str[i]) { |
83 | default: |
84 | Mangled += Str[i]; |
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(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 | std::vector<Record *> Tags = Records.getAllDerivedDefinitions(ClassName: "Command" ); |
125 | for (size_t i = 0, e = Tags.size(); i != e; ++i) { |
126 | Record &Tag = *Tags[i]; |
127 | std::string MangledName = MangleName(Str: Tag.getValueAsString(FieldName: "Name" )); |
128 | |
129 | OS << "COMMENT_COMMAND(" << MangledName << ")\n" ; |
130 | } |
131 | } |
132 | |