blob: d3ea9867b1bc2bdb76921458fa85fec30aad4f73 [file] [log] [blame]
//===--- AST.h - Markup AST nodes -------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
#ifndef SWIFT_MARKUP_AST_H
#define SWIFT_MARKUP_AST_H
#include "swift/Markup/LineList.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TrailingObjects.h"
namespace swift {
namespace markup {
class MarkupContext;
class MarkupASTNode;
class Paragraph;
class ParamField;
class ReturnsField;
class TagField;
class ThrowsField;
class LocalizationKeyField;
/// The basic structure of a doc comment attached to a Swift
/// declaration.
struct CommentParts {
Optional<const Paragraph *> Brief;
ArrayRef<const MarkupASTNode *> BodyNodes;
ArrayRef<ParamField *> ParamFields;
Optional<const ReturnsField *> ReturnsField;
Optional<const ThrowsField *> ThrowsField;
llvm::SmallSetVector<StringRef, 8> Tags;
Optional<const LocalizationKeyField *> LocalizationKeyField;
bool isEmpty() const {
return !Brief.hasValue() &&
!ReturnsField.hasValue() &&
!ThrowsField.hasValue() &&
BodyNodes.empty() &&
ParamFields.empty();
}
bool hasFunctionDocumentation() const {
return !ParamFields.empty() ||
ReturnsField.hasValue() ||
ThrowsField.hasValue();
}
};
#define MARKUP_AST_NODE(Id, Parent) class Id;
#define ABSTRACT_MARKUP_AST_NODE(Id, Parent) class Id;
#define MARKUP_AST_NODE_RANGE(Id, FirstId, LastId)
#include "swift/Markup/ASTNodes.def"
enum class ASTNodeKind : uint8_t {
#define MARKUP_AST_NODE(Id, Parent) Id,
#define ABSTRACT_MARKUP_AST_NODE(Id, Parent)
#define MARKUP_AST_NODE_RANGE(Id, FirstId, LastId) \
First_##Id = FirstId, Last_##Id = LastId,
#include "swift/Markup/ASTNodes.def"
};
class alignas(void *) MarkupASTNode {
MarkupASTNode(const MarkupASTNode &) = delete;
void operator=(const MarkupASTNode &) = delete;
protected:
ASTNodeKind Kind;
public:
MarkupASTNode(ASTNodeKind Kind) : Kind(Kind) {}
ASTNodeKind getKind() const { return Kind; }
void *operator new(size_t Bytes, MarkupContext &MC,
unsigned alignment = alignof(MarkupASTNode));
void *operator new(size_t Bytes, void *Mem) {
assert(Mem);
return Mem;
}
ArrayRef<MarkupASTNode *> getChildren();
ArrayRef<const MarkupASTNode *> getChildren() const;
void *operator new(size_t Bytes) = delete;
void operator delete(void *Data) = delete;
};
#pragma mark Markdown Nodes
class Document final : public MarkupASTNode,
private llvm::TrailingObjects<Document, MarkupASTNode *> {
friend TrailingObjects;
size_t NumChildren;
Document(ArrayRef<MarkupASTNode*> Children);
public:
static Document *create(MarkupContext &MC,
ArrayRef<MarkupASTNode *> Children);
ArrayRef<MarkupASTNode *> getChildren() {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
ArrayRef<const MarkupASTNode *> getChildren() const {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::Document;
}
};
class BlockQuote final : public MarkupASTNode,
private llvm::TrailingObjects<BlockQuote, MarkupASTNode *> {
friend TrailingObjects;
size_t NumChildren;
BlockQuote(ArrayRef<MarkupASTNode *> Children);
public:
static BlockQuote *create(MarkupContext &MC, ArrayRef<MarkupASTNode *> Children);
ArrayRef<MarkupASTNode *> getChildren() {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
ArrayRef<const MarkupASTNode *> getChildren() const {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::BlockQuote;
}
};
class List final : public MarkupASTNode,
private llvm::TrailingObjects<List, MarkupASTNode *> {
friend TrailingObjects;
size_t NumChildren;
bool Ordered;
List(ArrayRef<MarkupASTNode *> Children, bool IsOrdered);
public:
static List *create(MarkupContext &MC, ArrayRef<MarkupASTNode *> Items,
bool IsOrdered);
ArrayRef<MarkupASTNode *> getChildren() {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
ArrayRef<const MarkupASTNode *> getChildren() const {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
void setChildren(ArrayRef<MarkupASTNode *> NewChildren) {
assert(NewChildren.size() <= NumChildren);
std::copy(NewChildren.begin(), NewChildren.end(),
getTrailingObjects<MarkupASTNode *>());
NumChildren = NewChildren.size();
}
bool isOrdered() const {
return Ordered;
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::List;
}
};
class Item final : public MarkupASTNode,
private llvm::TrailingObjects<Item, MarkupASTNode *> {
friend TrailingObjects;
size_t NumChildren;
Item(ArrayRef<MarkupASTNode *> Children);
public:
static Item *create(MarkupContext &MC, ArrayRef<MarkupASTNode *> Children);
ArrayRef<MarkupASTNode *> getChildren() {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
ArrayRef<const MarkupASTNode *> getChildren() const {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::Item;
}
};
class CodeBlock final : public MarkupASTNode {
StringRef LiteralContent;
StringRef Language;
CodeBlock(StringRef LiteralContent, StringRef Language)
: MarkupASTNode(ASTNodeKind::CodeBlock),
LiteralContent(LiteralContent),
Language(Language) {}
public:
static CodeBlock *create(MarkupContext &MC, StringRef LiteralContent,
StringRef Language);
StringRef getLiteralContent() const { return LiteralContent; };
StringRef getLanguage() const { return Language; };
ArrayRef<const MarkupASTNode *> getChildren() const {
return {};
}
ArrayRef<MarkupASTNode *> getChildren() {
return {};
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::CodeBlock;
}
};
class HTML final : public MarkupASTNode {
StringRef LiteralContent;
HTML(StringRef LiteralContent)
: MarkupASTNode(ASTNodeKind::HTML),
LiteralContent(LiteralContent) {}
public:
static HTML *create(MarkupContext &MC, StringRef LiteralContent);
StringRef getLiteralContent() const { return LiteralContent; };
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::HTML;
}
};
class Paragraph final : public MarkupASTNode,
private llvm::TrailingObjects<Paragraph, MarkupASTNode *> {
friend TrailingObjects;
size_t NumChildren;
Paragraph(ArrayRef<MarkupASTNode *> Children);
public:
static Paragraph *create(MarkupContext &MC,
ArrayRef<MarkupASTNode *> Children);
ArrayRef<MarkupASTNode *> getChildren() {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
ArrayRef<const MarkupASTNode *> getChildren() const {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::Paragraph;
}
};
class Header final : public MarkupASTNode,
private llvm::TrailingObjects<Header, MarkupASTNode *> {
friend TrailingObjects;
size_t NumChildren;
unsigned Level;
Header(unsigned Level, ArrayRef<MarkupASTNode *> Children);
public:
static Header *create(MarkupContext &MC, unsigned Level,
ArrayRef<MarkupASTNode *> Children);
ArrayRef<MarkupASTNode *> getChildren() {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
ArrayRef<const MarkupASTNode *> getChildren() const {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
unsigned getLevel() const {
return Level;
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::Header;
}
};
class HRule final : public MarkupASTNode {
HRule() : MarkupASTNode(ASTNodeKind::HRule) {}
public:
static HRule *create(MarkupContext &MC);
ArrayRef<const MarkupASTNode *> getChildren() const {
return {};
}
ArrayRef<MarkupASTNode *> getChildren() {
return {};
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::HRule;
}
};
class InlineContent : public MarkupASTNode {
public:
InlineContent(ASTNodeKind Kind) : MarkupASTNode(Kind) {}
ArrayRef<const MarkupASTNode *> getChildren() const {
return {};
}
ArrayRef<MarkupASTNode *> getChildren() {
return {};
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() >= ASTNodeKind::First_Inline &&
N->getKind() <= ASTNodeKind::Last_Inline;
}
};
class Text final : public InlineContent {
StringRef LiteralContent;
Text(StringRef LiteralContent)
: InlineContent(ASTNodeKind::Text),
LiteralContent(LiteralContent) {}
public:
static Text *create(MarkupContext &MC, StringRef LiteralContent);
StringRef getLiteralContent() const { return LiteralContent; };
void setLiteralContent(StringRef LC) {
LiteralContent = LC;
}
ArrayRef<const MarkupASTNode *> getChildren() const {
return {};
}
ArrayRef<MarkupASTNode *> getChildren() {
return {};
}
StringRef str() const {
return LiteralContent;
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::Text;
}
};
class SoftBreak final : public InlineContent {
SoftBreak() : InlineContent(ASTNodeKind::SoftBreak) {}
public:
static SoftBreak *create(MarkupContext &MC);
ArrayRef<const MarkupASTNode *> getChildren() const {
return {};
}
ArrayRef<MarkupASTNode *> getChildren() {
return {};
}
StringRef str() const {
return "\n";
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::SoftBreak;
}
};
class LineBreak final : public InlineContent {
LineBreak() : InlineContent(ASTNodeKind::LineBreak) {}
public:
static LineBreak *create(MarkupContext &MC);
ArrayRef<const MarkupASTNode *> getChildren() const {
return {};
}
ArrayRef<MarkupASTNode *> getChildren() {
return {};
}
StringRef str() const {
return "\n";
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::LineBreak;
}
};
class Code final : public InlineContent {
StringRef LiteralContent;
Code(StringRef LiteralContent)
: InlineContent(ASTNodeKind::Code),
LiteralContent(LiteralContent) {}
public:
static Code *create(MarkupContext &MC, StringRef LiteralContent);
StringRef getLiteralContent() const { return LiteralContent; };
ArrayRef<const MarkupASTNode *> getChildren() const {
return {};
}
ArrayRef<MarkupASTNode *> getChildren() {
return {};
}
StringRef str() const {
return LiteralContent;
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::Code;
}
};
class InlineHTML final : public InlineContent {
StringRef LiteralContent;
InlineHTML(StringRef LiteralContent)
: InlineContent(ASTNodeKind::InlineHTML),
LiteralContent(LiteralContent) {}
public:
static InlineHTML *create(MarkupContext &MC, StringRef LiteralContent);
StringRef getLiteralContent() const { return LiteralContent; };
ArrayRef<const MarkupASTNode *> getChildren() const {
return {};
}
ArrayRef<MarkupASTNode *> getChildren() {
return {};
}
StringRef str() const {
return LiteralContent;
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::InlineHTML;
}
};
class Emphasis final : public InlineContent,
private llvm::TrailingObjects<Emphasis, MarkupASTNode *> {
friend TrailingObjects;
size_t NumChildren;
Emphasis(ArrayRef<MarkupASTNode *> Children);
public:
static Emphasis *create(MarkupContext &MC,
ArrayRef<MarkupASTNode *> Children);
ArrayRef<MarkupASTNode *> getChildren() {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
ArrayRef<const MarkupASTNode *> getChildren() const {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::Emphasis;
}
};
class Strong final : public InlineContent,
private llvm::TrailingObjects<Strong, MarkupASTNode *> {
friend TrailingObjects;
size_t NumChildren;
Strong(ArrayRef<MarkupASTNode *> Children);
public:
static Strong *create(MarkupContext &MC,
ArrayRef<MarkupASTNode *> Children);
ArrayRef<MarkupASTNode *> getChildren() {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
ArrayRef<const MarkupASTNode *> getChildren() const {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::Strong;
}
};
class Link final : public InlineContent,
private llvm::TrailingObjects<Link, MarkupASTNode *> {
friend TrailingObjects;
size_t NumChildren;
StringRef Destination;
Link(StringRef Destination, ArrayRef<MarkupASTNode *> Children);
public:
static Link *create(MarkupContext &MC,
StringRef Destination,
ArrayRef<MarkupASTNode *> Children);
StringRef getDestination() const { return Destination; }
ArrayRef<MarkupASTNode *> getChildren() {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
ArrayRef<const MarkupASTNode *> getChildren() const {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::Link;
}
};
class Image final : public InlineContent,
private llvm::TrailingObjects<Image, MarkupASTNode *> {
friend TrailingObjects;
size_t NumChildren;
// FIXME: Hyperlink destinations can't be wrapped - use a Line
StringRef Destination;
Optional<StringRef> Title;
Image(StringRef Destination, Optional<StringRef> Title,
ArrayRef<MarkupASTNode *> Children);
public:
static Image *create(MarkupContext &MC,
StringRef Destination,
Optional<StringRef> Title,
ArrayRef<MarkupASTNode *> Children);
StringRef getDestination() const { return Destination; }
bool hasTitle() const {
return Title.hasValue();
}
StringRef getTitle() const {
return StringRef(Title.getValue());
}
ArrayRef<MarkupASTNode *> getChildren() {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
ArrayRef<const MarkupASTNode *> getChildren() const {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::Image;
}
};
#pragma mark Private Extensions
class PrivateExtension : public MarkupASTNode {
protected:
PrivateExtension(ASTNodeKind Kind)
: MarkupASTNode(Kind) {}
public:
ArrayRef<const MarkupASTNode *> getChildren() const {
return {};
}
ArrayRef<MarkupASTNode *> getChildren() {
return {};
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() >= ASTNodeKind::First_Private &&
N->getKind() <= ASTNodeKind::Last_Private;
}
};
class ParamField final : public PrivateExtension,
private llvm::TrailingObjects<ParamField, MarkupASTNode *> {
friend TrailingObjects;
size_t NumChildren;
StringRef Name;
// Parameter fields can contain a substructure describing a
// function or closure parameter.
llvm::Optional<CommentParts> Parts;
ParamField(StringRef Name, ArrayRef<MarkupASTNode *> Children);
public:
static ParamField *create(MarkupContext &MC, StringRef Name,
ArrayRef<MarkupASTNode *> Children);
StringRef getName() const {
return Name;
}
llvm::Optional<CommentParts> getParts() const {
return Parts;
}
void setParts(CommentParts P) {
Parts = P;
}
bool isClosureParameter() const {
if (!Parts.hasValue())
return false;
return Parts.getValue().hasFunctionDocumentation();
}
ArrayRef<MarkupASTNode *> getChildren() {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
ArrayRef<const MarkupASTNode *> getChildren() const {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
static bool classof(const MarkupASTNode *N) {
return N->getKind() == ASTNodeKind::ParamField;
}
};
#define MARKUP_SIMPLE_FIELD(Id, Keyword, XMLKind) \
class Id final : public PrivateExtension, \
private llvm::TrailingObjects<Id, MarkupASTNode *> { \
friend TrailingObjects; \
\
size_t NumChildren; \
\
Id(ArrayRef<MarkupASTNode *> Children);\
\
public: \
static Id *create(MarkupContext &MC, ArrayRef<MarkupASTNode *> Children); \
\
ArrayRef<MarkupASTNode *> getChildren() { \
return {getTrailingObjects<MarkupASTNode *>(), NumChildren}; \
} \
\
ArrayRef<const MarkupASTNode *> getChildren() const { \
return {getTrailingObjects<MarkupASTNode *>(), NumChildren}; \
} \
\
static bool classof(const MarkupASTNode *N) { \
return N->getKind() == ASTNodeKind::Id; \
} \
};
#include "swift/Markup/SimpleFields.def"
class MarkupASTWalker {
public:
void walk(const MarkupASTNode *Node) {
enter(Node);
switch(Node->getKind()) {
#define MARKUP_AST_NODE(Id, Parent) \
case ASTNodeKind::Id: \
visit##Id(static_cast<const Id*>(Node)); \
break;
#define ABSTRACT_MARKUP_AST_NODE(Id, Parent)
#define MARKUP_AST_NODE_RANGE(Id, FirstId, LastId)
#include "swift/Markup/ASTNodes.def"
}
if (shouldVisitChildrenOf(Node))
for (auto Child : Node->getChildren())
walk(Child);
exit(Node);
}
virtual bool shouldVisitChildrenOf(const MarkupASTNode *Node) {
return true;
}
virtual void enter(const MarkupASTNode *Node) {}
virtual void exit(const MarkupASTNode *Node) {}
#define MARKUP_AST_NODE(Id, Parent) \
virtual void visit##Id(const Id *Node) {}
#define ABSTRACT_MARKUP_AST_NODE(Id, Parent)
#define MARKUP_AST_NODE_RANGE(Id, FirstId, LastId)
#include "swift/Markup/ASTNodes.def"
virtual ~MarkupASTWalker() = default;
};
MarkupASTNode *createSimpleField(MarkupContext &MC, StringRef Tag,
ArrayRef<MarkupASTNode *> Children);
bool isAFieldTag(StringRef Tag);
void dump(const MarkupASTNode *Node, llvm::raw_ostream &OS, unsigned indent = 0);
void printInlinesUnder(const MarkupASTNode *Node, llvm::raw_ostream &OS,
bool PrintDecorators = false);
template <typename ImplClass, typename RetTy = void, typename... Args>
class MarkupASTVisitor {
public:
RetTy visit(const MarkupASTNode *Node, Args... args) {
switch (Node->getKind()) {
#define MARKUP_AST_NODE(Id, Parent) \
case ASTNodeKind::Id: \
return static_cast<ImplClass*>(this) \
->visit##Id(cast<const Id>(Node), \
::std::forward<Args>(args)...);
#define ABSTRACT_MARKUP_AST_NODE(Id, Parent)
#define MARKUP_AST_NODE_RANGE(Id, FirstId, LastId)
#include "swift/Markup/ASTNodes.def"
}
}
virtual ~MarkupASTVisitor() {}
};
} // namespace markup
} // namespace swift
#endif // SWIFT_MARKUP_AST_H