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