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)  2014-2020, University of Amsterdam
    7                              VU University Amsterdam
    8                              CWI, Amsterdam
    9    All rights reserved.
   10
   11    Redistribution and use in source and binary forms, with or without
   12    modification, are permitted provided that the following conditions
   13    are met:
   14
   15    1. Redistributions of source code must retain the above copyright
   16       notice, this list of conditions and the following disclaimer.
   17
   18    2. Redistributions in binary form must reproduce the above copyright
   19       notice, this list of conditions and the following disclaimer in
   20       the documentation and/or other materials provided with the
   21       distribution.
   22
   23    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   24    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   25    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   26    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   27    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   28    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   29    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   30    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   31    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   33    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   34    POSSIBILITY OF SUCH DAMAGE.
   35*/
   36
   37:- module(prolog_pretty_print,
   38          [ print_term/2        % +Term, +Options
   39          ]).   40:- autoload(library(option),
   41            [merge_options/3, select_option/3, select_option/4,
   42             option/2, option/3]).

Pretty Print Prolog terms

This module is a first start of what should become a full-featured pretty printer for Prolog terms with many options and parameters. Eventually, it should replace portray_clause/1 and various other special-purpose predicates.

To be done
- This is just a quicky. We need proper handling of portray/1, avoid printing very long terms multiple times, spacing (around operators), etc.
- Use a record for the option-processing.
- The current approach is far too simple, often resulting in illegal terms. */
   61:- predicate_options(print_term/2, 2,
   62                     [ output(stream),
   63                       right_margin(integer),
   64                       left_margin(integer),
   65                       tab_width(integer),
   66                       indent_arguments(integer),
   67                       operators(boolean),
   68                       write_options(list)
   69                     ]).
 print_term(+Term, +Options) is det
Pretty print a Prolog term. The following options are processed:
output(+Stream)
Define the output stream. Default is user_output
right_margin(?Column)
Width of a line. If the output is a tty and tty_size/2 can produce a size the default is the number of columns minus 8. Otherwise the default is 72 characters. If the Column is unbound it is unified with the computed value.
left_margin(+Integer)
Left margin for continuation lines. Default is 0.
tab_width(+Integer)
Distance between tab-stops. Default is 8 characters.
indent_arguments(+Spec)
Defines how arguments of compound terms are placed. Defined values are:
false
Simply place them left to right (no line-breaks)
true
Place them vertically, aligned with the open bracket (not implemented)
auto (default)
As horizontal if line-width is not exceeded, vertical otherwise.
An integer
Place them vertically aligned, <N> spaces to the right of the beginning of the head.
operators(+Boolean)
This is the inverse of the write_term/3 option ignore_ops. Default is to respect them.
write_options(+List)
List of options passed to write_term/3 for terms that are not further processed. Default:
    [ numbervars(true),
      quoted(true),
      portray(true)
    ]
fullstop(Boolean)
If true (default false), add a full stop (.) to the output.
nl(Boolean)
If true (default false), add a newline to the output.
  118print_term(Term, Options) :-
  119    defaults(Defs0),
  120    select_option(write_options(WrtDefs), Defs0, Defs),
  121    select_option(write_options(WrtUser), Options, Options1, []),
  122    merge_options(WrtUser, WrtDefs, WrtOpts),
  123    merge_options(Options1, Defs, Options2),
  124    Options3 = [write_options(WrtOpts)|Options2],
  125    default_margin(Options3, Options4),
  126    \+ \+ print_term_2(Term, Options4).
  127
  128print_term_2(Term, Options) :-
  129    prepare_term(Term, Template, Cycles, Constraints),
  130    option(write_options(WrtOpts), Options),
  131    option(max_depth(MaxDepth), WrtOpts, infinite),
  132
  133    dict_create(Context, #, [max_depth(MaxDepth)|Options]),
  134    pp(Template, Context, Options),
  135    print_extra(Cycles, Context, 'where', Options),
  136    print_extra(Constraints, Context, 'with constraints', Options),
  137    (   option(fullstop(true), Options)
  138    ->  option(output(Out), Options),
  139        put_char(Out, '.')
  140    ;   true
  141    ),
  142    (   option(nl(true), Options)
  143    ->  option(output(Out2), Options),
  144        nl(Out2)
  145    ;   true
  146    ).
  147
  148print_extra([], _, _, _) :- !.
  149print_extra(List, Context, Comment, Options) :-
  150    option(output(Out), Options),
  151    format(Out, ', % ~w', [Comment]),
  152    modify_context(Context, [indent=4], Context1),
  153    print_extra_2(List, Context1, Options).
  154
  155print_extra_2([H|T], Context, Options) :-
  156    option(output(Out), Options),
  157    context(Context, indent, Indent),
  158    indent(Out, Indent, Options),
  159    pp(H, Context, Options),
  160    (   T == []
  161    ->  true
  162    ;   format(Out, ',', []),
  163        print_extra_2(T, Context, Options)
  164    ).
 prepare_term(+Term, -Template, -Cycles, -Constraints)
Prepare a term, possibly holding cycles and constraints for printing.
  172prepare_term(Term, Template, Cycles, Constraints) :-
  173    term_attvars(Term, []),
  174    !,
  175    Constraints = [],
  176    '$factorize_term'(Term, Template, Factors),
  177    bind_non_cycles(Factors, 1, Cycles),
  178    numbervars(Template+Cycles+Constraints, 0, _,
  179               [singletons(true)]).
  180prepare_term(Term, Template, Cycles, Constraints) :-
  181    copy_term(Term, Copy, Constraints),
  182    !,
  183    '$factorize_term'(Copy, Template, Factors),
  184    bind_non_cycles(Factors, 1, Cycles),
  185    numbervars(Template+Cycles+Constraints, 0, _,
  186               [singletons(true)]).
  187
  188
  189bind_non_cycles([], _, []).
  190bind_non_cycles([V=Term|T], I, L) :-
  191    unify_with_occurs_check(V, Term),
  192    !,
  193    bind_non_cycles(T, I, L).
  194bind_non_cycles([H|T0], I, [H|T]) :-
  195    H = ('$VAR'(Name)=_),
  196    atom_concat('_S', I, Name),
  197    I2 is I + 1,
  198    bind_non_cycles(T0, I2, T).
  199
  200
  201defaults([ output(user_output),
  202           left_margin(0),
  203           depth(0),
  204           indent(0),
  205           indent_arguments(auto),
  206           operators(true),
  207           write_options([ quoted(true),
  208                           numbervars(true),
  209                           portray(true),
  210                           attributes(portray)
  211                         ]),
  212           priority(1200)
  213         ]).
  214
  215default_margin(Options0, Options) :-
  216    option(right_margin(Margin), Options0),
  217    !,
  218    (   var(Margin)
  219    ->  tty_right_margin(Options0, Margin)
  220    ;   true
  221    ),
  222    Options = Options0.
  223default_margin(Options0, [right_margin(Margin)|Options0]) :-
  224    tty_right_margin(Options0, Margin).
  225
  226tty_right_margin(Options, Margin) :-
  227    option(output(Output), Options),
  228    stream_property(Output, tty(true)),
  229    catch(tty_size(_Rows, Columns), error(_,_), fail),
  230    !,
  231    Margin is Columns - 8.
  232tty_right_margin(_, 72).
  233
  234
  235                 /*******************************
  236                 *             CONTEXT          *
  237                 *******************************/
  238
  239context(Ctx, Name, Value) :-
  240    get_dict(Name, Ctx, Value).
  241
  242modify_context(Ctx0, Mapping, Ctx) :-
  243    Ctx = Ctx0.put(Mapping).
  244
  245dec_depth(Ctx, Ctx) :-
  246    context(Ctx, max_depth, infinite),
  247    !.
  248dec_depth(Ctx0, Ctx) :-
  249    ND is Ctx0.max_depth - 1,
  250    Ctx = Ctx0.put(max_depth, ND).
  251
  252
  253                 /*******************************
  254                 *              PP              *
  255                 *******************************/
  256
  257pp(Primitive, Ctx, Options) :-
  258    (   atomic(Primitive)
  259    ;   var(Primitive)
  260    ;   Primitive = '$VAR'(Var),
  261        (   integer(Var)
  262        ;   atom(Var)
  263        )
  264    ),
  265    !,
  266    pprint(Primitive, Ctx, Options).
  267pp(Portray, _Ctx, Options) :-
  268    option(write_options(WriteOptions), Options),
  269    option(portray(true), WriteOptions),
  270    option(output(Out), Options),
  271    with_output_to(Out, user:portray(Portray)),
  272    !.
  273pp(List, Ctx, Options) :-
  274    List = [_|_],
  275    !,
  276    context(Ctx, indent, Indent),
  277    context(Ctx, depth, Depth),
  278    option(output(Out), Options),
  279    option(indent_arguments(IndentStyle), Options),
  280    (   (   IndentStyle == false
  281        ->  true
  282        ;   IndentStyle == auto,
  283            print_width(List, Width, Options),
  284            option(right_margin(RM), Options),
  285            Indent + Width < RM
  286        )
  287    ->  pprint(List, Ctx, Options)
  288    ;   format(Out, '[ ', []),
  289        Nindent is Indent + 2,
  290        NDepth is Depth + 1,
  291        modify_context(Ctx, [indent=Nindent, depth=NDepth, priority=999], NCtx),
  292        pp_list_elements(List, NCtx, Options),
  293        indent(Out, Indent, Options),
  294        format(Out, ']', [])
  295    ).
  296pp(Dict, Ctx, Options) :-
  297    is_dict(Dict),
  298    !,
  299    dict_pairs(Dict, Tag, Pairs),
  300    option(output(Out), Options),
  301    option(indent_arguments(IndentStyle), Options),
  302    context(Ctx, indent, Indent),
  303    (   IndentStyle == false ; Pairs == []
  304    ->  pprint(Dict, Ctx, Options)
  305    ;   IndentStyle == auto,
  306        print_width(Dict, Width, Options),
  307        option(right_margin(RM), Options),
  308        Indent + Width < RM         % fits on a line, simply write
  309    ->  pprint(Dict, Ctx, Options)
  310    ;   format(atom(Buf2), '~q{ ', [Tag]),
  311        write(Out, Buf2),
  312        atom_length(Buf2, FunctorIndent),
  313        (   integer(IndentStyle)
  314        ->  Nindent is Indent + IndentStyle,
  315            (   FunctorIndent > IndentStyle
  316            ->  indent(Out, Nindent, Options)
  317            ;   true
  318            )
  319        ;   Nindent is Indent + FunctorIndent
  320        ),
  321        context(Ctx, depth, Depth),
  322        NDepth is Depth + 1,
  323        modify_context(Ctx, [indent=Nindent, depth=NDepth], NCtx0),
  324        dec_depth(NCtx0, NCtx),
  325        pp_dict_args(Pairs, NCtx, Options),
  326        BraceIndent is Nindent - 2,         % '{ '
  327        indent(Out, BraceIndent, Options),
  328        write(Out, '}')
  329    ).
  330pp(Term, Ctx, Options) :-               % handle operators
  331    compound(Term),
  332    compound_name_arity(Term, Name, Arity),
  333    current_op(Prec, Type, Name),
  334    match_op(Type, Arity, Kind, Prec, Left, Right),
  335    option(operators(true), Options),
  336    !,
  337    quoted_op(Name, QName),
  338    option(output(Out), Options),
  339    context(Ctx, indent, Indent),
  340    context(Ctx, depth, Depth),
  341    context(Ctx, priority, CPrec),
  342    NDepth is Depth + 1,
  343    modify_context(Ctx, [depth=NDepth], Ctx1),
  344    dec_depth(Ctx1, Ctx2),
  345    LeftOptions  = Ctx2.put(priority, Left),
  346    FuncOptions  = Ctx2.put(embrace, never),
  347    RightOptions = Ctx2.put(priority, Right),
  348    (   Kind == prefix
  349    ->  arg(1, Term, Arg),
  350        (   (   space_op(Name)
  351            ;   need_space(Name, Arg, FuncOptions, RightOptions)
  352            )
  353        ->  Space = ' '
  354        ;   Space = ''
  355        ),
  356        (   CPrec >= Prec
  357        ->  format(atom(Buf), '~w~w', [QName, Space]),
  358            atom_length(Buf, AL),
  359            NIndent is Indent + AL,
  360            write(Out, Buf),
  361            modify_context(Ctx2, [indent=NIndent, priority=Right], Ctx3),
  362            pp(Arg, Ctx3, Options)
  363        ;   format(atom(Buf), '(~w~w', [QName,Space]),
  364            atom_length(Buf, AL),
  365            NIndent is Indent + AL,
  366            write(Out, Buf),
  367            modify_context(Ctx2, [indent=NIndent, priority=Right], Ctx3),
  368            pp(Arg, Ctx3, Options),
  369            format(Out, ')', [])
  370        )
  371    ;   Kind == postfix
  372    ->  arg(1, Term, Arg),
  373        (   (   space_op(Name)
  374            ;   need_space(Name, Arg, FuncOptions, LeftOptions)
  375            )
  376        ->  Space = ' '
  377        ;   Space = ''
  378        ),
  379        (   CPrec >= Prec
  380        ->  modify_context(Ctx2, [priority=Left], Ctx3),
  381            pp(Arg, Ctx3, Options),
  382            format(Out, '~w~w', [Space,QName])
  383        ;   format(Out, '(', []),
  384            NIndent is Indent + 1,
  385            modify_context(Ctx2, [indent=NIndent, priority=Left], Ctx3),
  386            pp(Arg, Ctx3, Options),
  387            format(Out, '~w~w)', [Space,QName])
  388        )
  389    ;   arg(1, Term, Arg1),             % Infix operators
  390        arg(2, Term, Arg2),
  391        (   print_width(Term, Width, Options),
  392            option(right_margin(RM), Options),
  393            Indent + Width < RM
  394        ->  ToWide = false,
  395            (   (   space_op(Name)
  396                ;   need_space(Arg1, Name, LeftOptions, FuncOptions)
  397                ;   need_space(Name, Arg2, FuncOptions, RightOptions)
  398                )
  399            ->  Space = ' '
  400            ;   Space = ''
  401            )
  402        ;   ToWide = true,
  403            (   (   is_solo(Name)
  404                ;   space_op(Name)
  405                )
  406            ->  Space = ''
  407            ;   Space = ' '
  408            )
  409        ),
  410        (   CPrec >= Prec
  411        ->  (   ToWide == true,
  412                infix_list(Term, Name, List),
  413                List == [_,_|_]
  414            ->  Pri is min(Left,Right),
  415                modify_context(Ctx2, [space=Space, priority=Pri], Ctx3),
  416                pp_infix_list(List, QName, 2, Ctx3, Options)
  417            ;   modify_context(Ctx2, [priority=Left], Ctx3),
  418                pp(Arg1, Ctx3, Options),
  419                format(Out, '~w~w~w', [Space,QName,Space]),
  420                modify_context(Ctx2, [priority=Right], Ctx4),
  421                pp(Arg2, Ctx4, Options)
  422            )
  423        ;   (   ToWide == true,
  424                infix_list(Term, Name, List),
  425                List = [_,_|_]
  426            ->  Pri is min(Left,Right),
  427                format(Out, '( ', []),
  428                NIndent is Indent + 2,
  429                modify_context(Ctx2,
  430                           [space=Space, indent=NIndent, priority=Pri],
  431                               Ctx3),
  432                pp_infix_list(List, QName, 0, Ctx3, Options),
  433                indent(Out, Indent, Options),
  434                format(Out, ')', [])
  435            ;   format(Out, '(', []),
  436                NIndent is Indent + 1,
  437                modify_context(Ctx2, [indent=NIndent, priority=Left], Ctx3),
  438                pp(Arg1, Ctx3, Options),
  439                format(Out, '~w~w~w', [Space,QName,Space]),
  440                modify_context(Ctx2, [priority=Right], Ctx4),
  441                pp(Arg2, Ctx4, Options),
  442                format(Out, ')', [])
  443            )
  444        )
  445    ).
  446pp(Term, Ctx, Options) :-               % compound
  447    option(output(Out), Options),
  448    option(indent_arguments(IndentStyle), Options),
  449    context(Ctx, indent, Indent),
  450    (   IndentStyle == false
  451    ->  pprint(Term, Ctx, Options)
  452    ;   IndentStyle == auto,
  453        print_width(Term, Width, Options),
  454        option(right_margin(RM), Options),
  455        Indent + Width < RM         % fits on a line, simply write
  456    ->  pprint(Term, Ctx, Options)
  457    ;   compound_name_arguments(Term, Name, Args),
  458        format(atom(Buf2), '~q(', [Name]),
  459        write(Out, Buf2),
  460        atom_length(Buf2, FunctorIndent),
  461        (   integer(IndentStyle)
  462        ->  Nindent is Indent + IndentStyle,
  463            (   FunctorIndent > IndentStyle
  464            ->  indent(Out, Nindent, Options)
  465            ;   true
  466            )
  467        ;   Nindent is Indent + FunctorIndent
  468        ),
  469        context(Ctx, depth, Depth),
  470        NDepth is Depth + 1,
  471        modify_context(Ctx,
  472                       [indent=Nindent, depth=NDepth, priority=999],
  473                       NCtx0),
  474        dec_depth(NCtx0, NCtx),
  475        pp_compound_args(Args, NCtx, Options),
  476        write(Out, ')')
  477    ).
  478
  479
  480quoted_op(Op, Atom) :-
  481    is_solo(Op),
  482    !,
  483    Atom = Op.
  484quoted_op(Op, Q) :-
  485    format(atom(Q), '~q', [Op]).
 infix_list(+Term, ?Op, -List) is semidet
True when List is a list of subterms of Term that are the result of the nested infix operator Op. Deals both with xfy and yfx operators.
  493infix_list(Term, Op, List) :-
  494    phrase(infix_list(Term, Op), List).
  495
  496infix_list(Term, Op) -->
  497    { compound(Term),
  498      compound_name_arity(Term, Op, 2)
  499    },
  500    (   {current_op(_Pri, xfy, Op)}
  501    ->  { arg(1, Term, H),
  502          arg(2, Term, Term2)
  503        },
  504        [H],
  505        infix_list(Term2, Op)
  506    ;   {current_op(_Pri, yfx, Op)}
  507    ->  { arg(1, Term, Term2),
  508          arg(2, Term, T)
  509        },
  510        infix_list(Term2, Op),
  511        [T]
  512    ).
  513infix_list(Term, Op) -->
  514    {atom(Op)},                      % we did something before
  515    [Term].
  516
  517pp_infix_list([H|T], QName, IncrIndent, Ctx, Options) =>
  518    pp(H, Ctx, Options),
  519    context(Ctx, space, Space),
  520    (   T == []
  521    ->  true
  522    ;   option(output(Out), Options),
  523        format(Out, '~w~w', [Space,QName]),
  524        context(Ctx, indent, Indent),
  525        NIndent is Indent+IncrIndent,
  526        indent(Out, NIndent, Options),
  527        modify_context(Ctx, [indent=NIndent], Ctx2),
  528        pp_infix_list(T, QName, 0, Ctx2, Options)
  529    ).
 pp_list_elements(+List, +Ctx, +Options) is det
Print the elements of a possibly open list as a vertical list.
  536pp_list_elements(_, Ctx, Options) :-
  537    context(Ctx, max_depth, 0),
  538    !,
  539    option(output(Out), Options),
  540    write(Out, '...').
  541pp_list_elements([H|T], Ctx0, Options) :-
  542    dec_depth(Ctx0, Ctx),
  543    pp(H, Ctx, Options),
  544    (   T == []
  545    ->  true
  546    ;   nonvar(T),
  547        T = [_|_]
  548    ->  option(output(Out), Options),
  549        write(Out, ','),
  550        context(Ctx, indent, Indent),
  551        indent(Out, Indent, Options),
  552        pp_list_elements(T, Ctx, Options)
  553    ;   option(output(Out), Options),
  554        context(Ctx, indent, Indent),
  555        indent(Out, Indent-2, Options),
  556        write(Out, '| '),
  557        pp(T, Ctx, Options)
  558    ).
  559
  560
  561pp_compound_args([], _, _).
  562pp_compound_args([H|T], Ctx, Options) :-
  563    pp(H, Ctx, Options),
  564    (   T == []
  565    ->  true
  566    ;   T = [_|_]
  567    ->  option(output(Out), Options),
  568        write(Out, ','),
  569        context(Ctx, indent, Indent),
  570        indent(Out, Indent, Options),
  571        pp_compound_args(T, Ctx, Options)
  572    ;   option(output(Out), Options),
  573        context(Ctx, indent, Indent),
  574        indent(Out, Indent-2, Options),
  575        write(Out, '| '),
  576        pp(T, Ctx, Options)
  577    ).
  578
  579
  580:- if(current_predicate(is_dict/1)).  581pp_dict_args([Name-Value|T], Ctx, Options) :-
  582    option(output(Out), Options),
  583    line_position(Out, Pos0),
  584    pp(Name, Ctx, Options),
  585    write(Out, ':'),
  586    line_position(Out, Pos1),
  587    context(Ctx, indent, Indent),
  588    Indent2 is Indent + Pos1-Pos0,
  589    modify_context(Ctx, [indent=Indent2], Ctx2),
  590    pp(Value, Ctx2, Options),
  591    (   T == []
  592    ->  true
  593    ;   option(output(Out), Options),
  594        write(Out, ','),
  595        indent(Out, Indent, Options),
  596        pp_dict_args(T, Ctx, Options)
  597    ).
  598:- endif.  599
  600%       match_op(+Type, +Arity, +Precedence, -LeftPrec, -RightPrec
  601
  602match_op(fx,    1, prefix,  P, _, R) :- R is P - 1.
  603match_op(fy,    1, prefix,  P, _, P).
  604match_op(xf,    1, postfix, P, L, _) :- L is P - 1.
  605match_op(yf,    1, postfix, P, P, _).
  606match_op(xfx,   2, infix,   P, A, A) :- A is P - 1.
  607match_op(xfy,   2, infix,   P, L, P) :- L is P - 1.
  608match_op(yfx,   2, infix,   P, P, R) :- R is P - 1.
 indent(+Out, +Indent, +Options)
Newline and indent to the indicated column. Respects the option tab_width. Default is 8. If the tab-width equals zero, indentation is emitted using spaces.
  617indent(Out, Indent, Options) :-
  618    option(tab_width(TW), Options, 8),
  619    nl(Out),
  620    (   TW =:= 0
  621    ->  tab(Out, Indent)
  622    ;   Tabs is Indent // TW,
  623        Spaces is Indent mod TW,
  624        forall(between(1, Tabs, _), put(Out, 9)),
  625        tab(Out, Spaces)
  626    ).
 print_width(+Term, -W, +Options) is det
Width required when printing `normally' left-to-right.
  632print_width(Term, W, Options) :-
  633    option(right_margin(RM), Options),
  634    option(write_options(WOpts), Options),
  635    (   catch(write_length(Term, W, [max_length(RM)|WOpts]),
  636              error(_,_), fail)      % silence uncaught exceptions from
  637    ->  true                         % nested portray callbacks
  638    ;   W = RM
  639    ).
 pprint(+Term, +Context, +Options)
The bottom-line print-routine.
  645pprint(Term, Ctx, Options) :-
  646    option(output(Out), Options),
  647    pprint(Out, Term, Ctx, Options).
  648
  649pprint(Out, Term, Ctx, Options) :-
  650    option(write_options(WriteOptions), Options),
  651    context(Ctx, max_depth, MaxDepth),
  652    (   MaxDepth == infinite
  653    ->  write_term(Out, Term, WriteOptions)
  654    ;   MaxDepth =< 0
  655    ->  format(Out, '...', [])
  656    ;   write_term(Out, Term, [max_depth(MaxDepth)|WriteOptions])
  657    ).
  658
  659
  660		 /*******************************
  661		 *    SHARED WITH term_html.pl	*
  662		 *******************************/
 is_op1(+Name, -Type, -Priority, -ArgPriority, +Options) is semidet
True if Name is an operator taking one argument of Type.
  669is_op1(Name, Type, Pri, ArgPri, Options) :-
  670    operator_module(Module, Options),
  671    current_op(Pri, OpType, Module:Name),
  672    argpri(OpType, Type, Pri, ArgPri),
  673    !.
  674
  675argpri(fx, prefix,  Pri0, Pri) :- Pri is Pri0 - 1.
  676argpri(fy, prefix,  Pri,  Pri).
  677argpri(xf, postfix, Pri0, Pri) :- Pri is Pri0 - 1.
  678argpri(yf, postfix, Pri,  Pri).
 is_op2(+Name, -LeftPri, -Pri, -RightPri, +Options) is semidet
True if Name is an operator taking two arguments of Type.
  684is_op2(Name, LeftPri, Pri, RightPri, Options) :-
  685    operator_module(Module, Options),
  686    current_op(Pri, Type, Module:Name),
  687    infix_argpri(Type, LeftPri, Pri, RightPri),
  688    !.
  689
  690infix_argpri(xfx, ArgPri, Pri, ArgPri) :- ArgPri is Pri - 1.
  691infix_argpri(yfx, Pri, Pri, ArgPri) :- ArgPri is Pri - 1.
  692infix_argpri(xfy, ArgPri, Pri, Pri) :- ArgPri is Pri - 1.
 need_space(@Term1, @Term2, +LeftOptions, +RightOptions)
True if a space is needed between Term1 and Term2 if they are printed using the given option lists.
  700need_space(T1, T2, _, _) :-
  701    (   is_solo(T1)
  702    ;   is_solo(T2)
  703    ),
  704    !,
  705    fail.
  706need_space(T1, T2, LeftOptions, RightOptions) :-
  707    end_code_type(T1, TypeR, LeftOptions.put(side, right)),
  708    end_code_type(T2, TypeL, RightOptions.put(side, left)),
  709    \+ no_space(TypeR, TypeL).
  710
  711no_space(punct, _).
  712no_space(_, punct).
  713no_space(quote(R), quote(L)) :-
  714    !,
  715    R \== L.
  716no_space(alnum, symbol).
  717no_space(symbol, alnum).
 end_code_type(+Term, -Code, Options)
True when code is the first/last character code that is emitted by printing Term using Options.
  724end_code_type(_, Type, Options) :-
  725    MaxDepth = Options.max_depth,
  726    integer(MaxDepth),
  727    Options.depth >= MaxDepth,
  728    !,
  729    Type = symbol.
  730end_code_type(Term, Type, Options) :-
  731    primitive(Term, _),
  732    !,
  733    quote_atomic(Term, S, Options),
  734    end_type(S, Type, Options).
  735end_code_type(Dict, Type, Options) :-
  736    is_dict(Dict, Tag),
  737    !,
  738    (   Options.side == left
  739    ->  end_code_type(Tag, Type, Options)
  740    ;   Type = punct
  741    ).
  742end_code_type('$VAR'(Var), Type, Options) :-
  743    Options.get(numbervars) == true,
  744    !,
  745    format(string(S), '~W', ['$VAR'(Var), [numbervars(true)]]),
  746    end_type(S, Type, Options).
  747end_code_type(List, Type, _) :-
  748    (   List == []
  749    ;   List = [_|_]
  750    ),
  751    !,
  752    Type = punct.
  753end_code_type(OpTerm, Type, Options) :-
  754    compound_name_arity(OpTerm, Name, 1),
  755    is_op1(Name, OpType, Pri, ArgPri, Options),
  756    \+ Options.get(ignore_ops) == true,
  757    !,
  758    (   Pri > Options.priority
  759    ->  Type = punct
  760    ;   op_or_arg(OpType, Options.side, OpArg),
  761        (   OpArg == op
  762        ->  end_code_type(Name, Type, Options)
  763        ;   arg(1, OpTerm, Arg),
  764            arg_options(Options, ArgOptions),
  765            end_code_type(Arg, Type, ArgOptions.put(priority, ArgPri))
  766        )
  767    ).
  768end_code_type(OpTerm, Type, Options) :-
  769    compound_name_arity(OpTerm, Name, 2),
  770    is_op2(Name, LeftPri, Pri, _RightPri, Options),
  771    \+ Options.get(ignore_ops) == true,
  772    !,
  773    (   Pri > Options.priority
  774    ->  Type = punct
  775    ;   arg(1, OpTerm, Arg),
  776        arg_options(Options, ArgOptions),
  777        end_code_type(Arg, Type, ArgOptions.put(priority, LeftPri))
  778    ).
  779end_code_type(Compound, Type, Options) :-
  780    compound_name_arity(Compound, Name, _),
  781    end_code_type(Name, Type, Options).
  782
  783op_or_arg(prefix,  left,  op).
  784op_or_arg(prefix,  right, arg).
  785op_or_arg(postfix, left,  arg).
  786op_or_arg(postfix, right, op).
  787
  788
  789
  790end_type(S, Type, Options) :-
  791    number(S),
  792    !,
  793    (   (S < 0 ; S == -0.0),
  794        Options.side == left
  795    ->  Type = symbol
  796    ;   Type = alnum
  797    ).
  798end_type(S, Type, Options) :-
  799    Options.side == left,
  800    !,
  801    sub_string(S, 0, 1, _, Start),
  802    syntax_type(Start, Type).
  803end_type(S, Type, _) :-
  804    sub_string(S, _, 1, 0, End),
  805    syntax_type(End, Type).
  806
  807syntax_type("\"", quote(double)) :- !.
  808syntax_type("\'", quote(single)) :- !.
  809syntax_type("\`", quote(back))   :- !.
  810syntax_type(S, Type) :-
  811    string_code(1, S, C),
  812    (   code_type(C, prolog_identifier_continue)
  813    ->  Type = alnum
  814    ;   code_type(C, prolog_symbol)
  815    ->  Type = symbol
  816    ;   code_type(C, space)
  817    ->  Type = layout
  818    ;   Type = punct
  819    ).
  820
  821is_solo(Var) :-
  822    var(Var), !, fail.
  823is_solo(',').
  824is_solo(';').
  825is_solo('!').
 primitive(+Term, -Class) is semidet
True if Term is a primitive term, rendered using the CSS class Class.
  832primitive(Term, Type) :- var(Term),      !, Type = 'pl-avar'.
  833primitive(Term, Type) :- atom(Term),     !, Type = 'pl-atom'.
  834primitive(Term, Type) :- string(Term),   !, Type = 'pl-string'.
  835primitive(Term, Type) :- integer(Term),  !, Type = 'pl-int'.
  836primitive(Term, Type) :- rational(Term), !, Type = 'pl-rational'.
  837primitive(Term, Type) :- float(Term),    !, Type = 'pl-float'.
 operator_module(-Module, +Options) is det
Find the module for evaluating operators.
  843operator_module(Module, Options) :-
  844    Module = Options.get(module),
  845    !.
  846operator_module(TypeIn, _) :-
  847    '$module'(TypeIn, TypeIn).
 arg_options(+Options, -OptionsOut) is det
Increment depth in Options.
  853arg_options(Options, Options.put(depth, NewDepth)) :-
  854    NewDepth is Options.depth+1.
  855
  856quote_atomic(Float, String, Options) :-
  857    float(Float),
  858    Format = Options.get(float_format),
  859    !,
  860    format(string(String), Format, [Float]).
  861quote_atomic(Plain, Plain, _) :-
  862    number(Plain),
  863    !.
  864quote_atomic(Plain, String, Options) :-
  865    Options.get(quoted) == true,
  866    !,
  867    (   Options.get(embrace) == never
  868    ->  format(string(String), '~q', [Plain])
  869    ;   format(string(String), '~W', [Plain, Options])
  870    ).
  871quote_atomic(Var, String, Options) :-
  872    var(Var),
  873    !,
  874    format(string(String), '~W', [Var, Options]).
  875quote_atomic(Plain, Plain, _).
  876
  877space_op(:-)