//===--- Markup.cpp - Markup ----------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://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 llvm; 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 struct ParseResult { NodeType *Node; ParseState State; public: operator ParseResult() { return { cast(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 parseElement(MarkupContext &MC, LineList &LL, ParseState State); ParseState parseChildren(MarkupContext &MC, LineList &LL, ParseState State, SmallVectorImpl &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 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
parseBlockQuote(MarkupContext &MC, LineList &LL, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_BLOCK_QUOTE && State.Event == CMARK_EVENT_ENTER); SmallVector 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 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 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 parseEmphasis(MarkupContext &MC, LineList &LL, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_EMPH && State.Event == CMARK_EVENT_ENTER); SmallVector 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 parseStrong(MarkupContext &MC, LineList &LL, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_STRONG && State.Event == CMARK_EVENT_ENTER); SmallVector 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
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 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 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 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 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 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(TitleString); SmallVector 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 parseItem(MarkupContext &MC, LineList &LL, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_ITEM && State.Event == CMARK_EVENT_ENTER); SmallVector 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 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 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 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 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 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 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 parseParagraph(MarkupContext &MC, LineList &LL, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_PARAGRAPH && State.Event == CMARK_EVENT_ENTER); SmallVector 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 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 *llvm::markup::parseDocument(MarkupContext &MC, LineList &LL) { auto Comment = LL.str(); auto CMarkDoc = cmark_parse_document(Comment.c_str(), Comment.size(), CMARK_OPT_DEFAULT); if (CMarkDoc == nullptr) return nullptr; ParseState State(cmark_iter_new(CMarkDoc)); // Prime the parser. State = State.next(); SmallVector 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); }