/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/utils/TableGen/ClangSyntaxEmitter.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- ClangSyntaxEmitter.cpp - Generate clang Syntax Tree nodes ----------===// |
2 | | // |
3 | | // The LLVM Compiler Infrastructure |
4 | | // |
5 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
6 | | // See https://llvm.org/LICENSE.txt for license information. |
7 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
8 | | // |
9 | | //===----------------------------------------------------------------------===// |
10 | | // |
11 | | // These backends consume the definitions of Syntax Tree nodes. |
12 | | // See clang/include/clang/Tooling/Syntax/{Syntax,Nodes}.td |
13 | | // |
14 | | // The -gen-clang-syntax-node-list backend produces a .inc with macro calls |
15 | | // NODE(Kind, BaseKind) |
16 | | // ABSTRACT_NODE(Type, Base, FirstKind, LastKind) |
17 | | // similar to those for AST nodes such as AST/DeclNodes.inc. |
18 | | // |
19 | | // The -gen-clang-syntax-node-classes backend produces definitions for the |
20 | | // syntax::Node subclasses (except those marked as External). |
21 | | // |
22 | | // In future, another backend will encode the structure of the various node |
23 | | // types in tables so their invariants can be checked and enforced. |
24 | | // |
25 | | //===----------------------------------------------------------------------===// |
26 | | #include "TableGenBackends.h" |
27 | | |
28 | | #include <deque> |
29 | | |
30 | | #include "llvm/ADT/StringExtras.h" |
31 | | #include "llvm/Support/FormatVariadic.h" |
32 | | #include "llvm/Support/raw_ostream.h" |
33 | | #include "llvm/TableGen/Record.h" |
34 | | #include "llvm/TableGen/TableGenBackend.h" |
35 | | |
36 | | namespace { |
37 | | using llvm::formatv; |
38 | | |
39 | | // The class hierarchy of Node types. |
40 | | // We assemble this in order to be able to define the NodeKind enum in a |
41 | | // stable and useful way, where abstract Node subclasses correspond to ranges. |
42 | | class Hierarchy { |
43 | | public: |
44 | 0 | Hierarchy(const llvm::RecordKeeper &Records) { |
45 | 0 | for (llvm::Record *T : Records.getAllDerivedDefinitions("NodeType")) |
46 | 0 | add(T); |
47 | 0 | for (llvm::Record *Derived : Records.getAllDerivedDefinitions("NodeType")) |
48 | 0 | if (llvm::Record *Base = Derived->getValueAsOptionalDef("base")) |
49 | 0 | link(Derived, Base); |
50 | 0 | for (NodeType &N : AllTypes) { |
51 | 0 | llvm::sort(N.Derived, [](const NodeType *L, const NodeType *R) { |
52 | 0 | return L->Record->getName() < R->Record->getName(); |
53 | 0 | }); |
54 | | // Alternatives nodes must have subclasses, External nodes may do. |
55 | 0 | assert(N.Record->isSubClassOf("Alternatives") || |
56 | 0 | N.Record->isSubClassOf("External") || N.Derived.empty()); |
57 | 0 | assert(!N.Record->isSubClassOf("Alternatives") || !N.Derived.empty()); |
58 | 0 | } |
59 | 0 | } |
60 | | |
61 | | struct NodeType { |
62 | | const llvm::Record *Record = nullptr; |
63 | | const NodeType *Base = nullptr; |
64 | | std::vector<const NodeType *> Derived; |
65 | 0 | llvm::StringRef name() const { return Record->getName(); } |
66 | | }; |
67 | | |
68 | 0 | NodeType &get(llvm::StringRef Name = "Node") { |
69 | 0 | auto NI = ByName.find(Name); |
70 | 0 | assert(NI != ByName.end() && "no such node"); |
71 | 0 | return *NI->second; |
72 | 0 | } |
73 | | |
74 | | // Traverse the hierarchy in pre-order (base classes before derived). |
75 | | void visit(llvm::function_ref<void(const NodeType &)> CB, |
76 | 0 | const NodeType *Start = nullptr) { |
77 | 0 | if (Start == nullptr) |
78 | 0 | Start = &get(); |
79 | 0 | CB(*Start); |
80 | 0 | for (const NodeType *D : Start->Derived) |
81 | 0 | visit(CB, D); |
82 | 0 | } |
83 | | |
84 | | private: |
85 | 0 | void add(const llvm::Record *R) { |
86 | 0 | AllTypes.emplace_back(); |
87 | 0 | AllTypes.back().Record = R; |
88 | 0 | bool Inserted = ByName.try_emplace(R->getName(), &AllTypes.back()).second; |
89 | 0 | assert(Inserted && "Duplicate node name"); |
90 | 0 | (void)Inserted; |
91 | 0 | } |
92 | | |
93 | 0 | void link(const llvm::Record *Derived, const llvm::Record *Base) { |
94 | 0 | auto &CN = get(Derived->getName()), &PN = get(Base->getName()); |
95 | 0 | assert(CN.Base == nullptr && "setting base twice"); |
96 | 0 | PN.Derived.push_back(&CN); |
97 | 0 | CN.Base = &PN; |
98 | 0 | } |
99 | | |
100 | | std::deque<NodeType> AllTypes; |
101 | | llvm::DenseMap<llvm::StringRef, NodeType *> ByName; |
102 | | }; |
103 | | |
104 | 0 | const Hierarchy::NodeType &firstConcrete(const Hierarchy::NodeType &N) { |
105 | 0 | return N.Derived.empty() ? N : firstConcrete(*N.Derived.front()); |
106 | 0 | } |
107 | 0 | const Hierarchy::NodeType &lastConcrete(const Hierarchy::NodeType &N) { |
108 | 0 | return N.Derived.empty() ? N : lastConcrete(*N.Derived.back()); |
109 | 0 | } |
110 | | |
111 | | struct SyntaxConstraint { |
112 | 0 | SyntaxConstraint(const llvm::Record &R) { |
113 | 0 | if (R.isSubClassOf("Optional")) { |
114 | 0 | *this = SyntaxConstraint(*R.getValueAsDef("inner")); |
115 | 0 | } else if (R.isSubClassOf("AnyToken")) { |
116 | 0 | NodeType = "Leaf"; |
117 | 0 | } else if (R.isSubClassOf("NodeType")) { |
118 | 0 | NodeType = R.getName().str(); |
119 | 0 | } else { |
120 | 0 | assert(false && "Unhandled Syntax kind"); |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | | std::string NodeType; |
125 | | // optional and leaf types also go here, once we want to use them. |
126 | | }; |
127 | | |
128 | | } // namespace |
129 | | |
130 | | void clang::EmitClangSyntaxNodeList(llvm::RecordKeeper &Records, |
131 | 0 | llvm::raw_ostream &OS) { |
132 | 0 | llvm::emitSourceFileHeader("Syntax tree node list", OS); |
133 | 0 | Hierarchy H(Records); |
134 | 0 | OS << R"cpp( |
135 | 0 | #ifndef NODE |
136 | 0 | #define NODE(Kind, Base) |
137 | 0 | #endif |
138 | 0 |
|
139 | 0 | #ifndef CONCRETE_NODE |
140 | 0 | #define CONCRETE_NODE(Kind, Base) NODE(Kind, Base) |
141 | 0 | #endif |
142 | 0 |
|
143 | 0 | #ifndef ABSTRACT_NODE |
144 | 0 | #define ABSTRACT_NODE(Kind, Base, First, Last) NODE(Kind, Base) |
145 | 0 | #endif |
146 | 0 |
|
147 | 0 | )cpp"; |
148 | 0 | H.visit([&](const Hierarchy::NodeType &N) { |
149 | | // Don't emit ABSTRACT_NODE for node itself, which has no parent. |
150 | 0 | if (N.Base == nullptr) |
151 | 0 | return; |
152 | 0 | if (N.Derived.empty()) |
153 | 0 | OS << formatv("CONCRETE_NODE({0},{1})\n", N.name(), N.Base->name()); |
154 | 0 | else |
155 | 0 | OS << formatv("ABSTRACT_NODE({0},{1},{2},{3})\n", N.name(), |
156 | 0 | N.Base->name(), firstConcrete(N).name(), |
157 | 0 | lastConcrete(N).name()); |
158 | 0 | }); |
159 | 0 | OS << R"cpp( |
160 | 0 | #undef NODE |
161 | 0 | #undef CONCRETE_NODE |
162 | 0 | #undef ABSTRACT_NODE |
163 | 0 | )cpp"; |
164 | 0 | } |
165 | | |
166 | | // Format a documentation string as a C++ comment. |
167 | | // Trims leading whitespace handling since comments come from a TableGen file: |
168 | | // documentation = [{ |
169 | | // This is a widget. Example: |
170 | | // widget.explode() |
171 | | // }]; |
172 | | // and should be formatted as: |
173 | | // /// This is a widget. Example: |
174 | | // /// widget.explode() |
175 | | // Leading and trailing whitespace lines are stripped. |
176 | | // The indentation of the first line is stripped from all lines. |
177 | 0 | static void printDoc(llvm::StringRef Doc, llvm::raw_ostream &OS) { |
178 | 0 | Doc = Doc.rtrim(); |
179 | 0 | llvm::StringRef Line; |
180 | 0 | while (Line.trim().empty() && !Doc.empty()) |
181 | 0 | std::tie(Line, Doc) = Doc.split('\n'); |
182 | 0 | llvm::StringRef Indent = Line.take_while(llvm::isSpace); |
183 | 0 | for (; !Line.empty() || !Doc.empty(); std::tie(Line, Doc) = Doc.split('\n')) { |
184 | 0 | Line.consume_front(Indent); |
185 | 0 | OS << "/// " << Line << "\n"; |
186 | 0 | } |
187 | 0 | } |
188 | | |
189 | | void clang::EmitClangSyntaxNodeClasses(llvm::RecordKeeper &Records, |
190 | 0 | llvm::raw_ostream &OS) { |
191 | 0 | llvm::emitSourceFileHeader("Syntax tree node list", OS); |
192 | 0 | Hierarchy H(Records); |
193 | |
|
194 | 0 | OS << "\n// Forward-declare node types so we don't have to carefully " |
195 | 0 | "sequence definitions.\n"; |
196 | 0 | H.visit([&](const Hierarchy::NodeType &N) { |
197 | 0 | OS << "class " << N.name() << ";\n"; |
198 | 0 | }); |
199 | |
|
200 | 0 | OS << "\n// Node definitions\n\n"; |
201 | 0 | H.visit([&](const Hierarchy::NodeType &N) { |
202 | 0 | if (N.Record->isSubClassOf("External")) |
203 | 0 | return; |
204 | 0 | printDoc(N.Record->getValueAsString("documentation"), OS); |
205 | 0 | OS << formatv("class {0}{1} : public {2} {{\n", N.name(), |
206 | 0 | N.Derived.empty() ? " final" : "", N.Base->name()); |
207 | | |
208 | | // Constructor. |
209 | 0 | if (N.Derived.empty()) |
210 | 0 | OS << formatv("public:\n {0}() : {1}(NodeKind::{0}) {{}\n", N.name(), |
211 | 0 | N.Base->name()); |
212 | 0 | else |
213 | 0 | OS << formatv("protected:\n {0}(NodeKind K) : {1}(K) {{}\npublic:\n", |
214 | 0 | N.name(), N.Base->name()); |
215 | |
|
216 | 0 | if (N.Record->isSubClassOf("Sequence")) { |
217 | | // Getters for sequence elements. |
218 | 0 | for (const auto &C : N.Record->getValueAsListOfDefs("children")) { |
219 | 0 | assert(C->isSubClassOf("Role")); |
220 | 0 | llvm::StringRef Role = C->getValueAsString("role"); |
221 | 0 | SyntaxConstraint Constraint(*C->getValueAsDef("syntax")); |
222 | 0 | for (const char *Const : {"", "const "}) |
223 | 0 | OS << formatv( |
224 | 0 | " {2}{1} *get{0}() {2} {{\n" |
225 | 0 | " return llvm::cast_or_null<{1}>(findChild(NodeRole::{0}));\n" |
226 | 0 | " }\n", |
227 | 0 | Role, Constraint.NodeType, Const); |
228 | 0 | } |
229 | 0 | } |
230 | | |
231 | | // classof. FIXME: move definition inline once ~all nodes are generated. |
232 | 0 | OS << " static bool classof(const Node *N);\n"; |
233 | |
|
234 | 0 | OS << "};\n\n"; |
235 | 0 | }); |
236 | 0 | } |