Path: blob/main/contrib/llvm-project/clang/utils/TableGen/ClangSyntaxEmitter.cpp
35231 views
//===- ClangSyntaxEmitter.cpp - Generate clang Syntax Tree nodes ----------===//1//2// The LLVM Compiler Infrastructure3//4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.5// See https://llvm.org/LICENSE.txt for license information.6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception7//8//===----------------------------------------------------------------------===//9//10// These backends consume the definitions of Syntax Tree nodes.11// See clang/include/clang/Tooling/Syntax/{Syntax,Nodes}.td12//13// The -gen-clang-syntax-node-list backend produces a .inc with macro calls14// NODE(Kind, BaseKind)15// ABSTRACT_NODE(Type, Base, FirstKind, LastKind)16// similar to those for AST nodes such as AST/DeclNodes.inc.17//18// The -gen-clang-syntax-node-classes backend produces definitions for the19// syntax::Node subclasses (except those marked as External).20//21// In future, another backend will encode the structure of the various node22// types in tables so their invariants can be checked and enforced.23//24//===----------------------------------------------------------------------===//25#include "TableGenBackends.h"2627#include <deque>2829#include "llvm/ADT/StringExtras.h"30#include "llvm/Support/FormatVariadic.h"31#include "llvm/Support/raw_ostream.h"32#include "llvm/TableGen/Record.h"33#include "llvm/TableGen/TableGenBackend.h"3435namespace {36using llvm::formatv;3738// The class hierarchy of Node types.39// We assemble this in order to be able to define the NodeKind enum in a40// stable and useful way, where abstract Node subclasses correspond to ranges.41class Hierarchy {42public:43Hierarchy(const llvm::RecordKeeper &Records) {44for (llvm::Record *T : Records.getAllDerivedDefinitions("NodeType"))45add(T);46for (llvm::Record *Derived : Records.getAllDerivedDefinitions("NodeType"))47if (llvm::Record *Base = Derived->getValueAsOptionalDef("base"))48link(Derived, Base);49for (NodeType &N : AllTypes) {50llvm::sort(N.Derived, [](const NodeType *L, const NodeType *R) {51return L->Record->getName() < R->Record->getName();52});53// Alternatives nodes must have subclasses, External nodes may do.54assert(N.Record->isSubClassOf("Alternatives") ||55N.Record->isSubClassOf("External") || N.Derived.empty());56assert(!N.Record->isSubClassOf("Alternatives") || !N.Derived.empty());57}58}5960struct NodeType {61const llvm::Record *Record = nullptr;62const NodeType *Base = nullptr;63std::vector<const NodeType *> Derived;64llvm::StringRef name() const { return Record->getName(); }65};6667NodeType &get(llvm::StringRef Name = "Node") {68auto NI = ByName.find(Name);69assert(NI != ByName.end() && "no such node");70return *NI->second;71}7273// Traverse the hierarchy in pre-order (base classes before derived).74void visit(llvm::function_ref<void(const NodeType &)> CB,75const NodeType *Start = nullptr) {76if (Start == nullptr)77Start = &get();78CB(*Start);79for (const NodeType *D : Start->Derived)80visit(CB, D);81}8283private:84void add(const llvm::Record *R) {85AllTypes.emplace_back();86AllTypes.back().Record = R;87bool Inserted = ByName.try_emplace(R->getName(), &AllTypes.back()).second;88assert(Inserted && "Duplicate node name");89(void)Inserted;90}9192void link(const llvm::Record *Derived, const llvm::Record *Base) {93auto &CN = get(Derived->getName()), &PN = get(Base->getName());94assert(CN.Base == nullptr && "setting base twice");95PN.Derived.push_back(&CN);96CN.Base = &PN;97}9899std::deque<NodeType> AllTypes;100llvm::DenseMap<llvm::StringRef, NodeType *> ByName;101};102103const Hierarchy::NodeType &firstConcrete(const Hierarchy::NodeType &N) {104return N.Derived.empty() ? N : firstConcrete(*N.Derived.front());105}106const Hierarchy::NodeType &lastConcrete(const Hierarchy::NodeType &N) {107return N.Derived.empty() ? N : lastConcrete(*N.Derived.back());108}109110struct SyntaxConstraint {111SyntaxConstraint(const llvm::Record &R) {112if (R.isSubClassOf("Optional")) {113*this = SyntaxConstraint(*R.getValueAsDef("inner"));114} else if (R.isSubClassOf("AnyToken")) {115NodeType = "Leaf";116} else if (R.isSubClassOf("NodeType")) {117NodeType = R.getName().str();118} else {119assert(false && "Unhandled Syntax kind");120}121}122123std::string NodeType;124// optional and leaf types also go here, once we want to use them.125};126127} // namespace128129void clang::EmitClangSyntaxNodeList(llvm::RecordKeeper &Records,130llvm::raw_ostream &OS) {131llvm::emitSourceFileHeader("Syntax tree node list", OS, Records);132Hierarchy H(Records);133OS << R"cpp(134#ifndef NODE135#define NODE(Kind, Base)136#endif137138#ifndef CONCRETE_NODE139#define CONCRETE_NODE(Kind, Base) NODE(Kind, Base)140#endif141142#ifndef ABSTRACT_NODE143#define ABSTRACT_NODE(Kind, Base, First, Last) NODE(Kind, Base)144#endif145146)cpp";147H.visit([&](const Hierarchy::NodeType &N) {148// Don't emit ABSTRACT_NODE for node itself, which has no parent.149if (N.Base == nullptr)150return;151if (N.Derived.empty())152OS << formatv("CONCRETE_NODE({0},{1})\n", N.name(), N.Base->name());153else154OS << formatv("ABSTRACT_NODE({0},{1},{2},{3})\n", N.name(),155N.Base->name(), firstConcrete(N).name(),156lastConcrete(N).name());157});158OS << R"cpp(159#undef NODE160#undef CONCRETE_NODE161#undef ABSTRACT_NODE162)cpp";163}164165// Format a documentation string as a C++ comment.166// Trims leading whitespace handling since comments come from a TableGen file:167// documentation = [{168// This is a widget. Example:169// widget.explode()170// }];171// and should be formatted as:172// /// This is a widget. Example:173// /// widget.explode()174// Leading and trailing whitespace lines are stripped.175// The indentation of the first line is stripped from all lines.176static void printDoc(llvm::StringRef Doc, llvm::raw_ostream &OS) {177Doc = Doc.rtrim();178llvm::StringRef Line;179while (Line.trim().empty() && !Doc.empty())180std::tie(Line, Doc) = Doc.split('\n');181llvm::StringRef Indent = Line.take_while(llvm::isSpace);182for (; !Line.empty() || !Doc.empty(); std::tie(Line, Doc) = Doc.split('\n')) {183Line.consume_front(Indent);184OS << "/// " << Line << "\n";185}186}187188void clang::EmitClangSyntaxNodeClasses(llvm::RecordKeeper &Records,189llvm::raw_ostream &OS) {190llvm::emitSourceFileHeader("Syntax tree node list", OS, Records);191Hierarchy H(Records);192193OS << "\n// Forward-declare node types so we don't have to carefully "194"sequence definitions.\n";195H.visit([&](const Hierarchy::NodeType &N) {196OS << "class " << N.name() << ";\n";197});198199OS << "\n// Node definitions\n\n";200H.visit([&](const Hierarchy::NodeType &N) {201if (N.Record->isSubClassOf("External"))202return;203printDoc(N.Record->getValueAsString("documentation"), OS);204OS << formatv("class {0}{1} : public {2} {{\n", N.name(),205N.Derived.empty() ? " final" : "", N.Base->name());206207// Constructor.208if (N.Derived.empty())209OS << formatv("public:\n {0}() : {1}(NodeKind::{0}) {{}\n", N.name(),210N.Base->name());211else212OS << formatv("protected:\n {0}(NodeKind K) : {1}(K) {{}\npublic:\n",213N.name(), N.Base->name());214215if (N.Record->isSubClassOf("Sequence")) {216// Getters for sequence elements.217for (const auto &C : N.Record->getValueAsListOfDefs("children")) {218assert(C->isSubClassOf("Role"));219llvm::StringRef Role = C->getValueAsString("role");220SyntaxConstraint Constraint(*C->getValueAsDef("syntax"));221for (const char *Const : {"", "const "})222OS << formatv(223" {2}{1} *get{0}() {2} {{\n"224" return llvm::cast_or_null<{1}>(findChild(NodeRole::{0}));\n"225" }\n",226Role, Constraint.NodeType, Const);227}228}229230// classof. FIXME: move definition inline once ~all nodes are generated.231OS << " static bool classof(const Node *N);\n";232233OS << "};\n\n";234});235}236237238