1:- module(md_header, [
    2    md_header//1 % -Header
    3]).

Markdown header parser.

Recognizes atx and setext-styled headers. */

   10:- use_module(library(dcg/basics)).   11
   12:- use_module(md_line).   13:- use_module(md_trim).
 md_header(-Header)// is semidet
Recognizes either setext or atx-styled headings.
   20md_header(Header) -->
   21    setext_header(Header), !.
   22
   23md_header(Header) -->
   24    atx_header(Header).
   25
   26% Recognizes setext-styled headings.
   27% Output is a term like h1("heading").
   28
   29setext_header(Header) -->
   30    non_empty_line(Codes),
   31    (   equals_line,
   32        { Name = h1 }
   33    ;   dashes_line,
   34        { Name = h2 }),
   35    {
   36        string_codes(Title, Codes),
   37        Header =.. [Name, Title]
   38    }.
   39
   40% Recognizes atx-styled heading.
   41% Output is a term like h1(heading).
   42
   43atx_header(Header) -->
   44    atx_level(Level), whites,
   45    atx_header_text(Codes), !,
   46    discard_to_line_end,
   47    {
   48        trim(Codes, Trimmed),
   49        string_codes(Title, Trimmed),
   50        atomic_concat(h, Level, Name),
   51        Header =.. [Name,Title]
   52    }.
   53
   54% Recognizes atx-styled header
   55% prefix.
   56
   57atx_level(1) --> "# ".
   58atx_level(2) --> "## ".
   59atx_level(3) --> "### ".
   60atx_level(4) --> "#### ".
   61atx_level(5) --> "##### ".
   62atx_level(6) --> "###### ".
   63
   64% Recognizes header text for
   65% atx-styles headings. Such text
   66% ends with #, line end, or eos.
   67% Escapes \# must be also processed.
   68% Line ending is not consumed.
   69
   70atx_header_text([]) -->
   71    "#", !.
   72
   73atx_header_text([]) -->
   74    lookahead_ln_or_eos, !.
   75
   76atx_header_text([0'#|Codes]) -->
   77    "\\#", !,
   78    atx_header_text(Codes).
   79
   80atx_header_text([Code|Codes]) -->
   81    [Code], { Code \= 0'# }, !,
   82    atx_header_text(Codes).
   83
   84% Line filles with one or more dashes.
   85
   86dashes_line -->
   87    "-", dashes_line_rest.
   88
   89dashes_line_rest -->
   90    eos, !.
   91
   92dashes_line_rest -->
   93    "\n", !.
   94
   95dashes_line_rest -->
   96    "-", dashes_line_rest.
   97
   98equals_line -->
   99    "=", equals_line_rest.
  100
  101equals_line_rest -->
  102    eos, !.
  103
  104equals_line_rest -->
  105    "\n", !.
  106
  107equals_line_rest -->
  108    "=", equals_line_rest