markdown.kpeg - rdoc-5.1.0 Documentation

%% name = RDoc::Markdown

%% header { # coding: UTF-8 # :markup: markdown

## # RDoc::Markdown as described by the [markdown syntax]. # # To choose Markdown as your only default format see # Saved Options at RDoc::Options for instructions on setting up a `.doc_options` # file to store your project default. # # ## Usage # # Here is a brief example of using this parse to read a markdown file by hand. # # data = File.read(“README.md”) # formatter = RDoc::Markup::ToHtml.new(RDoc::Options.new, nil) # html = RDoc::Markdown.parse.accept(formatter) # # # do something with html # # ## Extensions # # The following markdown extensions are supported by the parser, but not all # are used in RDoc output by default. # # ### RDoc # # The RDoc Markdown parser has the following built-in behaviors that cannot be # disabled. # # Underscores embedded in words are never interpreted as emphasis. (While the # [markdown dingus] emphasizes in-word underscores, neither the # Markdown syntax nor MarkdownTest mention this behavior.) # # For HTML output, RDoc always auto-links bare URLs. # # ### Break on Newline # # The break_on_newline extension converts all newlines into hard line breaks # as in [Github Flavored Markdown]. This extension is disabled by # default. # # ### CSS # # The css extension enables CSS blocks to be included in the output, but they # are not used for any built-in RDoc output format. This extension is disabled # by default. # # Example: # # <style type=“text/css”> # h1 { font-size: 3em } # </style> # # ### Definition Lists # # The definition_lists extension allows definition lists using the [PHP # Markdown Extra syntax], but only one label and definition are supported # at this time. This extension is enabled by default. # # Example: # # “` # cat # : A small furry mammal # that seems to sleep a lot # # ant # : A little insect that is known # to enjoy picnics # # “` # # Produces: # # cat # : A small furry mammal # that seems to sleep a lot # # ant # : A little insect that is known # to enjoy picnics # # ### Github # # The github extension enables a partial set of [Github Flavored Markdown] # [GFM]. This extension is enabled by default. # # Supported github extensions include: # # #### Fenced code blocks # # Use ` “` ` around a block of code instead of indenting it four spaces. # # #### Syntax highlighting # # Use ` “` ruby ` as the start of a code fence to add syntax highlighting. # (Currently only `ruby` syntax is supported). # # ### HTML # # Enables raw HTML to be included in the output. This extension is enabled by # default. # # Example: # # <table> # … # </table> # # ### Notes # # The notes extension enables footnote support. This extension is enabled by # default. # # Example: # # Here is some text including an inline footnote ^[for short footnotes] # # … # # [^1]: With the footnote text down at the bottom # # Produces: # # Here is some text including an inline footnote ^[for short footnotes] # # [^1]: With the footnote text down at the bottom # # ## Limitations # # * Link titles are not used # * Footnotes are collapsed into a single paragraph # # ## Author # # This markdown parser is a port to kpeg from [peg-markdown] by # John MacFarlane. # # It is used under the MIT license: # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # The port to kpeg was performed by Eric Hodel and Evan Phoenix # # [dingus]: daringfireball.net/projects/markdown/dingus # [GFM]: github.github.com/github-flavored-markdown/ # [pegmarkdown]: github.com/jgm/peg-markdown # [PHPE]: michelf.com/projects/php-markdown/extra/#def-list # [syntax]: daringfireball.net/projects/markdown/syntax #– # Last updated to jgm/peg-markdown commit 8f8fc22ef0

}

%% {

    require 'rdoc'
    require 'rdoc/markup/to_joined_paragraph'
    require 'rdoc/markdown/entities'

    require 'rdoc/markdown/literals'

    ##
    # Supported extensions

    EXTENSIONS = []

    ##
    # Extensions enabled by default

    DEFAULT_EXTENSIONS = [
      :definition_lists,
      :github,
      :html,
      :notes,
    ]

    # :section: Extensions

    ##
    # Creates extension methods for the `name` extension to enable and disable
    # the extension and to query if they are active.

    def self.extension name
      EXTENSIONS << name

      define_method "#{name}?" do
        extension? name
      end

      define_method "#{name}=" do |enable|
        extension name, enable
      end
    end

    ##
    # Converts all newlines into hard breaks

    extension :break_on_newline

    ##
    # Allow style blocks

    extension :css

    ##
    # Allow PHP Markdown Extras style definition lists

    extension :definition_lists

    ##
    # Allow Github Flavored Markdown

    extension :github

    ##
    # Allow HTML

    extension :html

    ##
    # Enables the notes extension

    extension :notes

    # :section:

    ##
    # Parses the `markdown` document into an RDoc::Document using the default
    # extensions.

    def self.parse markdown
      parser = new

      parser.parse markdown
    end

    # TODO remove when kpeg 0.10 is released
    alias orig_initialize initialize # :nodoc:

    ##
    # Creates a new markdown parser that enables the given +extensions+.

    def initialize extensions = DEFAULT_EXTENSIONS, debug = false
      @debug      = debug
      @formatter  = RDoc::Markup::ToJoinedParagraph.new
      @extensions = extensions

      @references          = nil
      @unlinked_references = nil

      @footnotes       = nil
      @note_order      = nil
    end

    ##
    # Wraps `text` in emphasis for rdoc inline formatting

    def emphasis text
      if text =~ /\A[a-z\d.\/]+\z/i then
        "_#{text}_"
      else
        "<em>#{text}</em>"
      end
    end

    ##

#

    #
    # Is the extension `name` enabled?

    def extension? name
      @extensions.include? name
    end

    ##

#

    #
    # Enables or disables the extension with `name`

    def extension name, enable
      if enable then
        @extensions |= [name]
      else
        @extensions -= [name]
      end
    end

    ##
    # Parses `text` in a clone of this parser.  This is used for handling nested
    # lists the same way as markdown_parser.

    def inner_parse text # :nodoc:
      parser = clone

      parser.setup_parser text, @debug

      parser.peg_parse

      doc = parser.result

      doc.accept @formatter

      doc.parts
    end

    ##
    # Finds a link reference for `label` and creates a new link to it with
    # `content` as the link text.  If `label` was not encountered in the
    # reference-gathering parser pass the label and content are reconstructed
    # with the linking `text` (usually whitespace).

    def link_to content, label = content, text = nil
      raise 'enable notes extension' if
        content.start_with? '^' and label.equal? content

      if ref = @references[label] then
        "{#{content}}[#{ref}]"
      elsif label.equal? content then
        "[#{content}]#{text}"
      else
        "[#{content}]#{text}[#{label}]"
      end
    end

    ##
    # Creates an RDoc::Markup::ListItem by parsing the `unparsed` content from
    # the first parsing pass.

    def list_item_from unparsed
      parsed = inner_parse unparsed.join
      RDoc::Markup::ListItem.new nil, *parsed
    end

    ##
    # Stores `label` as a note and fills in previously unknown note references.

    def note label
      #foottext = "rdoc-label:foottext-#{label}:footmark-#{label}"

      #ref.replace foottext if ref = @unlinked_notes.delete(label)

      @notes[label] = foottext

      #"{^1}[rdoc-label:footmark-#{label}:foottext-#{label}] "
    end

    ##
    # Creates a new link for the footnote `reference` and adds the reference to
    # the note order list for proper display at the end of the document.

    def note_for ref
      @note_order << ref

      label = @note_order.length

      "{*#{label}}[rdoc-label:foottext-#{label}:footmark-#{label}]"
    end

    ##
    # The internal kpeg parse method

    alias peg_parse parse # :nodoc:

    ##
    # Creates an RDoc::Markup::Paragraph from `parts` and including
    # extension-specific behavior

    def paragraph parts
      parts = parts.map do |part|
        if "\n" == part then
          RDoc::Markup::HardBreak.new
        else
          part
        end
      end if break_on_newline?

      RDoc::Markup::Paragraph.new(*parts)
    end

    ##
    # Parses `markdown` into an RDoc::Document

    def parse markdown
      @references          = {}
      @unlinked_references = {}

      markdown += "\n\n"

      setup_parser markdown, @debug
      peg_parse 'References'

      if notes? then
        @footnotes       = {}

        setup_parser markdown, @debug
        peg_parse 'Notes'

        # using note_order on the first pass would be a bug
        @note_order      = []
      end

      setup_parser markdown, @debug
      peg_parse

      doc = result

      if notes? and not @footnotes.empty? then
        doc << RDoc::Markup::Rule.new(1)

        @note_order.each_with_index do |ref, index|
          label = index + 1
          note = @footnotes[ref]

          link = "{^#{label}}[rdoc-label:footmark-#{label}:foottext-#{label}] "
          note.parts.unshift link

          doc << note
        end
      end

      doc.accept @formatter

      doc
    end

    ##
    # Stores `label` as a reference to `link` and fills in previously unknown
    # link references.

    def reference label, link
      if ref = @unlinked_references.delete(label) then
        ref.replace link
      end

      @references[label] = link
    end

    ##
    # Wraps `text` in strong markup for rdoc inline formatting

    def strong text
      if text =~ /\A[a-z\d.\/-]+\z/i then
        "*#{text}*"
      else
        "<b>#{text}</b>"
      end
    end

}

root = Doc

Doc = BOM? Block*:a { RDoc::Markup::Document.new }

Block = @BlankLine*

    ( BlockQuote
    | Verbatim
    | CodeFence
    | Note
    | Reference
    | HorizontalRule
    | Heading
    | OrderedList
    | BulletList
    | DefinitionList
    | HtmlBlock
    | StyleBlock
    | Para
    | Plain )

Para = @NonindentSpace Inlines:a @BlankLine+

    { paragraph a }

Plain = Inlines:a

    { paragraph a }

AtxInline = !@Newline !(@Sp? /#*/ @Sp @Newline) Inline

AtxStart = < /#{1,6}/ >

    { text.length }

AtxHeading = AtxStart:s @Sp? AtxInline+:a (@Sp? /#*/ @Sp)? @Newline

    { RDoc::Markup::Heading.new(s, a.join) }

SetextHeading = SetextHeading1 | SetextHeading2

SetextBottom1 = /={3,}/ @Newline

SetextBottom2 = /-{3,}/ @Newline

SetextHeading1 = &(@RawLine SetextBottom1)

    @StartList:a ( !@Endline Inline:b { a << b } )+ @Sp? @Newline
    SetextBottom1
    { RDoc::Markup::Heading.new(1, a.join) }

SetextHeading2 = &(@RawLine SetextBottom2)

    @StartList:a ( !@Endline Inline:b { a << b })+ @Sp? @Newline
    SetextBottom2
    { RDoc::Markup::Heading.new(2, a.join) }

Heading = SetextHeading | AtxHeading

BlockQuote = BlockQuoteRaw:a

    { RDoc::Markup::BlockQuote.new(*a) }

BlockQuoteRaw = @StartList:a

    (( ">" " "? Line:l { a << l } )
     ( !">" !@BlankLine Line:c { a << c } )*
     ( @BlankLine:n { a << n } )*
    )+
    { inner_parse a.join }

NonblankIndentedLine = !@BlankLine IndentedLine

VerbatimChunk = @BlankLine*:a

    NonblankIndentedLine+:b
    { a.concat b }

Verbatim = VerbatimChunk+:a

    { RDoc::Markup::Verbatim.new(*a.flatten) }

HorizontalRule = @NonindentSpace

    ( "*" @Sp "*" @Sp "*" (@Sp "*")*
    | "-" @Sp "-" @Sp "-" (@Sp "-")*
    | "_" @Sp "_" @Sp "_" (@Sp "_")*)
    @Sp @Newline @BlankLine+
    { RDoc::Markup::Rule.new 1 }

Bullet = !HorizontalRule @NonindentSpace /[+*-]/ @Spacechar+

BulletList = &Bullet (ListTight | ListLoose):a

    { RDoc::Markup::List.new(:BULLET, *a) }

ListTight = ListItemTight+:a

    @BlankLine* !(Bullet | Enumerator)
    { a }

ListLoose = @StartList:a

    ( ListItem:b @BlankLine* { a << b } )+
    { a }

ListItem = ( Bullet | Enumerator )

    @StartList:a
    ListBlock:b { a << b }
    ( ListContinuationBlock:c { a.push(*c) } )*
    { list_item_from a }

ListItemTight =

    ( Bullet | Enumerator )
    ListBlock:a
    ( !@BlankLine
      ListContinuationBlock:b { a.push(*b) } )*
    !ListContinuationBlock
    { list_item_from a }

ListBlock = !@BlankLine Line:a

    ListBlockLine*:c
    { [a, *c] }

ListContinuationBlock = @StartList:a

    ( @BlankLine*
      { a << "\n" } )
    ( Indent
      ListBlock:b { a.concat b } )+
    { a }

Enumerator = @NonindentSpace [0-9]+ “.” @Spacechar+

OrderedList = &Enumerator (ListTight | ListLoose):a

    { RDoc::Markup::List.new(:NUMBER, *a) }

ListBlockLine = !@BlankLine

    !( Indent? (Bullet | Enumerator) )
    !HorizontalRule
    OptionallyIndentedLine

# Parsers for different kinds of block-level HTML content. # This is repetitive due to constraints of PEG grammar.

HtmlOpenAnchor = “<” Spnl (“a” | “A”) Spnl HtmlAttribute* “>” HtmlCloseAnchor = “<” Spnl “/” (“a” | “A”) Spnl “>” HtmlAnchor = HtmlOpenAnchor (HtmlAnchor | !HtmlCloseAnchor .)* HtmlCloseAnchor

HtmlBlockOpenAddress = “<” Spnl (“address” | “ADDRESS”) Spnl HtmlAttribute* “>” HtmlBlockCloseAddress = “<” Spnl “/” (“address” | “ADDRESS”) Spnl “>” HtmlBlockAddress = HtmlBlockOpenAddress (HtmlBlockAddress | !HtmlBlockCloseAddress .)* HtmlBlockCloseAddress

HtmlBlockOpenBlockquote = “<” Spnl (“blockquote” | “BLOCKQUOTE”) Spnl HtmlAttribute* “>” HtmlBlockCloseBlockquote = “<” Spnl “/” (“blockquote” | “BLOCKQUOTE”) Spnl “>” HtmlBlockBlockquote = HtmlBlockOpenBlockquote (HtmlBlockBlockquote | !HtmlBlockCloseBlockquote .)* HtmlBlockCloseBlockquote

HtmlBlockOpenCenter = “<” Spnl (“center” | “CENTER”) Spnl HtmlAttribute* “>” HtmlBlockCloseCenter = “<” Spnl “/” (“center” | “CENTER”) Spnl “>” HtmlBlockCenter = HtmlBlockOpenCenter (HtmlBlockCenter | !HtmlBlockCloseCenter .)* HtmlBlockCloseCenter

HtmlBlockOpenDir = “<” Spnl (“dir” | “DIR”) Spnl HtmlAttribute* “>” HtmlBlockCloseDir = “<” Spnl “/” (“dir” | “DIR”) Spnl “>” HtmlBlockDir = HtmlBlockOpenDir (HtmlBlockDir | !HtmlBlockCloseDir .)* HtmlBlockCloseDir

HtmlBlockOpenDiv = “<” Spnl (“div” | “DIV”) Spnl HtmlAttribute* “>” HtmlBlockCloseDiv = “<” Spnl “/” (“div” | “DIV”) Spnl “>” HtmlBlockDiv = HtmlBlockOpenDiv (HtmlBlockDiv | !HtmlBlockCloseDiv .)* HtmlBlockCloseDiv

HtmlBlockOpenDl = “<” Spnl (“dl” | “DL”) Spnl HtmlAttribute* “>” HtmlBlockCloseDl = “<” Spnl “/” (“dl” | “DL”) Spnl “>” HtmlBlockDl = HtmlBlockOpenDl (HtmlBlockDl | !HtmlBlockCloseDl .)* HtmlBlockCloseDl

HtmlBlockOpenFieldset = “<” Spnl (“fieldset” | “FIELDSET”) Spnl HtmlAttribute* “>” HtmlBlockCloseFieldset = “<” Spnl “/” (“fieldset” | “FIELDSET”) Spnl “>” HtmlBlockFieldset = HtmlBlockOpenFieldset (HtmlBlockFieldset | !HtmlBlockCloseFieldset .)* HtmlBlockCloseFieldset

HtmlBlockOpenForm = “<” Spnl (“form” | “FORM”) Spnl HtmlAttribute* “>” HtmlBlockCloseForm = “<” Spnl “/” (“form” | “FORM”) Spnl “>” HtmlBlockForm = HtmlBlockOpenForm (HtmlBlockForm | !HtmlBlockCloseForm .)* HtmlBlockCloseForm

HtmlBlockOpenH1 = “<” Spnl (“h1” | “H1”) Spnl HtmlAttribute* “>” HtmlBlockCloseH1 = “<” Spnl “/” (“h1” | “H1”) Spnl “>” HtmlBlockH1 = HtmlBlockOpenH1 (HtmlBlockH1 | !HtmlBlockCloseH1 .)* HtmlBlockCloseH1

HtmlBlockOpenH2 = “<” Spnl (“h2” | “H2”) Spnl HtmlAttribute* “>” HtmlBlockCloseH2 = “<” Spnl “/” (“h2” | “H2”) Spnl “>” HtmlBlockH2 = HtmlBlockOpenH2 (HtmlBlockH2 | !HtmlBlockCloseH2 .)* HtmlBlockCloseH2

HtmlBlockOpenH3 = “<” Spnl (“h3” | “H3”) Spnl HtmlAttribute* “>” HtmlBlockCloseH3 = “<” Spnl “/” (“h3” | “H3”) Spnl “>” HtmlBlockH3 = HtmlBlockOpenH3 (HtmlBlockH3 | !HtmlBlockCloseH3 .)* HtmlBlockCloseH3

HtmlBlockOpenH4 = “<” Spnl (“h4” | “H4”) Spnl HtmlAttribute* “>” HtmlBlockCloseH4 = “<” Spnl “/” (“h4” | “H4”) Spnl “>” HtmlBlockH4 = HtmlBlockOpenH4 (HtmlBlockH4 | !HtmlBlockCloseH4 .)* HtmlBlockCloseH4

HtmlBlockOpenH5 = “<” Spnl (“h5” | “H5”) Spnl HtmlAttribute* “>” HtmlBlockCloseH5 = “<” Spnl “/” (“h5” | “H5”) Spnl “>” HtmlBlockH5 = HtmlBlockOpenH5 (HtmlBlockH5 | !HtmlBlockCloseH5 .)* HtmlBlockCloseH5

HtmlBlockOpenH6 = “<” Spnl (“h6” | “H6”) Spnl HtmlAttribute* “>” HtmlBlockCloseH6 = “<” Spnl “/” (“h6” | “H6”) Spnl “>” HtmlBlockH6 = HtmlBlockOpenH6 (HtmlBlockH6 | !HtmlBlockCloseH6 .)* HtmlBlockCloseH6

HtmlBlockOpenMenu = “<” Spnl (“menu” | “MENU”) Spnl HtmlAttribute* “>” HtmlBlockCloseMenu = “<” Spnl “/” (“menu” | “MENU”) Spnl “>” HtmlBlockMenu = HtmlBlockOpenMenu (HtmlBlockMenu | !HtmlBlockCloseMenu .)* HtmlBlockCloseMenu

HtmlBlockOpenNoframes = “<” Spnl (“noframes” | “NOFRAMES”) Spnl HtmlAttribute* “>” HtmlBlockCloseNoframes = “<” Spnl “/” (“noframes” | “NOFRAMES”) Spnl “>” HtmlBlockNoframes = HtmlBlockOpenNoframes (HtmlBlockNoframes | !HtmlBlockCloseNoframes .)* HtmlBlockCloseNoframes

HtmlBlockOpenNoscript = “<” Spnl (“noscript” | “NOSCRIPT”) Spnl HtmlAttribute* “>” HtmlBlockCloseNoscript = “<” Spnl “/” (“noscript” | “NOSCRIPT”) Spnl “>” HtmlBlockNoscript = HtmlBlockOpenNoscript (HtmlBlockNoscript | !HtmlBlockCloseNoscript .)* HtmlBlockCloseNoscript

HtmlBlockOpenOl = “<” Spnl (“ol” | “OL”) Spnl HtmlAttribute* “>” HtmlBlockCloseOl = “<” Spnl “/” (“ol” | “OL”) Spnl “>” HtmlBlockOl = HtmlBlockOpenOl (HtmlBlockOl | !HtmlBlockCloseOl .)* HtmlBlockCloseOl

HtmlBlockOpenP = “<” Spnl (“p” | “P”) Spnl HtmlAttribute* “>” HtmlBlockCloseP = “<” Spnl “/” (“p” | “P”) Spnl “>” HtmlBlockP = HtmlBlockOpenP (HtmlBlockP | !HtmlBlockCloseP .)* HtmlBlockCloseP

HtmlBlockOpenPre = “<” Spnl (“pre” | “PRE”) Spnl HtmlAttribute* “>” HtmlBlockClosePre = “<” Spnl “/” (“pre” | “PRE”) Spnl “>” HtmlBlockPre = HtmlBlockOpenPre (HtmlBlockPre | !HtmlBlockClosePre .)* HtmlBlockClosePre

HtmlBlockOpenTable = “<” Spnl (“table” | “TABLE”) Spnl HtmlAttribute* “>” HtmlBlockCloseTable = “<” Spnl “/” (“table” | “TABLE”) Spnl “>” HtmlBlockTable = HtmlBlockOpenTable (HtmlBlockTable | !HtmlBlockCloseTable .)* HtmlBlockCloseTable

HtmlBlockOpenUl = “<” Spnl (“ul” | “UL”) Spnl HtmlAttribute* “>” HtmlBlockCloseUl = “<” Spnl “/” (“ul” | “UL”) Spnl “>” HtmlBlockUl = HtmlBlockOpenUl (HtmlBlockUl | !HtmlBlockCloseUl .)* HtmlBlockCloseUl

HtmlBlockOpenDd = “<” Spnl (“dd” | “DD”) Spnl HtmlAttribute* “>” HtmlBlockCloseDd = “<” Spnl “/” (“dd” | “DD”) Spnl “>” HtmlBlockDd = HtmlBlockOpenDd (HtmlBlockDd | !HtmlBlockCloseDd .)* HtmlBlockCloseDd

HtmlBlockOpenDt = “<” Spnl (“dt” | “DT”) Spnl HtmlAttribute* “>” HtmlBlockCloseDt = “<” Spnl “/” (“dt” | “DT”) Spnl “>” HtmlBlockDt = HtmlBlockOpenDt (HtmlBlockDt | !HtmlBlockCloseDt .)* HtmlBlockCloseDt

HtmlBlockOpenFrameset = “<” Spnl (“frameset” | “FRAMESET”) Spnl HtmlAttribute* “>” HtmlBlockCloseFrameset = “<” Spnl “/” (“frameset” | “FRAMESET”) Spnl “>” HtmlBlockFrameset = HtmlBlockOpenFrameset (HtmlBlockFrameset | !HtmlBlockCloseFrameset .)* HtmlBlockCloseFrameset

HtmlBlockOpenLi = “<” Spnl (“li” | “LI”) Spnl HtmlAttribute* “>” HtmlBlockCloseLi = “<” Spnl “/” (“li” | “LI”) Spnl “>” HtmlBlockLi = HtmlBlockOpenLi (HtmlBlockLi | !HtmlBlockCloseLi .)* HtmlBlockCloseLi

HtmlBlockOpenTbody = “<” Spnl (“tbody” | “TBODY”) Spnl HtmlAttribute* “>” HtmlBlockCloseTbody = “<” Spnl “/” (“tbody” | “TBODY”) Spnl “>” HtmlBlockTbody = HtmlBlockOpenTbody (HtmlBlockTbody | !HtmlBlockCloseTbody .)* HtmlBlockCloseTbody

HtmlBlockOpenTd = “<” Spnl (“td” | “TD”) Spnl HtmlAttribute* “>” HtmlBlockCloseTd = “<” Spnl “/” (“td” | “TD”) Spnl “>” HtmlBlockTd = HtmlBlockOpenTd (HtmlBlockTd | !HtmlBlockCloseTd .)* HtmlBlockCloseTd

HtmlBlockOpenTfoot = “<” Spnl (“tfoot” | “TFOOT”) Spnl HtmlAttribute* “>” HtmlBlockCloseTfoot = “<” Spnl “/” (“tfoot” | “TFOOT”) Spnl “>” HtmlBlockTfoot = HtmlBlockOpenTfoot (HtmlBlockTfoot | !HtmlBlockCloseTfoot .)* HtmlBlockCloseTfoot

HtmlBlockOpenTh = “<” Spnl (“th” | “TH”) Spnl HtmlAttribute* “>” HtmlBlockCloseTh = “<” Spnl “/” (“th” | “TH”) Spnl “>” HtmlBlockTh = HtmlBlockOpenTh (HtmlBlockTh | !HtmlBlockCloseTh .)* HtmlBlockCloseTh

HtmlBlockOpenThead = “<” Spnl (“thead” | “THEAD”) Spnl HtmlAttribute* “>” HtmlBlockCloseThead = “<” Spnl “/” (“thead” | “THEAD”) Spnl “>” HtmlBlockThead = HtmlBlockOpenThead (HtmlBlockThead | !HtmlBlockCloseThead .)* HtmlBlockCloseThead

HtmlBlockOpenTr = “<” Spnl (“tr” | “TR”) Spnl HtmlAttribute* “>” HtmlBlockCloseTr = “<” Spnl “/” (“tr” | “TR”) Spnl “>” HtmlBlockTr = HtmlBlockOpenTr (HtmlBlockTr | !HtmlBlockCloseTr .)* HtmlBlockCloseTr

HtmlBlockOpenScript = “<” Spnl (“script” | “SCRIPT”) Spnl HtmlAttribute* “>” HtmlBlockCloseScript = “<” Spnl “/” (“script” | “SCRIPT”) Spnl “>” HtmlBlockScript = HtmlBlockOpenScript (!HtmlBlockCloseScript .)* HtmlBlockCloseScript

HtmlBlockInTags = HtmlAnchor

    | HtmlBlockAddress
    | HtmlBlockBlockquote
    | HtmlBlockCenter
    | HtmlBlockDir
    | HtmlBlockDiv
    | HtmlBlockDl
    | HtmlBlockFieldset
    | HtmlBlockForm
    | HtmlBlockH1
    | HtmlBlockH2
    | HtmlBlockH3
    | HtmlBlockH4
    | HtmlBlockH5
    | HtmlBlockH6
    | HtmlBlockMenu
    | HtmlBlockNoframes
    | HtmlBlockNoscript
    | HtmlBlockOl
    | HtmlBlockP
    | HtmlBlockPre
    | HtmlBlockTable
    | HtmlBlockUl
    | HtmlBlockDd
    | HtmlBlockDt
    | HtmlBlockFrameset
    | HtmlBlockLi
    | HtmlBlockTbody
    | HtmlBlockTd
    | HtmlBlockTfoot
    | HtmlBlockTh
    | HtmlBlockThead
    | HtmlBlockTr
    | HtmlBlockScript

HtmlBlock = < ( HtmlBlockInTags | HtmlComment | HtmlBlockSelfClosing | HtmlUnclosed) >

    @BlankLine+
    { if html? then
        RDoc::Markup::Raw.new text
      end }

HtmlUnclosed = “<” Spnl HtmlUnclosedType Spnl HtmlAttribute* Spnl “>”

HtmlUnclosedType = “HR” | “hr”

HtmlBlockSelfClosing = “<” Spnl HtmlBlockType Spnl HtmlAttribute* “/” Spnl “>”

HtmlBlockType = “ADDRESS” |

    "BLOCKQUOTE" |
    "CENTER" |
    "DD" |
    "DIR" |
    "DIV" |
    "DL" |
    "DT" |
    "FIELDSET" |
    "FORM" |
    "FRAMESET" |
    "H1" |
    "H2" |
    "H3" |
    "H4" |
    "H5" |
    "H6" |
    "HR" |
    "ISINDEX" |
    "LI" |
    "MENU" |
    "NOFRAMES" |
    "NOSCRIPT" |
    "OL" |
    "P" |
    "PRE" |
    "SCRIPT" |
    "TABLE" |
    "TBODY" |
    "TD" |
    "TFOOT" |
    "TH" |
    "THEAD" |
    "TR" |
    "UL" |
    "address" |
    "blockquote" |
    "center" |
    "dd" |
    "dir" |
    "div" |
    "dl" |
    "dt" |
    "fieldset" |
    "form" |
    "frameset" |
    "h1" |
    "h2" |
    "h3" |
    "h4" |
    "h5" |
    "h6" |
    "hr" |
    "isindex" |
    "li" |
    "menu" |
    "noframes" |
    "noscript" |
    "ol" |
    "p" |
    "pre" |
    "script" |
    "table" |
    "tbody" |
    "td" |
    "tfoot" |
    "th" |
    "thead" |
    "tr" |
    "ul"

StyleOpen = “<” Spnl (“style” | “STYLE”) Spnl HtmlAttribute* “>” StyleClose = “<” Spnl “/” (“style” | “STYLE”) Spnl “>” InStyleTags = StyleOpen (!StyleClose .)* StyleClose StyleBlock = < InStyleTags >

    @BlankLine*
    { if css? then
        RDoc::Markup::Raw.new text
      end }

Inlines = ( !@Endline Inline:i { i }

    | @Endline:c &Inline { c } )+:chunks @Endline?
    { chunks }

Inline = Str

    | @Endline
    | UlOrStarLine
    | @Space
    | Strong
    | Emph
    | Image
    | Link
    | NoteReference
    | InlineNote
    | Code
    | RawHtml
    | Entity
    | EscapedChar
    | Symbol

Space = @Spacechar+ { “ ” }

Str = @StartList:a

    < @NormalChar+ > { a = text }
    ( StrChunk:c { a << c } )* { a }

StrChunk = < (@NormalChar | /_+/ &Alphanumeric)+ > { text }

EscapedChar = “\” !@Newline < /[:\`|*_{}[]()#+.!><-]/ > { text }

Entity = ( HexEntity | DecEntity | CharEntity ):a { a }

Endline = @LineBreak | @TerminalEndline | @NormalEndline

NormalEndline = @Sp @Newline !@BlankLine !“>” !AtxStart

    !(Line /={3,}|-{3,}=/ @Newline)
    { "\n" }

TerminalEndline = @Sp @Newline @Eof

LineBreak = “ ” @NormalEndline { RDoc::Markup::HardBreak.new }

Symbol = < @SpecialChar >

    { text }

# This keeps the parser from getting bogged down on long strings of '*' or '_', # or strings of '*' or '_' with space on each side: UlOrStarLine = (UlLine | StarLine):a { a } StarLine = < /*{4,}/ > { text } |

    < @Spacechar /\*+/ &@Spacechar > { text }

UlLine = < /_{4,}/ > { text } |

    < @Spacechar /_+/ &@Spacechar > { text }

Emph = EmphStar | EmphUl

OneStarOpen = !StarLine “*” !@Spacechar !@Newline OneStarClose = !@Spacechar !@Newline Inline:a “*”

    { a }

EmphStar = OneStarOpen

    @StartList:a
    ( !OneStarClose Inline:l { a << l } )*
    OneStarClose:l { a << l }
    { emphasis a.join }

OneUlOpen = !UlLine “_” !@Spacechar !@Newline OneUlClose = !@Spacechar !@Newline Inline:a “_” # !Alphanumeric # TODO check

    { a }

EmphUl = OneUlOpen

    @StartList:a
    ( !OneUlClose Inline:l { a << l } )*
    OneUlClose:l { a << l }
    { emphasis a.join }

Strong = StrongStar | StrongUl

TwoStarOpen = !StarLine “**” !@Spacechar !@Newline TwoStarClose = !@Spacechar !@Newline Inline:a “**” { a }

StrongStar = TwoStarOpen

    @StartList:a
    ( !TwoStarClose Inline:l { a << l } )*
    TwoStarClose:l { a << l }
    { strong a.join }

TwoUlOpen = !UlLine “__” !@Spacechar !@Newline TwoUlClose = !@Spacechar !@Newline Inline:a “__” # !Alphanumeric # TODO check

    { a }

StrongUl = TwoUlOpen

    @StartList:a
    ( !TwoUlClose Inline:i { a << i } )*
    TwoUlClose:l { a << l }
    { strong a.join }

# TODO alt text support Image = “!” ( ExplicitLink | ReferenceLink ):a

    { "rdoc-image:#{a[/\[(.*)\]/, 1]}" }

Link = ExplicitLink | ReferenceLink | AutoLink

ReferenceLink = ReferenceLinkDouble | ReferenceLinkSingle

ReferenceLinkDouble = Label:content < Spnl > !“[]” Label:label

    { link_to content, label, text }

ReferenceLinkSingle = Label:content < (Spnl “[]”)? >

    { link_to content, content, text }

ExplicitLink = Label:l Spnl “(” @Sp Source:s Spnl Title @Sp “)”

    { "{#{l}}[#{s}]" }

Source = ( “<” < SourceContents > “>” | < SourceContents > )

    { text }

SourceContents = ( ( !“(” !“)” !“>” Nonspacechar )+ | “(” SourceContents “)”)*

    | ""

Title = ( TitleSingle | TitleDouble | “” ):a

    { a }

TitleSingle = “'” ( !( “'” @Sp ( “)” | @Newline ) ) . )* “'”

TitleDouble = “"” ( !( “"” @Sp ( “)” | @Newline ) ) . )* “"”

AutoLink = AutoLinkUrl | AutoLinkEmail

AutoLinkUrl = “<” < /[A-Za-z]+/ “://” ( !@Newline !“>” . )+ > “>”

    { text }

AutoLinkEmail = “<” (“mailto:”)? < /[w+./!%~$-]+/i “@” ( !@Newline !“>” . )+ > “>”

    { "mailto:#{text}" }

Reference = @NonindentSpace !“[]”

      Label:label ":" Spnl RefSrc:link RefTitle @BlankLine+
    { # TODO use title
      reference label, link
      nil
    }

Label = “[” ( !“^” &{ notes? } | &. &{ !notes? } )

    @StartList:a
    ( !"]" Inline:l { a << l } )*
    "]"
    { a.join.gsub(/\s+/, ' ') }

RefSrc = < Nonspacechar+ > { text }

RefTitle = ( RefTitleSingle | RefTitleDouble | RefTitleParens | EmptyTitle )

EmptyTitle = “”

RefTitleSingle = Spnl “'” < ( !( “'” @Sp @Newline | @Newline ) . )* > “'” { text }

RefTitleDouble = Spnl “"” < ( !(“"” @Sp @Newline | @Newline) . )* > “"” { text }

RefTitleParens = Spnl “(” < ( !(“)” @Sp @Newline | @Newline) . )* > “)” { text }

References = ( Reference | SkipBlock )*

Ticks1 = “`” !“`” Ticks2 = ““” !“`” Ticks3 = ““`” !“`” Ticks4 = “““” !“`” Ticks5 = “““`” !“`”

Code = ( Ticks1 @Sp < (

        ( !"`" Nonspacechar )+ | !Ticks1 /`+/ |
        !( @Sp Ticks1 ) ( @Spacechar | @Newline !@BlankLine )
      )+ > @Sp Ticks1 |
      Ticks2 @Sp < (
        ( !"`" Nonspacechar )+ |
        !Ticks2 /`+/ |
        !( @Sp Ticks2 ) ( @Spacechar | @Newline !@BlankLine )
      )+ > @Sp Ticks2 |
      Ticks3 @Sp < (
        ( !"`" Nonspacechar )+ |
        !Ticks3 /`+/ |
        !( @Sp Ticks3 ) ( @Spacechar | @Newline !@BlankLine )
      )+ > @Sp Ticks3 |
      Ticks4 @Sp < (
        ( !"`" Nonspacechar )+ |
        !Ticks4 /`+/ |
        !( @Sp Ticks4 ) ( @Spacechar | @Newline !@BlankLine )
      )+ > @Sp Ticks4 |
      Ticks5 @Sp < (
        ( !"`" Nonspacechar )+ |
        !Ticks5 /`+/ |
        !( @Sp Ticks5 ) ( @Spacechar | @Newline !@BlankLine )
      )+ > @Sp Ticks5
    )
    { "<code>#{text}</code>" }

RawHtml = < (HtmlComment | HtmlBlockScript | HtmlTag) >

    { if html? then text else '' end }

BlankLine = @Sp @Newline { “n” }

Quoted = “"” (!“"” .)* “"” | “'” (!“'” .)* “'” HtmlAttribute = (AlphanumericAscii | “-”)+ Spnl (“=” Spnl (Quoted | (!“>” Nonspacechar)+))? Spnl HtmlComment = “<!–” (!“–>” .)* “–>” HtmlTag = “<” Spnl “/”? AlphanumericAscii+ Spnl HtmlAttribute* “/”? Spnl “>” Eof = !. Nonspacechar = !@Spacechar !@Newline . Sp = @Spacechar* Spnl = @Sp (@Newline @Sp)? SpecialChar = /[*_`&()<!#\'“]/ | @ExtendedSpecialChar NormalChar = !( @SpecialChar | @Spacechar | @Newline ) . Digit = [0-9]

%literals = RDoc::Markdown::Literals Alphanumeric = %literals.Alphanumeric AlphanumericAscii = %literals.AlphanumericAscii BOM = %literals.BOM Newline = %literals.Newline NonAlphanumeric = %literals.NonAlphanumeric Spacechar = %literals.Spacechar

HexEntity = /&#x/i < /[0-9a-fA-F]+/ > “;”

    { [text.to_i(16)].pack 'U' }

DecEntity = “&#” < /[0-9]+/ > “;”

    { [text.to_i].pack 'U' }

CharEntity = “&” </[A-Za-z0-9]+/ > “;”

    { if entity = HTML_ENTITIES[text] then
        entity.pack 'U*'
      else
        "&#{text};"
      end
    }

NonindentSpace = / {0,3}/ Indent = /t| / IndentedLine = Indent Line OptionallyIndentedLine = Indent? Line

# StartList starts a list data structure that can be added to with cons: StartList = &.

    { [] }

Line = @RawLine:a { a } RawLine = ( < (!“r” !“n” .)* @Newline >

    | < .+ > @Eof ) { text }

SkipBlock = HtmlBlock

    | ( !"#" !SetextBottom1 !SetextBottom2 !@BlankLine @RawLine )+
      @BlankLine*
    | @BlankLine+
    | @RawLine

# Syntax extensions

ExtendedSpecialChar = &{ notes? } ( “^” )

NoteReference = &{ notes? }

    RawNoteReference:ref
    { note_for ref }

RawNoteReference = “[^” < ( !@Newline !“]” . )+ > “]” { text }

# TODO multiple paragraphs for a footnote Note = &{ notes? }

    @NonindentSpace RawNoteReference:ref ":" @Sp
    @StartList:a
    RawNoteBlock:i { a.concat i }
    ( &Indent RawNoteBlock:i { a.concat i } )*
    { @footnotes[ref] = paragraph a

      nil
    }

InlineNote = &{ notes? }

    "^["
    @StartList:a
    ( !"]" Inline:l { a << l } )+
    "]"
    {
      ref = [:inline, @note_order.length]
      @footnotes[ref] = paragraph a

      note_for ref
    }

Notes = ( Note | SkipBlock )*

RawNoteBlock = @StartList:a

    ( !@BlankLine OptionallyIndentedLine:l { a << l } )+
    ( < @BlankLine* > { a << text } )
    { a }

# Markdown extensions added by RDoc follow

CodeFence = &{ github? }

    Ticks3 (@Sp StrChunk:format)? Spnl < (
      ( !"`" Nonspacechar )+ |
      !Ticks3 /`+/ |
      Spacechar |
      @Newline
    )+ > Ticks3 @Sp @Newline*
    { verbatim = RDoc::Markup::Verbatim.new text
      verbatim.format = format.intern if format
      verbatim
    }

DefinitionList = &{ definition_lists? }

    ( DefinitionListItem+:list )
    { RDoc::Markup::List.new :NOTE, *list.flatten }

DefinitionListItem = ( DefinitionListLabel+ ):label

    ( DefinitionListDefinition+ ):defns
    { list_items = []
      list_items <<
        RDoc::Markup::ListItem.new(label, defns.shift)

      list_items.concat defns.map { |defn|
        RDoc::Markup::ListItem.new nil, defn
      } unless list_items.empty?

      list_items
    }

DefinitionListLabel = StrChunk:label @Sp @Newline

    { label }

DefinitionListDefinition = @NonindentSpace “:” @Space Inlines:a @BlankLine+

    { paragraph a }