1:- module(md_span_link, [
    2    md_span_link//1 % -Html
    3]).

Markdown span-level link parsing

Parses Markdown span-level links and images. Separated from the md_span module for code clarity. */

   11:- use_module(library(dcg/basics)).   12
   13:- use_module(md_links).   14:- use_module(md_line).
 md_span_link(-Link)// is det
Recognizes different types of links from the stream of symbol codes.
   21md_span_link(Link) -->
   22    plain_http_link(Link), !.
   23
   24md_span_link(Link) -->
   25    angular_link(Link), !.
   26
   27md_span_link(Link) -->
   28    angular_mail_link(Link), !.
   29
   30md_span_link(Link) -->
   31    normal_link(Link), !.
   32
   33md_span_link(Link) -->
   34    reference_link(Link), !.
   35
   36md_span_link(Image) -->
   37    reference_image(Image), !.
   38
   39md_span_link(Image) -->
   40    image(Image).
   41
   42% Recognizes a plain link.
   43
   44plain_http_link(Link) -->
   45    http_prefix(Prefix), inline_string(Codes), link_end,
   46    {
   47        atom_codes(Atom, Codes),
   48        atom_concat(Prefix, Atom, Url),
   49        link(Url, '', Url, Link)
   50    }.
   51
   52link_end -->
   53    eos, !.
   54
   55link_end -->
   56    lookahead(Code),
   57    { code_type(Code, space) }.
   58
   59http_prefix('http://') -->
   60    "http://".
   61
   62http_prefix('https://') -->
   63    "https://".
   64
   65% Recognizes inline automatic http(s) <link>.
   66
   67angular_link(Link) -->
   68    "<", http_prefix(Prefix),
   69    inline_string(Codes), ">",
   70    {
   71        atom_codes(Atom, Codes),
   72        atom_concat(Prefix, Atom, Url),
   73        link(Url, '', Url, Link)
   74    }.
   75
   76% Recognizes inline mail link <address@example.com>
   77
   78angular_mail_link(Link) -->
   79    "<", inline_string(User),
   80    "@", inline_string(Host), ">",
   81    {
   82        append(User, [0'@|Host], Codes),
   83        atom_codes(Address, Codes),
   84        mail_link(Address, Link)
   85    }.
   86
   87% Recognizes a normal Markdown link.
   88% Example: [an example](http://example.com/ "Title")
   89% More info:
   90% http://daringfireball.net/projects/markdown/syntax#link
   91
   92normal_link(Link) -->
   93    label(Label),
   94    url_title(Url, Title),
   95    { link(Url, Title, Label, Link) }.
   96
   97% Recognizes image ![Alt text](/path/to/img.jpg).
   98% With optional title: ![Alt text](/path/to/img.jpg "Optional title").
   99% More info: http://daringfireball.net/projects/markdown/syntax#img
  100
  101image(Image) -->
  102    "!", label(Alt),
  103    url_title(Url, Title),
  104    {
  105        (   Title = ''
  106        ->  Image = img([src=Url, alt=Alt])
  107        ;   Image = img([src=Url, alt=Alt, title=Title]))
  108    }.
  109
  110% Recognizes a reference link.
  111% Example: This is [an example][id] reference-style link.
  112% Fails when the reference is not defined. Identifier
  113% can be empty, then the lowercase label is used as the
  114% identifier.
  115
  116reference_link(Link) -->
  117    label(Label),
  118    whites, identifier(Id),
  119    {
  120        (   Id = ''
  121        ->  downcase_atom(Label, RealId)
  122        ;   RealId = Id),
  123        md_link(RealId, Url, Title),
  124        link(Url, Title, Label, Link)
  125    }.
  126
  127% Recognizes a reference-linked image.
  128% Example: ![Alt text][id]
  129
  130reference_image(Image) -->
  131    "!", label(Label),
  132    whites, identifier(Id),
  133    {
  134        (   Id = ''
  135        ->  downcase_atom(Label, RealId)
  136        ;   RealId = Id),
  137        md_link(RealId, Url, Title),
  138        (   Title = ''
  139        ->  Image = img([src=Url, alt=Label])
  140        ;   Image = img([src=Url, alt=Label, title=Title]))
  141    }.
  142
  143% Same as label block but the result
  144% is lowercased.
  145
  146identifier(Id) -->
  147    label(Label),
  148    { downcase_atom(Label, Id) }.
  149
  150% Recognizes a label block.
  151% Example: [This is a link].
  152
  153label(Label) -->
  154    "[", whites, inline_string(Codes), whites, "]", !,
  155    { atom_codes(Label, Codes) }.
  156
  157% Recognizes a normal link URL/title
  158% block. Example: (http://example.com "Title").
  159
  160url_title(Url, Title) -->
  161    "(", inline_string(UrlCodes),
  162    whites, "\"", inline_string(TitleCodes), "\"", whites, ")", !,
  163    {
  164        atom_codes(Url, UrlCodes),
  165        atom_codes(Title, TitleCodes)
  166    }.
  167
  168url_title(Url, Title) -->
  169    "(", inline_string(UrlCodes),
  170    whites, "'", inline_string(TitleCodes), "'", whites, ")", !,
  171    {
  172        atom_codes(Url, UrlCodes),
  173        atom_codes(Title, TitleCodes)
  174    }.
  175
  176url_title(Url, '') -->
  177    "(", inline_string(Codes), ")", !,
  178    { atom_codes(Url, Codes) }.
  179
  180% Creates an HTML link element. Has no title
  181% attribute when title is empty.
  182
  183link(Url, '', Label, Element):- !,
  184    Element = a([href=Url], Label).
  185
  186link(Url, Title, Label, Element):-
  187    Element = a([href=Url, title=Title], Label).
  188
  189% Creates an HTML link element for an email address.
  190% Does not try to mangle the address as the original
  191% spec. XXX maybe should do that?
  192
  193mail_link(Address, Element):-
  194    atom_concat('mailto:', Address, Href),
  195    Element = a([href=Href], Address)