blob: e6f55ab69288cd6abe6ca6c25e82fbf6662267ad [file] [log] [blame]
//===--- Markup.cpp - Markup ----------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/ErrorHandling.h"
#include "swift/AST/Comment.h"
#include "swift/Markup/LineList.h"
#include "swift/Markup/Markup.h"
#include "cmark.h"
using namespace swift;
using namespace markup;
struct ParseState {
cmark_iter *Iter = nullptr;
cmark_event_type Event = CMARK_EVENT_NONE;
cmark_node *Node = nullptr;
public:
ParseState() = default;
ParseState(cmark_iter *Iter, cmark_event_type Event = CMARK_EVENT_NONE,
cmark_node *Node = nullptr)
: Iter(Iter), Event(Event), Node(Node) {}
ParseState next() const {
auto I = Iter;
auto Event = cmark_iter_next(I);
auto Node = cmark_iter_get_node(I);
return ParseState(I, Event, Node);
}
cmark_node_type getType() const {
return cmark_node_get_type(Node);
}
};
template <typename NodeType>
struct ParseResult {
NodeType *Node;
ParseState State;
public:
operator ParseResult<MarkupASTNode>() {
return { cast<MarkupASTNode>(Node), State };
}
};
StringRef getLiteralContent(MarkupContext &MC, LineList &LL, cmark_node *Node) {
// Literal content nodes never have start/end column line information.
// It is a floating piece of text that inherits location information from
// its parent.
auto Literal = cmark_node_get_literal(Node);
assert(Literal != nullptr);
return MC.allocateCopy(StringRef(Literal));
}
ParseResult<MarkupASTNode>
parseElement(MarkupContext &MC, LineList &LL, ParseState State);
ParseState parseChildren(MarkupContext &MC, LineList &LL, ParseState State,
SmallVectorImpl<MarkupASTNode *> &Children) {
auto Root = State.Node;
State = State.next();
do {
if (Root == State.Node && State.Event == CMARK_EVENT_EXIT)
break;
auto Result = parseElement(MC, LL, State);
Children.push_back(Result.Node);
State = Result.State;
} while (!(Root == State.Node && State.Event == CMARK_EVENT_EXIT));
return State;
}
ParseResult<Text> parseText(MarkupContext &MC, LineList &LL, ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_TEXT
&& State.Event == CMARK_EVENT_ENTER);
return { Text::create(MC, getLiteralContent(MC, LL, State.Node)),
State.next() };
}
ParseResult<BlockQuote> parseBlockQuote(MarkupContext &MC, LineList &LL,
ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_BLOCK_QUOTE
&& State.Event == CMARK_EVENT_ENTER);
SmallVector<MarkupASTNode *, 4> Children;
auto ResultState = parseChildren(MC, LL, State, Children);
assert(State.Node == ResultState.Node
&& ResultState.Event == CMARK_EVENT_EXIT);
return { BlockQuote::create(MC, Children), ResultState.next() };
}
ParseResult<Code> parseCode(MarkupContext &MC, LineList &LL, ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_CODE
&& State.Event == CMARK_EVENT_ENTER);
return { Code::create(MC, getLiteralContent(MC, LL, State.Node)),
State.next() };
}
ParseResult<CodeBlock> parseCodeBlock(MarkupContext &MC, LineList &LL,
ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_CODE_BLOCK
&& State.Event == CMARK_EVENT_ENTER);
StringRef Language("swift");
if (auto FenceInfo = cmark_node_get_fence_info(State.Node)) {
StringRef FenceInfoStr(FenceInfo);
if (!FenceInfoStr.empty())
Language = MC.allocateCopy(FenceInfoStr);
}
return { CodeBlock::create(MC, getLiteralContent(MC, LL, State.Node),
Language),
State.next() };
}
ParseResult<Emphasis> parseEmphasis(MarkupContext &MC, LineList &LL,
ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_EMPH
&& State.Event == CMARK_EVENT_ENTER);
SmallVector<MarkupASTNode *, 2> Children;
auto ResultState = parseChildren(MC, LL, State, Children);
assert(State.Node == ResultState.Node
&& ResultState.Event == CMARK_EVENT_EXIT);
return { Emphasis::create(MC, Children), ResultState.next() };
}
ParseResult<Strong> parseStrong(MarkupContext &MC, LineList &LL,
ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_STRONG
&& State.Event == CMARK_EVENT_ENTER);
SmallVector<MarkupASTNode *, 2> Children;
auto ResultState = parseChildren(MC, LL, State, Children);
assert(State.Node == ResultState.Node
&& ResultState.Event == CMARK_EVENT_EXIT);
return { Strong::create(MC, Children), ResultState.next() };
}
ParseResult<Header> parseHeader(MarkupContext &MC, LineList &LL,
ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_HEADER
&& State.Event == CMARK_EVENT_ENTER);
auto Level = cmark_node_get_header_level(State.Node);
SmallVector<MarkupASTNode *, 2> Children;
auto ResultState = parseChildren(MC, LL, State, Children);
assert(State.Node == ResultState.Node
&& ResultState.Event == CMARK_EVENT_EXIT);
(void) ResultState;
return { Header::create(MC, Level, Children), State.next() };
}
ParseResult<HRule> parseHRule(MarkupContext &MC, LineList &LL,
ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_HRULE
&& State.Event == CMARK_EVENT_ENTER);
return { HRule::create(MC), State.next() };
}
ParseResult<HTML> parseHTML(MarkupContext &MC, LineList &LL, ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_HTML
&& State.Event == CMARK_EVENT_ENTER);
return { HTML::create(MC, getLiteralContent(MC, LL, State.Node)),
State.next() };
}
ParseResult<InlineHTML> parseInlineHTML(MarkupContext &MC, LineList &LL,
ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_INLINE_HTML
&& State.Event == CMARK_EVENT_ENTER);
return { InlineHTML::create(MC, getLiteralContent(MC, LL, State.Node)),
State.next() };
}
ParseResult<Image> parseImage(MarkupContext &MC, LineList &LL, ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_IMAGE
&& State.Event == CMARK_EVENT_ENTER);
std::string Destination(cmark_node_get_url(State.Node));
auto NodeTitle = cmark_node_get_title(State.Node);
std::string TitleString = NodeTitle ? NodeTitle : "";
auto Title = TitleString.empty() ? None : Optional<StringRef>(TitleString);
SmallVector<MarkupASTNode *, 2> Children;
auto ResultState = parseChildren(MC, LL, State, Children);
assert(State.Node == ResultState.Node
&& ResultState.Event == CMARK_EVENT_EXIT);
return { Image::create(MC, Destination, Title, Children), ResultState.next() };
}
ParseResult<Item> parseItem(MarkupContext &MC, LineList &LL, ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_ITEM
&& State.Event == CMARK_EVENT_ENTER);
SmallVector<MarkupASTNode *, 2> Children;
auto ResultState = parseChildren(MC, LL, State, Children);
assert(State.Node == ResultState.Node
&& ResultState.Event == CMARK_EVENT_EXIT);
return { Item::create(MC, Children), ResultState.next() };
}
ParseResult<LineBreak> parseLineBreak(MarkupContext &MC, LineList &LL,
ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_LINEBREAK
&& State.Event == CMARK_EVENT_ENTER);
return { LineBreak::create(MC), State.next() };
}
ParseResult<SoftBreak> parseSoftBreak(MarkupContext &MC, LineList &LL,
ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_SOFTBREAK
&& State.Event == CMARK_EVENT_ENTER);
return { SoftBreak::create(MC), State.next() };
}
ParseResult<Link> parseLink(MarkupContext &MC, LineList &LL, ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_LINK
&& State.Event == CMARK_EVENT_ENTER);
std::string Destination(cmark_node_get_url(State.Node));
SmallVector<MarkupASTNode *, 2> Children;
auto ResultState = parseChildren(MC, LL, State, Children);
assert(State.Node == ResultState.Node
&& ResultState.Event == CMARK_EVENT_EXIT);
return { Link::create(MC, Destination, Children), ResultState.next() };
}
ParseResult<List> parseList(MarkupContext &MC, LineList &LL, ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_LIST
&& State.Event == CMARK_EVENT_ENTER);
auto ListRoot = State.Node;
auto IsOrdered = cmark_node_get_list_type(ListRoot) == CMARK_ORDERED_LIST;
SmallVector<MarkupASTNode *, 3> Children;
auto ResultState = parseChildren(MC, LL, State, Children);
assert(State.Node == ResultState.Node
&& ResultState.Event == CMARK_EVENT_EXIT);
return { List::create(MC, Children, IsOrdered), ResultState.next() };
}
ParseResult<Paragraph> parseParagraph(MarkupContext &MC, LineList &LL,
ParseState State) {
assert(cmark_node_get_type(State.Node) == CMARK_NODE_PARAGRAPH
&& State.Event == CMARK_EVENT_ENTER);
SmallVector<MarkupASTNode *, 3> Children;
auto ResultState = parseChildren(MC, LL, State, Children);
assert(State.Node == ResultState.Node
&& ResultState.Event == CMARK_EVENT_EXIT);
return { Paragraph::create(MC, Children), ResultState.next() };
}
ParseResult<MarkupASTNode>
parseElement(MarkupContext &MC, LineList &LL, ParseState State) {
assert(State.Event == CMARK_EVENT_ENTER);
auto NodeType = cmark_node_get_type(State.Node);
switch (NodeType) {
case CMARK_NODE_DOCUMENT: {
llvm_unreachable("Markup documents cannot be nested");
}
case CMARK_NODE_BLOCK_QUOTE: {
return parseBlockQuote(MC, LL, State);
}
case CMARK_NODE_CODE: {
return parseCode(MC, LL, State);
}
case CMARK_NODE_CODE_BLOCK: {
return parseCodeBlock(MC, LL, State);
}
case CMARK_NODE_EMPH: {
return parseEmphasis(MC, LL, State);
}
case CMARK_NODE_HEADER: {
return parseHeader(MC, LL, State);
}
case CMARK_NODE_HRULE: {
return parseHRule(MC, LL, State);
}
case CMARK_NODE_HTML: {
return parseHTML(MC, LL, State);
}
case CMARK_NODE_IMAGE: {
return parseImage(MC, LL, State);
}
case CMARK_NODE_INLINE_HTML: {
return parseInlineHTML(MC, LL, State);
}
case CMARK_NODE_ITEM: {
return parseItem(MC, LL, State);
}
case CMARK_NODE_LINEBREAK: {
return parseLineBreak(MC, LL, State);
}
case CMARK_NODE_LINK: {
return parseLink(MC, LL, State);
}
case CMARK_NODE_LIST: {
return parseList(MC, LL, State);
}
case CMARK_NODE_PARAGRAPH: {
return parseParagraph(MC, LL, State);
}
case CMARK_NODE_SOFTBREAK: {
return parseSoftBreak(MC, LL, State);
}
case CMARK_NODE_STRONG: {
return parseStrong(MC, LL, State);
}
case CMARK_NODE_TEXT: {
return parseText(MC, LL, State);
}
default: {
llvm_unreachable("Can't parse a Markup node of type 'None'");
}
}
}
Document *swift::markup::parseDocument(MarkupContext &MC, LineList &LL) {
auto Comment = LL.str();
auto CMarkDoc = cmark_parse_document(Comment.c_str(), Comment.size(),
CMARK_OPT_SMART);
if (CMarkDoc == nullptr)
return nullptr;
ParseState State(cmark_iter_new(CMarkDoc));
// Prime the parser.
State = State.next();
SmallVector<MarkupASTNode *, 8> Children;
assert(cmark_node_get_type(State.Node) == CMARK_NODE_DOCUMENT
&& State.Event == CMARK_EVENT_ENTER);
auto ResultState = parseChildren(MC, LL, State, Children);
assert(State.Node == ResultState.Node
&& ResultState.Event == CMARK_EVENT_EXIT);
State = ResultState.next();
assert(State.Event == CMARK_EVENT_DONE);
cmark_node_free(CMarkDoc);
cmark_iter_free(State.Iter);
return Document::create(MC, Children);
}