View source with raw comments or as raw
   1/*  Part of SWI-Prolog
   2
   3    Author:        Jan Wielemaker
   4    E-mail:        J.Wielemaker@vu.nl
   5    WWW:           http://www.swi-prolog.org
   6    Copyright (c)  2007-2015, University of Amsterdam
   7                              VU University Amsterdam
   8    All rights reserved.
   9
  10    Redistribution and use in source and binary forms, with or without
  11    modification, are permitted provided that the following conditions
  12    are met:
  13
  14    1. Redistributions of source code must retain the above copyright
  15       notice, this list of conditions and the following disclaimer.
  16
  17    2. Redistributions in binary form must reproduce the above copyright
  18       notice, this list of conditions and the following disclaimer in
  19       the documentation and/or other materials provided with the
  20       distribution.
  21
  22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33    POSSIBILITY OF SUCH DAMAGE.
  34*/
  35
  36:- module(pldoc_latex,
  37          [ doc_latex/3,                % +Items, +OutFile, +Options
  38            latex_for_file/3,           % +FileSpec, +Out, +Options
  39            latex_for_wiki_file/3,      % +FileSpec, +Out, +Options
  40            latex_for_predicates/3      % +PI, +Out, +Options
  41          ]).
  42:- use_module(library(pldoc)).
  43:- use_module(library(readutil)).
  44:- use_module(library(error)).
  45:- use_module(library(apply)).
  46:- use_module(library(option)).
  47:- use_module(library(lists)).
  48:- use_module(library(debug)).
  49:- use_module(pldoc(doc_wiki)).
  50:- use_module(pldoc(doc_process)).
  51:- use_module(pldoc(doc_modes)).
  52:- use_module(pldoc(doc_html),          % we cannot import all as the
  53              [ doc_file_objects/5,     % \commands have the same name
  54                unquote_filespec/2,
  55                doc_tag_title/2,
  56                existing_linked_file/2,
  57                pred_anchor_name/3,
  58                private/2,
  59                (multifile)/2,
  60                is_pi/1,
  61                is_op_type/2
  62              ]).
  63
  64/** <module> PlDoc LaTeX backend
  65
  66This  module  translates  the  Herbrand   term  from  the  documentation
  67extracting module doc_wiki.pl into a  LaTeX   document  for  us with the
  68pl.sty LaTeX style file. The function of  this module is very similar to
  69doc_html.pl, providing the HTML backend,  and the implementation follows
  70the same paradigm. The module can
  71
  72        * Generate LaTeX documentation for a Prolog file, both for
  73        printing and embedding in a larger document using
  74        latex_for_file/3.
  75
  76        * Generate LaTeX from a Wiki file using latex_for_wiki_file/3
  77
  78        * Generate LaTeX for a single predicate or a list of predicates
  79        for embedding in a document using latex_for_predicates/3.
  80
  81@tbd See TODO
  82@author Jan Wielemaker
  83*/
  84
  85:- predicate_options(doc_latex/3, 3,
  86                     [ stand_alone(boolean),
  87                       public_only(boolean),
  88                       section_level(oneof([section,subsection,subsubsection])),
  89                       summary(atom)
  90                     ]).
  91:- predicate_options(latex_for_file/3, 3,
  92                     [ stand_alone(boolean),
  93                       public_only(boolean),
  94                       section_level(oneof([section,subsection,subsubsection]))
  95                     ]).
  96:- predicate_options(latex_for_predicates/3, 3,
  97                     [                          % no options
  98                     ]).
  99:- predicate_options(latex_for_wiki_file/3, 3,
 100                     [ stand_alone(boolean),
 101                       public_only(boolean),
 102                       section_level(oneof([section,subsection,subsubsection]))
 103                     ]).
 104
 105
 106:- thread_local
 107    options/1,
 108    documented/1.
 109
 110current_options(Options) :-
 111    options(Current),
 112    !,
 113    Options = Current.
 114current_options([]).
 115
 116%!  doc_latex(+Spec, +OutFile, +Options) is det.
 117%
 118%   Process one or  more  objects,  writing   the  LaTeX  output  to
 119%   OutFile.  Spec is one of:
 120%
 121%     - Name/Arity
 122%       Generate documentation for predicate
 123%     - Name//Arity
 124%       Generate documentation for DCG rule
 125%     - File
 126%       If File is a prolog file (as defined by
 127%       user:prolog_file_type/2), process using
 128%       latex_for_file/3, otherwise process using
 129%       latex_for_wiki_file/3.
 130%
 131%   Typically Spec is either a  list  of   filenames  or  a  list of
 132%   predicate indicators.   Defined options are:
 133%
 134%     - stand_alone(+Bool)
 135%       If =true= (default), create a document that can be run
 136%       through LaTeX.  If =false=, produce a document to be
 137%       included in another LaTeX document.
 138%     - public_only(+Bool)
 139%       If =true= (default), only emit documentation for
 140%       exported predicates.
 141%     - section_level(+Level)
 142%       Outermost section level produced. Level is the
 143%       name of a LaTeX section command.  Default is =section=.
 144%     - summary(+File)
 145%       Write summary declarations to the named File.
 146%     - modules(+List)
 147%       If [[Name/Arity]] needs to be resolved, search for the
 148%       predicates in the given modules.
 149%     - module(+Module)
 150%       Same as modules([Module]).
 151
 152doc_latex(Spec, OutFile, Options) :-
 153    load_urldefs,
 154    merge_options(Options,
 155                  [ include_reexported(true)
 156                  ],
 157                  Options1),
 158    retractall(documented(_)),
 159    setup_call_cleanup(
 160        asserta(options(Options), Ref),
 161        phrase(process_items(Spec, [body], Options1), Tokens),
 162        erase(Ref)),
 163    setup_call_cleanup(
 164        open(OutFile, write, Out),
 165        print_latex(Out, Tokens, Options1),
 166        close(Out)),
 167    latex_summary(Options).
 168
 169process_items([], Mode, _) -->
 170    !,
 171    pop_mode(body, Mode, _).
 172process_items([H|T], Mode, Options) -->
 173    process_items(H, Mode, Mode1, Options),
 174    process_items(T, Mode1, Options).
 175process_items(Spec, Mode, Options) -->
 176    {Mode = [Mode0|_]},
 177    process_items(Spec, Mode, Mode1, Options),
 178    pop_mode(Mode0, Mode1, _).
 179
 180process_items(PI, Mode0, Mode, Options) -->
 181    { is_pi(PI) },
 182    !,
 183    need_mode(description, Mode0, Mode),
 184    latex_tokens_for_predicates(PI, Options).
 185process_items(FileSpec, Mode0, Mode, Options) -->
 186    {   (   absolute_file_name(FileSpec,
 187                               [ file_type(prolog),
 188                                 access(read),
 189                                 file_errors(fail)
 190                               ],
 191                               File)
 192        ->  true
 193        ;   absolute_file_name(FileSpec,
 194                               [ access(read)
 195                               ],
 196                               File)
 197        ),
 198        file_name_extension(_Base, Ext, File)
 199    },
 200    need_mode(body, Mode0, Mode),
 201    (   { user:prolog_file_type(Ext, prolog) }
 202    ->  latex_tokens_for_file(File, Options)
 203    ;   latex_tokens_for_wiki_file(File, Options)
 204    ).
 205
 206
 207%!  latex_for_file(+File, +Out, +Options) is det.
 208%
 209%   Generate a LaTeX description of all commented predicates in
 210%   File, writing the LaTeX text to the stream Out. Supports
 211%   the options =stand_alone=, =public_only= and =section_level=.
 212%   See doc_latex/3 for a description of the options.
 213
 214latex_for_file(FileSpec, Out, Options) :-
 215    load_urldefs,
 216    phrase(latex_tokens_for_file(FileSpec, Options), Tokens),
 217    print_latex(Out, Tokens, Options).
 218
 219
 220%!  latex_tokens_for_file(+FileSpec, +Options)//
 221
 222latex_tokens_for_file(FileSpec, Options, Tokens, Tail) :-
 223    absolute_file_name(FileSpec,
 224                       [ file_type(prolog),
 225                         access(read)
 226                       ],
 227                       File),
 228    doc_file_objects(FileSpec, File, Objects, FileOptions, Options),
 229    asserta(options(Options), Ref),
 230    call_cleanup(phrase(latex([ \file_header(File, FileOptions)
 231                              | \objects(Objects, FileOptions)
 232                              ]),
 233                        Tokens, Tail),
 234                 erase(Ref)).
 235
 236
 237%!  latex_for_wiki_file(+File, +Out, +Options) is det.
 238%
 239%   Write a LaTeX translation of  a  Wiki   file  to  the steam Out.
 240%   Supports   the   options   =stand_alone=,    =public_only=   and
 241%   =section_level=.  See  doc_latex/3  for  a  description  of  the
 242%   options.
 243
 244latex_for_wiki_file(FileSpec, Out, Options) :-
 245    load_urldefs,
 246    phrase(latex_tokens_for_wiki_file(FileSpec, Options), Tokens),
 247    print_latex(Out, Tokens, Options).
 248
 249latex_tokens_for_wiki_file(FileSpec, Options, Tokens, Tail) :-
 250    absolute_file_name(FileSpec, File,
 251                       [ access(read)
 252                       ]),
 253    read_file_to_codes(File, String, []),
 254    b_setval(pldoc_file, File),
 255    asserta(options(Options), Ref),
 256    call_cleanup((wiki_codes_to_dom(String, [], DOM),
 257                  phrase(latex(DOM), Tokens, Tail)
 258                 ),
 259                 (nb_delete(pldoc_file),
 260                  erase(Ref))).
 261
 262
 263%!  latex_for_predicates(+PI:list, +Out, +Options) is det.
 264%
 265%   Generate LaTeX for a list  of   predicate  indicators. This does
 266%   *not*   produce   the    \begin{description}...\end{description}
 267%   environment, just a plain list   of \predicate, etc. statements.
 268%   The current implementation ignores Options.
 269
 270latex_for_predicates(Spec, Out, Options) :-
 271    load_urldefs,
 272    phrase(latex_tokens_for_predicates(Spec, Options), Tokens),
 273    print_latex(Out, [nl_exact(0)|Tokens], Options).
 274
 275latex_tokens_for_predicates([], _Options) --> !.
 276latex_tokens_for_predicates([H|T], Options) -->
 277    !,
 278    latex_tokens_for_predicates(H, Options),
 279    latex_tokens_for_predicates(T, Options).
 280latex_tokens_for_predicates(PI, Options) -->
 281    { generic_pi(PI),
 282      !,
 283      (   doc_comment(PI, Pos, _Summary, Comment)
 284      ->  true
 285      ;   Comment = ''
 286      )
 287    },
 288    object(PI, Pos, Comment, [description], _, Options).
 289latex_tokens_for_predicates(Spec, Options) -->
 290    { findall(PI, documented_pi(Spec, PI, Options), List),
 291      (   List == []
 292      ->  print_message(warning, pldoc(no_predicates_from(Spec)))
 293      ;   true
 294      )
 295    },
 296    latex_tokens_for_predicates(List, Options).
 297
 298documented_pi(Spec, PI, Options) :-
 299    option(modules(List), Options),
 300    member(M, List),
 301    generalise_spec(Spec, PI, M),
 302    doc_comment(PI, _Pos, _Summary, _Comment),
 303    !.
 304documented_pi(Spec, PI, Options) :-
 305    option(module(M), Options),
 306    generalise_spec(Spec, PI, M),
 307    doc_comment(PI, _Pos, _Summary, _Comment),
 308    !.
 309documented_pi(Spec, PI, _Options) :-
 310    generalise_spec(Spec, PI, _),
 311    doc_comment(PI, _Pos, _Summary, _Comment).
 312
 313generic_pi(Module:Name/Arity) :-
 314    atom(Module), atom(Name), integer(Arity),
 315    !.
 316generic_pi(Module:Name//Arity) :-
 317    atom(Module), atom(Name), integer(Arity).
 318
 319generalise_spec(Name/Arity, M:Name/Arity, M).
 320generalise_spec(Name//Arity, M:Name//Arity, M).
 321
 322
 323                 /*******************************
 324                 *       LATEX PRODUCTION       *
 325                 *******************************/
 326
 327:- thread_local
 328    fragile/0.                      % provided when in fragile mode
 329
 330latex([]) -->
 331    !,
 332    [].
 333latex(Atomic) -->
 334    { string(Atomic),
 335      atom_string(Atom, Atomic),
 336      sub_atom(Atom, 0, _, 0, 'LaTeX')
 337    },
 338    !,
 339    [ latex('\\LaTeX{}') ].
 340latex(Atomic) -->                       % can this actually happen?
 341    { atomic(Atomic),
 342      !,
 343      atom_string(Atom, Atomic),
 344      findall(x, sub_atom(Atom, _, _, _, '\n'), Xs),
 345      length(Xs, Lines)
 346    },
 347    (   {Lines == 0}
 348    ->  [ Atomic ]
 349    ;   [ nl(Lines) ]
 350    ).
 351latex(List) -->
 352    latex_special(List, Rest),
 353    !,
 354    latex(Rest).
 355latex(w(Word)) -->
 356    [ Word ].
 357latex([H|T]) -->
 358    !,
 359    (   latex(H)
 360    ->  latex(T)
 361    ;   { print_message(error, latex(failed(H))) },
 362        latex(T)
 363    ).
 364
 365% high level commands
 366latex(h1(Attrs, Content)) -->
 367    latex_section(0, Attrs, Content).
 368latex(h2(Attrs, Content)) -->
 369    latex_section(1, Attrs, Content).
 370latex(h3(Attrs, Content)) -->
 371    latex_section(2, Attrs, Content).
 372latex(h4(Attrs, Content)) -->
 373    latex_section(3, Attrs, Content).
 374latex(p(Content)) -->
 375    [ nl_exact(2) ],
 376    latex(Content).
 377latex(blockquote(Content)) -->
 378    latex(cmd(begin(quote))),
 379    latex(Content),
 380    latex(cmd(end(quote))).
 381latex(center(Content)) -->
 382    latex(cmd(begin(center))),
 383    latex(Content),
 384    latex(cmd(end(center))).
 385latex(a(Attrs, Content)) -->
 386    { attribute(href(HREF), Attrs) },
 387    (   {HREF == Content}
 388    ->  latex(cmd(url(no_escape(HREF))))
 389    ;   { atom_concat(#,Sec,HREF) }
 390    ->  latex([Content, ' (', cmd(secref(Sec)), ')'])
 391    ;   latex(cmd(href(no_escape(HREF), Content)))
 392    ).
 393latex(hr(_)) -->
 394    latex(cmd(hrule)).
 395latex(code(CodeList)) -->
 396    { is_list(CodeList),
 397      !,
 398      atomic_list_concat(CodeList, Atom)
 399    },
 400    (   {fragile}
 401    ->  latex(cmd(const(Atom)))
 402    ;   [ verb(Atom) ]
 403    ).
 404latex(code(Code)) -->
 405    { identifier(Code) },
 406    !,
 407    latex(cmd(const(Code))).
 408latex(code(Code)) -->
 409    (   {fragile}
 410    ->  latex(cmd(const(Code)))
 411    ;   [ verb(Code) ]
 412    ).
 413latex(b(Code)) -->
 414    latex(cmd(textbf(Code))).
 415latex(strong(Code)) -->
 416    latex(cmd(textbf(Code))).
 417latex(i(Code)) -->
 418    latex(cmd(textit(Code))).
 419latex(var(Var)) -->
 420    latex(cmd(arg(Var))).
 421latex(pre(_Class, Code)) -->
 422    [ nl_exact(2), code(Code), nl_exact(2) ].
 423latex(ul(Content)) -->
 424    { if_short_list(Content, shortlist, itemize, Env) },
 425    latex(cmd(begin(Env))),
 426    latex(Content),
 427    latex(cmd(end(Env))).
 428latex(ol(Content)) -->
 429    latex(cmd(begin(enumerate))),
 430    latex(Content),
 431    latex(cmd(end(enumerate))).
 432latex(li(Content)) -->
 433    latex(cmd(item)),
 434    latex(Content).
 435latex(dl(_, Content)) -->
 436    latex(cmd(begin(description))),
 437    latex(Content),
 438    latex(cmd(end(description))).
 439latex(dd(_, Content)) -->
 440    latex(Content).
 441latex(dd(Content)) -->
 442    latex(Content).
 443latex(dt(class=term, \term(Text, Term, Bindings))) -->
 444    termitem(Text, Term, Bindings).
 445latex(dt(Content)) -->
 446    latex(cmd(item(opt(Content)))).
 447latex(table(Attrs, Content)) -->
 448    latex_table(Attrs, Content).
 449latex(\Cmd, List, Tail) :-
 450    call(Cmd, List, Tail).
 451
 452% low level commands
 453latex(latex(Text)) -->
 454    [ latex(Text) ].
 455latex(cmd(Term)) -->
 456    { Term =.. [Cmd|Args] },
 457    indent(Cmd),
 458    [ cmd(Cmd) ],
 459    latex_arguments(Args),
 460    outdent(Cmd).
 461
 462indent(begin) --> !,           [ nl(2) ].
 463indent(end) --> !,             [ nl_exact(1) ].
 464indent(section) --> !,         [ nl(2) ].
 465indent(subsection) --> !,      [ nl(2) ].
 466indent(subsubsection) --> !,   [ nl(2) ].
 467indent(item) --> !,            [ nl(1), indent(4) ].
 468indent(definition) --> !,      [ nl(1), indent(4) ].
 469indent(tag) --> !,             [ nl(1), indent(4) ].
 470indent(termitem) --> !,        [ nl(1), indent(4) ].
 471indent(prefixtermitem) --> !,  [ nl(1), indent(4) ].
 472indent(infixtermitem) --> !,   [ nl(1), indent(4) ].
 473indent(postfixtermitem) --> !, [ nl(1), indent(4) ].
 474indent(predicate) --> !,       [ nl(1), indent(4) ].
 475indent(dcg) --> !,             [ nl(1), indent(4) ].
 476indent(infixop) --> !,         [ nl(1), indent(4) ].
 477indent(prefixop) --> !,        [ nl(1), indent(4) ].
 478indent(postfixop) --> !,       [ nl(1), indent(4) ].
 479indent(predicatesummary) --> !,[ nl(1) ].
 480indent(dcgsummary) --> !,      [ nl(1) ].
 481indent(oppredsummary) --> !,   [ nl(1) ].
 482indent(hline) --> !,           [ nl(1) ].
 483indent(_) -->                  [].
 484
 485outdent(begin) --> !,           [ nl_exact(1) ].
 486outdent(end) --> !,             [ nl(2) ].
 487outdent(item) --> !,            [ ' ' ].
 488outdent(tag) --> !,             [ nl(1) ].
 489outdent(termitem) --> !,        [ nl(1) ].
 490outdent(prefixtermitem) --> !,  [ nl(1) ].
 491outdent(infixtermitem) --> !,   [ nl(1) ].
 492outdent(postfixtermitem) --> !, [ nl(1) ].
 493outdent(definition) --> !,      [ nl(1) ].
 494outdent(section) --> !,         [ nl(2) ].
 495outdent(subsection) --> !,      [ nl(2) ].
 496outdent(subsubsection) --> !,   [ nl(2) ].
 497outdent(predicate) --> !,       [ nl(1) ].
 498outdent(dcg) --> !,             [ nl(1) ].
 499outdent(infixop) --> !,         [ nl(1) ].
 500outdent(prefixop) --> !,        [ nl(1) ].
 501outdent(postfixop) --> !,       [ nl(1) ].
 502outdent(predicatesummary) --> !,[ nl(1) ].
 503outdent(dcgsummary) --> !,      [ nl(1) ].
 504outdent(oppredsummary) --> !,   [ nl(1) ].
 505outdent(hline) --> !,           [ nl(1) ].
 506outdent(_) -->                  [].
 507
 508%!  latex_special(String, Rest)// is semidet.
 509%
 510%   Deals with special sequences of symbols.
 511
 512latex_special(In, Rest) -->
 513    { url_chars(In, Chars, Rest),
 514      special(Chars),
 515      atom_chars(Atom, Chars),
 516      urldef_name(Atom, Name)
 517    },
 518    !,
 519    latex([cmd(Name), latex('{}')]).
 520
 521special(Chars) :-
 522    memberchk(\, Chars),
 523    !.
 524special(Chars) :-
 525    length(Chars, Len),
 526    Len > 1.
 527
 528url_chars([H|T0], [H|T], Rest) :-
 529    urlchar(H),
 530    !,
 531    url_chars(T0, T, Rest).
 532url_chars(L, [], L).
 533
 534
 535%!  latex_arguments(+Args:list)// is det.
 536%
 537%   Write LaTeX command arguments. If  an   argument  is of the form
 538%   opt(Arg) it is written as  [Arg],   Otherwise  it  is written as
 539%   {Arg}. Note that opt([]) is omitted. I think no LaTeX command is
 540%   designed to handle an empty optional argument special.
 541%
 542%   During processing the arguments it asserts fragile/0 to allow is
 543%   taking care of LaTeX fragile   constructs  (i.e. constructs that
 544%   are not allows inside {...}).
 545
 546latex_arguments(List, Out, Tail) :-
 547    asserta(fragile, Ref),
 548    call_cleanup(fragile_list(List, Out, Tail),
 549                 erase(Ref)).
 550
 551fragile_list([]) --> [].
 552fragile_list([opt([])|T]) -->
 553    !,
 554    fragile_list(T).
 555fragile_list([opt(H)|T]) -->
 556    !,
 557    [ '[' ],
 558    latex_arg(H),
 559    [ ']' ],
 560    fragile_list(T).
 561fragile_list([H|T]) -->
 562    [ curl(open) ],
 563    latex_arg(H),
 564    [ curl(close) ],
 565    fragile_list(T).
 566
 567%!  latex_arg(+In)//
 568%
 569%   Write a LaTeX argument.  If  we  can,   we  will  use  a defined
 570%   urldef_name/2.
 571
 572latex_arg(H) -->
 573    { atomic(H),
 574      atom_string(Atom, H),
 575      urldef_name(Atom, Name)
 576    },
 577    !,
 578    latex(cmd(Name)).
 579latex_arg(H) -->
 580    { maplist(atom, H),
 581      atomic_list_concat(H, Atom),
 582      urldef_name(Atom, Name)
 583    },
 584    !,
 585    latex(cmd(Name)).
 586latex_arg(no_escape(Text)) -->
 587    !,
 588    [no_escape(Text)].
 589latex_arg(H) -->
 590    latex(H).
 591
 592attribute(Att, Attrs) :-
 593    is_list(Attrs),
 594    !,
 595    option(Att, Attrs).
 596attribute(Att, One) :-
 597    option(Att, [One]).
 598
 599if_short_list(Content, If, Else, Env) :-
 600    (   short_list(Content)
 601    ->  Env = If
 602    ;   Env = Else
 603    ).
 604
 605%!  short_list(+Content) is semidet.
 606%
 607%   True if Content describes the content of a dl or ul/ol list
 608%   where each elemenent has short content.
 609
 610short_list([]).
 611short_list([_,dd(Content)|T]) :-
 612    !,
 613    short_content(Content),
 614    short_list(T).
 615short_list([_,dd(_, Content)|T]) :-
 616    !,
 617    short_content(Content),
 618    short_list(T).
 619short_list([li(Content)|T]) :-
 620    short_content(Content),
 621    short_list(T).
 622
 623short_content(Content) :-
 624    phrase(latex(Content), Tokens),
 625    summed_string_len(Tokens, 0, Len),
 626    Len < 50.
 627
 628summed_string_len([], Len, Len).
 629summed_string_len([H|T], L0, L) :-
 630    atomic(H),
 631    !,
 632    atom_length(H, AL),
 633    L1 is L0 + AL,
 634    summed_string_len(T, L1, L).
 635summed_string_len([_|T], L0, L) :-
 636    summed_string_len(T, L0, L).
 637
 638
 639%!  latex_section(+Level, +Attributes, +Content)// is det.
 640%
 641%   Emit a LaTeX section,  keeping  track   of  the  desired highest
 642%   section level.
 643%
 644%   @param Level    Desired level, relative to the base-level.  Must
 645%                   be a non-negative integer.
 646
 647latex_section(Level, Attrs, Content) -->
 648    { current_options(Options),
 649      option(section_level(LaTexSection), Options, section),
 650      latex_section_level(LaTexSection, BaseLevel),
 651      FinalLevel is BaseLevel+Level,
 652      (   latex_section_level(SectionCommand, FinalLevel)
 653      ->  Term =.. [SectionCommand, Content]
 654      ;   domain_error(latex_section_level, FinalLevel)
 655      )
 656    },
 657    latex(cmd(Term)),
 658    section_label(Attrs).
 659
 660section_label(Attrs) -->
 661    { is_list(Attrs),
 662      memberchk(id(Name), Attrs),
 663      !,
 664      delete_unsafe_label_chars(Name, SafeName),
 665      atom_concat('sec:', SafeName, Label)
 666    },
 667    latex(cmd(label(Label))).
 668section_label(_) -->
 669    [].
 670
 671latex_section_level(chapter,       0).
 672latex_section_level(section,       1).
 673latex_section_level(subsection,    2).
 674latex_section_level(subsubsection, 3).
 675latex_section_level(paragraph,     4).
 676
 677deepen_section_level(Level0, Level1) :-
 678    latex_section_level(Level0, N),
 679    N1 is N + 1,
 680    latex_section_level(Level1, N1).
 681
 682%!  delete_unsafe_label_chars(+LabelIn, -LabelOut)
 683%
 684%   delete unsafe characters from LabelIn. Currently only deletes _,
 685%   as this appears  commonly  through   filenames,  but  cannot  be
 686%   handled through the LaTeX processing chain.
 687
 688delete_unsafe_label_chars(LabelIn, LabelOut) :-
 689    atom_chars(LabelIn, Chars),
 690    delete(Chars, '_', CharsOut),
 691    atom_chars(LabelOut, CharsOut).
 692
 693
 694                 /*******************************
 695                 *         \ COMMANDS           *
 696                 *******************************/
 697
 698%!  include(+File, +Type, +Options)// is det.
 699%
 700%   Called from [[File]].
 701
 702include(PI, predicate, _) -->
 703    !,
 704    (   {   options(Options)
 705        ->  true
 706        ;   Options = []
 707        },
 708        latex_tokens_for_predicates(PI, Options)
 709    ->  []
 710    ;   latex(cmd(item(['[[', \predref(PI), ']]'])))
 711    ).
 712include(File, Type, Options) -->
 713    { existing_linked_file(File, Path) },
 714    !,
 715    include_file(Path, Type, Options).
 716include(File, _, _) -->
 717    latex(code(['[[', File, ']]'])).
 718
 719include_file(Path, image, Options) -->
 720    { option(caption(Caption), Options) },
 721    !,
 722    latex(cmd(begin(figure, [no_escape(htbp)]))),
 723    latex(cmd(begin(center))),
 724    latex(cmd(includegraphics(Path))),
 725    latex(cmd(end(center))),
 726    latex(cmd(caption(Caption))),
 727    latex(cmd(end(figure))).
 728include_file(Path, image, _) -->
 729    !,
 730    latex(cmd(includegraphics(Path))).
 731include_file(Path, Type, _) -->
 732    { assertion(memberchk(Type, [prolog,wiki])),
 733      current_options(Options0),
 734      select_option(stand_alone(_), Options0, Options1, _),
 735      select_option(section_level(Level0), Options1, Options2, section),
 736      deepen_section_level(Level0, Level),
 737      Options = [stand_alone(false), section_level(Level)|Options2]
 738    },
 739    (   {Type == prolog}
 740    ->  latex_tokens_for_file(Path, Options)
 741    ;   latex_tokens_for_wiki_file(Path, Options)
 742    ).
 743
 744%!  file(+File, +Options)// is det.
 745%
 746%   Called from implicitely linked files.  The HTML version creates
 747%   a hyperlink.  We just name the file.
 748
 749file(File, _Options) -->
 750    { fragile },
 751    !,
 752    latex(cmd(texttt(File))).
 753file(File, _Options) -->
 754    latex(cmd(file(File))).
 755
 756%!  predref(+PI)// is det.
 757%
 758%   Called  from  name/arity  or   name//arity    patterns   in  the
 759%   documentation.
 760
 761predref(Module:Name/Arity) -->
 762    !,
 763    latex(cmd(qpredref(Module, Name, Arity))).
 764predref(Module:Name//Arity) -->
 765    latex(cmd(qdcgref(Module, Name, Arity))).
 766predref(Name/Arity) -->
 767    latex(cmd(predref(Name, Arity))).
 768predref(Name//Arity) -->
 769    latex(cmd(dcgref(Name, Arity))).
 770
 771%!  tags(+Tags:list(Tag)) is det.
 772%
 773%   Emit tag list produced by the   Wiki processor from the @keyword
 774%   commands.
 775
 776tags([\args(Params)|Rest]) -->
 777    !,
 778    args(Params),
 779    tags_list(Rest).
 780tags(List) -->
 781    tags_list(List).
 782
 783tags_list([]) -->
 784    [].
 785tags_list(List) -->
 786    [ nl(2) ],
 787    latex(cmd(begin(tags))),
 788    latex(List),
 789    latex(cmd(end(tags))),
 790    [ nl(2) ].
 791
 792%!  tag(+Tag, +Values:list)// is det.
 793%
 794%   Called from \tag(Name, Values) terms produced by doc_wiki.pl.
 795
 796tag(Tag, [One]) -->
 797    !,
 798    { doc_tag_title(Tag, Title) },
 799    latex([ cmd(tag(Title))
 800          | One
 801          ]).
 802tag(Tag, More) -->
 803    { doc_tag_title(Tag, Title) },
 804    latex([ cmd(mtag(Title)),
 805            \tag_value_list(More)
 806          ]).
 807
 808tag_value_list([H|T]) -->
 809    latex(['- '|H]),
 810    (   { T \== [] }
 811    ->  [latex(' \\\\')],
 812        tag_value_list(T)
 813    ;   []
 814    ).
 815
 816%!  args(+Params:list) is det.
 817%
 818%   Called from \args(List) created by   doc_wiki.pl.  Params is a
 819%   list of arg(Name, Descr).
 820
 821args(Params) -->
 822    latex([ cmd(begin(arguments)),
 823            \arg_list(Params),
 824            cmd(end(arguments))
 825          ]).
 826
 827arg_list([]) -->
 828    [].
 829arg_list([H|T]) -->
 830    argument(H),
 831    arg_list(T).
 832
 833argument(arg(Name,Descr)) -->
 834    [ nl(1) ],
 835    latex(cmd(arg(Name))), [ latex(' & ') ],
 836    latex(Descr), [latex(' \\\\')].
 837
 838%!  file_header(+File, +Options)// is det.
 839%
 840%   Create the file header.
 841
 842file_header(File, Options) -->
 843    { memberchk(file(Title, Comment), Options),
 844      !,
 845      file_synopsis(File, Synopsis)
 846    },
 847    file_title([Synopsis, ': ', Title], File, Options),
 848    { is_structured_comment(Comment, Prefixes),
 849      string_codes(Comment, Codes),
 850      indented_lines(Codes, Prefixes, Lines),
 851      section_comment_header(Lines, _Header, Lines1),
 852      wiki_lines_to_dom(Lines1, [], DOM0),
 853      tags_to_front(DOM0, DOM)
 854    },
 855    latex(DOM),
 856    latex(cmd(vspace('0.7cm'))).
 857file_header(File, Options) -->
 858    { file_synopsis(File, Synopsis)
 859    },
 860    file_title([Synopsis], File, Options).
 861
 862tags_to_front(DOM0, DOM) :-
 863    append(Content, [\tags(Tags)], DOM0),
 864    !,
 865    DOM = [\tags(Tags)|Content].
 866tags_to_front(DOM, DOM).
 867
 868file_synopsis(File, Synopsis) :-
 869    file_name_on_path(File, Term),
 870    unquote_filespec(Term, Unquoted),
 871    format(atom(Synopsis), '~w', [Unquoted]).
 872
 873
 874%!  file_title(+Title:list, +File, +Options)// is det
 875%
 876%   Emit the file-header and manipulation buttons.
 877
 878file_title(Title, File, Options) -->
 879    { option(section_level(Level), Options, section),
 880      Section =.. [Level,Title],
 881      file_base_name(File, BaseExt),
 882      file_name_extension(Base, _, BaseExt),
 883      delete_unsafe_label_chars(Base, SafeBase),
 884      atom_concat('sec:', SafeBase, Label)
 885    },
 886    latex(cmd(Section)),
 887    latex(cmd(label(Label))).
 888
 889
 890%!  objects(+Objects:list, +Options)// is det.
 891%
 892%   Emit the documentation body.
 893
 894objects(Objects, Options) -->
 895    objects(Objects, [body], Options).
 896
 897objects([], Mode, _) -->
 898    pop_mode(body, Mode, _).
 899objects([Obj|T], Mode, Options) -->
 900    object(Obj, Mode, Mode1, Options),
 901    objects(T, Mode1, Options).
 902
 903object(doc(Obj,Pos,Comment), Mode0, Mode, Options) -->
 904    !,
 905    object(Obj, Pos, Comment, Mode0, Mode, Options).
 906object(Obj, Mode0, Mode, Options) -->
 907    { doc_comment(Obj, Pos, _Summary, Comment)
 908    },
 909    !,
 910    object(Obj, Pos, Comment, Mode0, Mode, Options).
 911
 912object(Obj, Pos, Comment, Mode0, Mode, Options) -->
 913    { is_pi(Obj),
 914      !,
 915      is_structured_comment(Comment, Prefixes),
 916      string_codes(Comment, Codes),
 917      indented_lines(Codes, Prefixes, Lines),
 918      strip_module(user:Obj, Module, _),
 919      process_modes(Lines, Module, Pos, Modes, Args, Lines1),
 920      (   private(Obj, Options)
 921      ->  Class = privdef           % private definition
 922      ;   multifile(Obj, Options)
 923      ->  Class = multidef
 924      ;   Class = pubdef            % public definition
 925      ),
 926      (   Obj = Module:_
 927      ->  POptions = [module(Module)|Options]
 928      ;   POptions = Options
 929      ),
 930      DOM = [\pred_dt(Modes, Class, POptions), dd(class=defbody, DOM1)],
 931      wiki_lines_to_dom(Lines1, Args, DOM0),
 932      strip_leading_par(DOM0, DOM1),
 933      assert_documented(Obj)
 934    },
 935    need_mode(description, Mode0, Mode),
 936    latex(DOM).
 937object([Obj|Same], Pos, Comment, Mode0, Mode, Options) -->
 938    !,
 939    object(Obj, Pos, Comment, Mode0, Mode, Options),
 940    { maplist(assert_documented, Same) }.
 941object(Obj, _Pos, _Comment, Mode, Mode, _Options) -->
 942    { debug(pldoc, 'Skipped ~p', [Obj]) },
 943    [].
 944
 945assert_documented(Obj) :-
 946    assert(documented(Obj)).
 947
 948
 949%!  need_mode(+Mode:atom, +Stack:list, -NewStack:list)// is det.
 950%
 951%   While predicates are part of a   description  list, sections are
 952%   not and we therefore  need  to   insert  <dl>...</dl>  into  the
 953%   output. We do so by demanding  an outer environment and push/pop
 954%   the required elements.
 955
 956need_mode(Mode, Stack, Stack) -->
 957    { Stack = [Mode|_] },
 958    !,
 959    [].
 960need_mode(Mode, Stack, Rest) -->
 961    { memberchk(Mode, Stack)
 962    },
 963    !,
 964    pop_mode(Mode, Stack, Rest).
 965need_mode(Mode, Stack, [Mode|Stack]) -->
 966    !,
 967    latex(cmd(begin(Mode))).
 968
 969pop_mode(Mode, Stack, Stack) -->
 970    { Stack = [Mode|_] },
 971    !,
 972    [].
 973pop_mode(Mode, [H|Rest0], Rest) -->
 974    latex(cmd(end(H))),
 975    pop_mode(Mode, Rest0, Rest).
 976
 977
 978%!  pred_dt(+Modes, +Class, Options)// is det.
 979%
 980%   Emit the \predicate{}{}{} header.
 981%
 982%   @param Modes    List as returned by process_modes/5.
 983%   @param Class    One of =privdef= or =pubdef=.
 984%
 985%   @tbd    Determinism
 986
 987pred_dt(Modes, Class, Options) -->
 988    [nl(2)],
 989    pred_dt(Modes, [], _Done, [class(Class)|Options]).
 990
 991pred_dt([], Done, Done, _) -->
 992    [].
 993pred_dt([H|T], Done0, Done, Options) -->
 994    pred_mode(H, Done0, Done1, Options),
 995    (   {T == []}
 996    ->  []
 997    ;   latex(cmd(nodescription)),
 998        pred_dt(T, Done1, Done, Options)
 999    ).
1000
1001pred_mode(mode(Head,Vars), Done0, Done, Options) -->
1002    !,
1003    { bind_vars(Head, Vars) },
1004    pred_mode(Head, Done0, Done, Options).
1005pred_mode(Head is Det, Done0, Done, Options) -->
1006    !,
1007    anchored_pred_head(Head, Done0, Done, [det(Det)|Options]).
1008pred_mode(Head, Done0, Done, Options) -->
1009    anchored_pred_head(Head, Done0, Done, Options).
1010
1011bind_vars(Term, Bindings) :-
1012    bind_vars(Bindings),
1013    anon_vars(Term).
1014
1015bind_vars([]).
1016bind_vars([Name=Var|T]) :-
1017    Var = '$VAR'(Name),
1018    bind_vars(T).
1019
1020%!  anon_vars(+Term) is det.
1021%
1022%   Bind remaining variables in Term to '$VAR'('_'), so they are
1023%   printed as '_'.
1024
1025anon_vars(Var) :-
1026    var(Var),
1027    !,
1028    Var = '$VAR'('_').
1029anon_vars(Term) :-
1030    compound(Term),
1031    !,
1032    Term =.. [_|Args],
1033    maplist(anon_vars, Args).
1034anon_vars(_).
1035
1036
1037anchored_pred_head(Head, Done0, Done, Options) -->
1038    { pred_anchor_name(Head, PI, _Name) },
1039    (   { memberchk(PI, Done0) }
1040    ->  { Done = Done0 }
1041    ;   { Done = [PI|Done0] }
1042    ),
1043    pred_head(Head, Options).
1044
1045
1046%!  pred_head(+Term, Options) is det.
1047%
1048%   Emit a predicate head. The functor is  typeset as a =span= using
1049%   class =pred= and the arguments and =var= using class =arglist=.
1050%
1051%   @tbd Support determinism in operators
1052
1053pred_head(//(Head), Options) -->
1054    !,
1055    { pred_attributes(Options, Atts),
1056      Head =.. [Functor|Args],
1057      length(Args, Arity)
1058    },
1059    latex(cmd(dcg(opt(Atts), Functor, Arity, \pred_args(Args, 1)))).
1060pred_head(Head, _Options) -->                   % Infix operators
1061    { Head =.. [Functor,Left,Right],
1062      Functor \== (:),
1063      is_op_type(Functor, infix), !
1064    },
1065    latex(cmd(infixop(Functor, \pred_arg(Left, 1), \pred_arg(Right, 2)))).
1066pred_head(Head, _Options) -->                   % Prefix operators
1067    { Head =.. [Functor,Arg],
1068      is_op_type(Functor, prefix), !
1069    },
1070    latex(cmd(prefixop(Functor, \pred_arg(Arg, 1)))).
1071pred_head(Head, _Options) -->                   % Postfix operators
1072    { Head =.. [Functor,Arg],
1073      is_op_type(Functor, postfix), !
1074    },
1075    latex(cmd(postfixop(Functor, \pred_arg(Arg, 1)))).
1076pred_head(M:Head, Options) -->                 % Qualified predicates
1077    !,
1078    { pred_attributes(Options, Atts),
1079      Head =.. [Functor|Args],
1080      length(Args, Arity)
1081    },
1082    latex(cmd(qpredicate(opt(Atts),
1083                         M,
1084                         Functor, Arity, \pred_args(Args, 1)))).
1085pred_head(Head, Options) -->                    % Plain terms
1086    { pred_attributes(Options, Atts),
1087      Head =.. [Functor|Args],
1088      length(Args, Arity)
1089    },
1090    latex(cmd(predicate(opt(Atts),
1091                        Functor, Arity, \pred_args(Args, 1)))).
1092
1093%!  pred_attributes(+Options, -Attributes) is det.
1094%
1095%   Create a comma-separated list of   predicate attributes, such as
1096%   determinism, etc.
1097
1098pred_attributes(Options, Attrs) :-
1099    findall(A, pred_att(Options, A), As),
1100    insert_comma(As, Attrs).
1101
1102pred_att(Options, Det) :-
1103    option(det(Det), Options).
1104pred_att(Options, private) :-
1105    option(class(privdef), Options).
1106pred_att(Options, multifile) :-
1107    option(class(multidef), Options).
1108
1109insert_comma([H1,H2|T0], [H1, ','|T]) :-
1110    !,
1111    insert_comma([H2|T0], T).
1112insert_comma(L, L).
1113
1114
1115:- if(current_predicate(is_dict/1)).
1116dict_kv_pairs([]) --> [].
1117dict_kv_pairs([H|T]) -->
1118    dict_kv(H),
1119    (   { T == [] }
1120    ->  []
1121    ;   latex(', '),
1122        dict_kv_pairs(T)
1123    ).
1124
1125dict_kv(Key-Value) -->
1126    latex(cmd(key(Key))),
1127    latex(':'),
1128    term(Value).
1129:- endif.
1130
1131pred_args([], _) -->
1132    [].
1133pred_args([H|T], I) -->
1134    pred_arg(H, I),
1135    (   {T==[]}
1136    ->  []
1137    ;   latex(', '),
1138        { I2 is I + 1 },
1139        pred_args(T, I2)
1140    ).
1141
1142pred_arg(Var, I) -->
1143    { var(Var) },
1144    !,
1145    latex(['Arg', I]).
1146pred_arg(...(Term), I) -->
1147    !,
1148    pred_arg(Term, I),
1149    latex(cmd(ldots)).
1150pred_arg(Term, I) -->
1151    { Term =.. [Ind,Arg],
1152      mode_indicator(Ind)
1153    },
1154    !,
1155    latex([Ind, \pred_arg(Arg, I)]).
1156pred_arg(Arg:Type, _) -->
1157    !,
1158    latex([\argname(Arg), :, \argtype(Type)]).
1159pred_arg(Arg, _) -->
1160    { atom(Arg) },
1161    !,
1162    argname(Arg).
1163pred_arg(Arg, _) -->
1164    argtype(Arg).                   % arbitrary term
1165
1166argname('$VAR'(Name)) -->
1167    !,
1168    latex(Name).
1169argname(Name) -->
1170    !,
1171    latex(Name).
1172
1173argtype(Term) -->
1174    { format(string(S), '~W',
1175             [ Term,
1176               [ quoted(true),
1177                 numbervars(true)
1178               ]
1179             ]) },
1180    latex(S).
1181
1182%!  term(+Text, +Term, +Bindings)// is det.
1183%
1184%   Process the \term element as produced by doc_wiki.pl.
1185%
1186%   @tbd    Properly merge with pred_head//1
1187
1188term(_, Term, Bindings) -->
1189    { bind_vars(Bindings) },
1190    term(Term).
1191
1192term('$VAR'(Name)) -->
1193    !,
1194    latex(cmd(arg(Name))).
1195term(Compound) -->
1196    { callable(Compound),
1197      !,
1198      Compound =.. [Functor|Args]
1199    },
1200    !,
1201    term_with_args(Functor, Args).
1202term(Rest) -->
1203    latex(Rest).
1204
1205term_with_args(Functor, [Left, Right]) -->
1206    { is_op_type(Functor, infix) },
1207    !,
1208    latex(cmd(infixterm(Functor, \term(Left), \term(Right)))).
1209term_with_args(Functor, [Arg]) -->
1210    { is_op_type(Functor, prefix) },
1211    !,
1212    latex(cmd(prefixterm(Functor, \term(Arg)))).
1213term_with_args(Functor, [Arg]) -->
1214    { is_op_type(Functor, postfix) },
1215    !,
1216    latex(cmd(postfixterm(Functor, \term(Arg)))).
1217term_with_args(Functor, Args) -->
1218    latex(cmd(term(Functor, \pred_args(Args, 1)))).
1219
1220
1221%!  termitem(+Text, +Term, +Bindings)// is det.
1222%
1223%   Create a termitem or one of its variations.
1224
1225termitem(_Text, Term, Bindings) -->
1226    { bind_vars(Bindings) },
1227    termitem(Term).
1228
1229termitem('$VAR'(Name)) -->
1230    !,
1231    latex(cmd(termitem(var(Name), ''))).
1232:- if(current_predicate(is_dict/1)).
1233termitem(Dict) -->
1234    { is_dict(Dict),
1235      !,
1236      dict_pairs(Dict, Tag, Pairs)
1237    },
1238    latex(cmd(dictitem(Tag, \dict_kv_pairs(Pairs)))).
1239:- endif.
1240termitem(Compound) -->
1241    { callable(Compound),
1242      !,
1243      Compound =.. [Functor|Args]
1244    },
1245    !,
1246    termitem_with_args(Functor, Args).
1247termitem(Rest) -->
1248    latex(cmd(termitem(Rest, ''))).
1249
1250termitem_with_args(Functor, [Left, Right]) -->
1251    { is_op_type(Functor, infix) },
1252    !,
1253    latex(cmd(infixtermitem(Functor, \term(Left), \term(Right)))).
1254termitem_with_args(Functor, [Arg]) -->
1255    { is_op_type(Functor, prefix) },
1256    !,
1257    latex(cmd(prefixtermitem(Functor, \term(Arg)))).
1258termitem_with_args(Functor, [Arg]) -->
1259    { is_op_type(Functor, postfix) },
1260    !,
1261    latex(cmd(postfixtermitem(Functor, \term(Arg)))).
1262termitem_with_args(Functor, Args) -->
1263    latex(cmd(termitem(Functor, \pred_args(Args, 1)))).
1264
1265
1266%!  latex_table(Attrs, Content)// is det.
1267%
1268%   Emit a table in LaTeX.
1269
1270latex_table(_Attrs, Content) -->
1271    { max_columns(Content, 0, N),
1272      make_frame(N, l, List),
1273      atom_chars(Format, ['|'|List])
1274    },
1275%       latex(cmd(begin(table, opt(h)))),
1276    latex(cmd(begin(quote))),
1277    latex(cmd(begin(tabular, no_escape(Format)))),
1278    latex(cmd(hline)),
1279    rows(Content),
1280    latex(cmd(hline)),
1281    latex(cmd(end(tabular))),
1282    latex(cmd(end(quote))).
1283%       latex(cmd(end(table))).
1284
1285max_columns([], C, C).
1286max_columns([tr(List)|T], C0, C) :-
1287    length(List, C1),
1288    C2 is max(C0, C1),
1289    max_columns(T, C2, C).
1290
1291make_frame(0, _, []) :- !.
1292make_frame(N, C, [C,'|'|T]) :-
1293    N2 is N - 1,
1294    make_frame(N2, C, T).
1295
1296rows([]) -->
1297    [].
1298rows([tr(Content)|T]) -->
1299    row(Content),
1300    rows(T).
1301
1302row([]) -->
1303    [ latex(' \\\\'), nl(1) ].
1304row([td(Content)|T]) -->
1305    latex(Content),
1306    (   {T == []}
1307    ->  []
1308    ;   [ latex(' & ') ]
1309    ),
1310    row(T).
1311
1312
1313                 /*******************************
1314                 *      SUMMARY PROCESSING      *
1315                 *******************************/
1316
1317%!  latex_summary(+Options)
1318%
1319%   If Options contains  summary(+File),  write   a  summary  of all
1320%   documented predicates to File.
1321
1322latex_summary(Options) :-
1323    option(summary(File), Options),
1324    !,
1325    findall(Obj, summary_obj(Obj), Objs),
1326    maplist(pi_sort_key, Objs, Keyed),
1327    keysort(Keyed, KSorted),
1328    pairs_values(KSorted, SortedObj),
1329    phrase(summarylist(SortedObj, Options), Tokens),
1330    open(File, write, Out),
1331    call_cleanup(print_latex(Out, Tokens, Options),
1332                 close(Out)).
1333latex_summary(_) :-
1334    retractall(documented(_)).
1335
1336summary_obj(Obj) :-
1337    documented(Obj),
1338    pi_head(Obj, Head),
1339    \+ xref_hook(Head).
1340
1341pi_head(M:PI, M:Head) :-
1342    !,
1343    pi_head(PI, Head).
1344pi_head(Name/Arity, Head) :-
1345    functor(Head, Name, Arity).
1346pi_head(Name//DCGArity, Head) :-
1347    Arity is DCGArity+2,
1348    functor(Head, Name, Arity).
1349
1350
1351pi_sort_key(M:PI, PI-(M:PI)) :- !.
1352pi_sort_key(PI, PI-PI).
1353
1354object_name_arity(_:Term, Type, Name, Arity) :-
1355    nonvar(Term),
1356    !,
1357    object_name_arity(Term, Type, Name, Arity).
1358object_name_arity(Name/Arity, pred, Name, Arity).
1359object_name_arity(Name//Arity, dcg, Name, Arity).
1360
1361summarylist(Objs, Options) -->
1362    latex(cmd(begin(summarylist, ll))),
1363    summary(Objs, Options),
1364    latex(cmd(end(summarylist))).
1365
1366summary([], _) -->
1367    [].
1368summary([H|T], Options) -->
1369    summary_line(H, Options),
1370    summary(T, Options).
1371
1372summary_line(Obj, _Options) -->
1373    { doc_comment(Obj, _Pos, Summary, _Comment) ->
1374      atom_codes(Summary, Codes),
1375      phrase(pldoc_wiki:line_tokens(Tokens), Codes), % TBD: proper export
1376      object_name_arity(Obj, Type, Name, Arity)
1377    },
1378    (   {Type == dcg}
1379    ->  latex(cmd(dcgsummary(Name, Arity, Tokens)))
1380    ;   { strip_module(Obj, M, _),
1381          current_op(Pri, Ass, M:Name)
1382        }
1383    ->  latex(cmd(oppredsummary(Name, Arity, Ass, Pri, Tokens)))
1384    ;   latex(cmd(predicatesummary(Name, Arity, Tokens)))
1385    ).
1386
1387
1388                 /*******************************
1389                 *          PRINT TOKENS        *
1390                 *******************************/
1391
1392print_latex(Out, Tokens, Options) :-
1393    latex_header(Out, Options),
1394    print_latex_tokens(Tokens, Out),
1395    latex_footer(Out, Options).
1396
1397
1398%!  print_latex_tokens(+Tokens, +Out)
1399%
1400%   Print primitive LaTeX tokens to Output
1401
1402print_latex_tokens([], _).
1403print_latex_tokens([nl(N)|T0], Out) :-
1404    !,
1405    max_nl(T0, T, N, NL),
1406    nl(Out, NL),
1407    print_latex_tokens(T, Out).
1408print_latex_tokens([nl_exact(N)|T0], Out) :-
1409    !,
1410    nl_exact(T0, T,N, NL),
1411    nl(Out, NL),
1412    print_latex_tokens(T, Out).
1413print_latex_tokens([H|T], Out) :-
1414    print_latex_token(H, Out),
1415    print_latex_tokens(T, Out).
1416
1417print_latex_token(cmd(Cmd), Out) :-
1418    !,
1419    format(Out, '\\~w', [Cmd]).
1420print_latex_token(curl(open), Out) :-
1421    !,
1422    format(Out, '{', []).
1423print_latex_token(curl(close), Out) :-
1424    !,
1425    format(Out, '}', []).
1426print_latex_token(indent(N), Out) :-
1427    !,
1428    format(Out, '~t~*|', [N]).
1429print_latex_token(nl(N), Out) :-
1430    !,
1431    format(Out, '~N', []),
1432    forall(between(2,N,_), nl(Out)).
1433print_latex_token(verb(Verb), Out) :-
1434    is_list(Verb), Verb \== [],
1435    !,
1436    atomic_list_concat(Verb, Atom),
1437    print_latex_token(verb(Atom), Out).
1438print_latex_token(verb(Verb), Out) :-
1439    !,
1440    (   member(C, [$,'|',@,=,'"',^,!]),
1441        \+ sub_atom(Verb, _, _, _, C)
1442    ->  atom_replace_char(Verb, '\n', ' ', Verb2),
1443        format(Out, '\\verb~w~w~w', [C,Verb2,C])
1444    ;   assertion(fail)
1445    ).
1446print_latex_token(code(Code), Out) :-
1447    !,
1448    format(Out, '~N\\begin{code}~n', []),
1449    format(Out, '~w', [Code]),
1450    format(Out, '~N\\end{code}', []).
1451print_latex_token(latex(Code), Out) :-
1452    !,
1453    write(Out, Code).
1454print_latex_token(w(Word), Out) :-
1455    !,
1456    print_latex(Out, Word).
1457print_latex_token(no_escape(Text), Out) :-
1458    !,
1459    write(Out, Text).
1460print_latex_token(Rest, Out) :-
1461    (   atomic(Rest)
1462    ->  print_latex(Out, Rest)
1463    ;   %type_error(latex_token, Rest)
1464        write(Out, Rest)
1465    ).
1466
1467atom_replace_char(In, From, To, Out) :-
1468    sub_atom(In, _, _, _, From),
1469    !,
1470    atom_chars(In, CharsIn),
1471    replace(CharsIn, From, To, CharsOut),
1472    atom_chars(Out, CharsOut).
1473atom_replace_char(In, _, _, In).
1474
1475replace([], _, _, []).
1476replace([H|T0], H, N, [N|T]) :-
1477    !,
1478    replace(T0, H, N, T).
1479replace([H|T0], F, N, [H|T]) :-
1480    replace(T0, F, N, T).
1481
1482
1483%!  print_latex(+Out, +Text:atomic) is det.
1484%
1485%   Print Text, such that it comes out as normal LaTeX text.
1486
1487print_latex(Out, String) :-
1488    atom_string(Atom, String),
1489    atom_chars(Atom, Chars),
1490    print_chars(Chars, Out).
1491
1492print_chars([], _).
1493print_chars([H|T], Out) :-
1494    print_char(H, Out),
1495    print_chars(T, Out).
1496
1497
1498%!  max_nl(T0, T, M0, M)
1499%
1500%   Remove leading sequence of nl(N) and return the maximum of it.
1501
1502max_nl([nl(M1)|T0], T, M0, M) :-
1503    !,
1504    M2 is max(M1, M0),
1505    max_nl(T0, T, M2, M).
1506max_nl([nl_exact(M1)|T0], T, _, M) :-
1507    !,
1508    nl_exact(T0, T, M1, M).
1509max_nl(T, T, M, M).
1510
1511nl_exact([nl(_)|T0], T, M0, M) :-
1512    !,
1513    max_nl(T0, T, M0, M).
1514nl_exact([nl_exact(M1)|T0], T, M0, M) :-
1515    !,
1516    M2 is max(M1, M0),
1517    max_nl(T0, T, M2, M).
1518nl_exact(T, T, M, M).
1519
1520
1521nl(Out, N) :-
1522    forall(between(1, N, _), nl(Out)).
1523
1524
1525print_char('<', Out) :- !, write(Out, '$<$').
1526print_char('>', Out) :- !, write(Out, '$>$').
1527print_char('{', Out) :- !, write(Out, '\\{').
1528print_char('}', Out) :- !, write(Out, '\\}').
1529print_char('$', Out) :- !, write(Out, '\\$').
1530print_char('&', Out) :- !, write(Out, '\\&').
1531print_char('#', Out) :- !, write(Out, '\\#').
1532print_char('%', Out) :- !, write(Out, '\\%').
1533print_char('~', Out) :- !, write(Out, '\\Stilde{}').
1534print_char('\\',Out) :- !, write(Out, '\\bsl{}').
1535print_char('^', Out) :- !, write(Out, '\\Shat{}').
1536print_char('|', Out) :- !, write(Out, '\\Sbar{}').
1537print_char(C,   Out) :- put_char(Out, C).
1538
1539
1540%!  identifier(+Atom) is semidet.
1541%
1542%   True if Atom is (lower, alnum*).
1543
1544identifier(Atom) :-
1545    atom_chars(Atom, [C0|Chars]),
1546    char_type(C0, lower),
1547    all_chartype(Chars, alnum).
1548
1549all_chartype([], _).
1550all_chartype([H|T], Type) :-
1551    char_type(H, Type),
1552    all_chartype(T, Type).
1553
1554
1555                 /*******************************
1556                 *    LATEX SPECIAL SEQUENCES   *
1557                 *******************************/
1558
1559%!  urldef_name(?String, ?DefName)
1560%
1561%   True if \DefName is  a  urldef   for  String.  UrlDefs are LaTeX
1562%   sequences that can be used to  represent strings with symbols in
1563%   fragile environments. Whenever a word can   be  expressed with a
1564%   urldef, we will  do  this  to   enhance  the  robustness  of the
1565%   generated LaTeX code.
1566
1567:- dynamic
1568    urldef_name/2,
1569    urlchar/1,                      % true if C appears in ine of them
1570    urldefs_loaded/1.
1571
1572%!  load_urldefs.
1573%!  load_urldefs(+File)
1574%
1575%   Load   =|\urldef|=   definitions   from    File   and   populate
1576%   urldef_name/2. See =|pldoc.sty|= for details.
1577
1578load_urldefs :-
1579    urldefs_loaded(_),
1580    !.
1581load_urldefs :-
1582    absolute_file_name(library('pldoc/pldoc.sty'), File,
1583                       [ access(read) ]),
1584    load_urldefs(File).
1585
1586load_urldefs(File) :-
1587    urldefs_loaded(File),
1588    !.
1589load_urldefs(File) :-
1590    open(File, read, In),
1591    call_cleanup((   read_line_to_codes(In, L0),
1592                     process_urldefs(L0, In)),
1593                 close(In)),
1594    assert(urldefs_loaded(File)).
1595
1596process_urldefs(end_of_file, _) :- !.
1597process_urldefs(Line, In) :-
1598    (   phrase(urldef(Name, String), Line)
1599    ->  assert(urldef_name(String, Name)),
1600        assert_chars(String)
1601    ;   true
1602    ),
1603    read_line_to_codes(In, L2),
1604    process_urldefs(L2, In).
1605
1606assert_chars(String) :-
1607    atom_chars(String, Chars),
1608    (   member(C, Chars),
1609        \+ urlchar(C),
1610        assert(urlchar(C)),
1611        fail
1612    ;   true
1613    ).
1614
1615urldef(Name, String) -->
1616    "\\urldef{\\", string(NameS), "}\\satom{", string(StringS), "}",
1617    ws,
1618    (   "%"
1619    ->  string(_)
1620    ;   []
1621    ),
1622    eol,
1623    !,
1624    { atom_codes(Name, NameS),
1625      atom_codes(String, StringS)
1626    }.
1627
1628ws --> [C], { C =< 32 }, !, ws.
1629ws --> [].
1630
1631string([]) --> [].
1632string([H|T]) --> [H], string(T).
1633
1634eol([],[]).
1635
1636
1637                 /*******************************
1638                 *         HEADER/FOOTER        *
1639                 *******************************/
1640
1641latex_header(Out, Options) :-
1642    (   option(stand_alone(true), Options, true)
1643    ->  forall(header(Line), format(Out, '~w~n', [Line]))
1644    ;   true
1645    ),
1646    forall(generated(Line), format(Out, '~w~n', [Line])).
1647
1648latex_footer(Out, Options) :-
1649    (   option(stand_alone(true), Options, true)
1650    ->  forall(footer(Line), format(Out, '~w~n', [Line]))
1651    ;   true
1652    ).
1653
1654header('\\documentclass[11pt]{article}').
1655header('\\usepackage{times}').
1656header('\\usepackage{pldoc}').
1657header('\\sloppy').
1658header('\\makeindex').
1659header('').
1660header('\\begin{document}').
1661
1662footer('').
1663footer('\\printindex').
1664footer('\\end{document}').
1665
1666generated('% This LaTeX document was generated using the LaTeX backend of PlDoc,').
1667generated('% The SWI-Prolog documentation system').
1668generated('').