Next: , Previous: , Up: Top   [Contents]

50 pretty_printer

% vim: ts=4 sw=4 expandtab tw=0 wm=0 ft=mercury
% Copyright (C) 2007, 2009-2011 The University of Melbourne
% This file may only be copied under the terms of the GNU Library General
% Public License - see the file COPYING.LIB in the Mercury distribution.
% File: pretty_printer.m
% Main author: rafe
% Stability: medium
% This module defines a doc type for formatting and a pretty printer for
% displaying docs.
% The doc type includes data constructors for outputting strings, newlines,
% forming groups, indented blocks, and arbitrary values.
% The key feature of the algorithm is this: newlines in a group are ignored if
% the group can fit on the remainder of the current line.  [The algorithm is
% similar to those of Oppen and Wadler, although it uses neither coroutines or
% laziness.]
% When a newline is printed, indentation is also output according to the
% current indentation level.
% The pretty printer includes special support for formatting Mercury style
% terms in a way that respects Mercury's operator precedence and
% bracketing rules.
% The pretty printer takes a parameter specifying a collection of user-defined
% formatting functions for handling certain types rather than using the
% default built-in mechanism.  This allows one to, say, format maps as
% sequences of (key -> value) pairs rather than exposing the underlying
% 234-tree structure.
% The amount of output produced is controlled via limit parameters.  Three
% kinds of limits are supported: the output line width, the maximum number of
% lines to be output, and a limit on the depth for formatting arbitrary terms.
% Output is replaced with ellipsis ("...") when limits are exceeded.

:- module pretty_printer.
:- interface.

:- import_module deconstruct.
:- import_module list.
:- import_module io.
:- import_module stream.
:- import_module type_desc.
:- import_module univ.


:- type doc
    --->    str(string)
            % Output a literal string. Strings containing newlines, hard tabs,
            % etc. will lead to strange output.

    ;       nl
            % Output a newline, followed by indentation, iff the enclosing
            % group does not fit on the current line and starting a new line
            % adds more space.

    ;       hard_nl
            % Always outputs a newline, followed by indentation.

    ;       docs(docs)
            % An embedded sequence of docs.

    ;       format_univ(univ)
            % Use a specialised formatter if available, otherwise use the
            % generic formatter.

    ;       format_list(list(univ), doc)
            % Pretty print a list of items using the given doc as a separator
            % between items.

    ;       format_term(string, list(univ))
            % Pretty print a term with zero or more arguments. If the term
            % corresponds to a Mercury operator it will be printed with
            % appropriate fixity and, if necessary, in parentheses. The term
            % name will be quoted and escaped if necessary.

    ;       format_susp((func) = doc)
            % The argument is a suspended computation used to lazily produce a
            % doc. If the formatting limit has been reached then just "..." is
            % output, otherwise the suspension is evaluated and the resulting
            % doc is used. This is useful for formatting large structures
            % without using more resources than required. Expanding a
            % suspended computation reduces the formatting limit by one.

    ;       pp_internal(pp_internal).
            % pp_internal docs are used in the implementation and cannot
            % be exploited by user code.

:- type docs == list(doc).

    % This type is private to the implementation. It cannot be exploited by
    % user code.
:- type pp_internal.

    % indent(IndentString, Docs):
    % Append IndentString to the current indentation while printing Docs.
    % Indentation is printed after each newline that is output.
:- func indent(string, docs) = doc.

    % indent(Docs) = indent("  ", Docs).
    %   A convenient abbreviation.
:- func indent(docs) = doc.

    % group(Docs):
    % If Docs can be output on the remainder of the current line by ignoring
    % any nls in Docs, then do so. Otherwise nls in Docs are printed
    % (followed by any indentation). The formatting test is applied recursively
    % for any subgroups in Docs.
:- func group(docs) = doc.

    % format(X) = format_univ(univ(X)):
    % A convenient abbreviation.
:- func format(T) = doc.

    % format_arg(Doc) has the effect of formatting any term in Doc as though
    % it were an argument in a Mercury term by enclosing it in parentheses if
    % necessary.
:- func format_arg(doc) = doc.

    % The pretty-printer limit type, used to control conversion by
    % format_univ, format_list, and format_term.
    % A limit of linear(N) formats the first N functors before truncating
    % output to "...".
    % A limit of triangular(N) formats a term t(X1, ..., Xn) by applying a
    % limit of triangular(N - 1) when formatting X1, triangular(N - 2) when
    % formatting X2, ..., and triangular(N - n) when formatting Xn.
    % The cost of formatting the term t(X1, ..., Xn) as a whole is just one,
    % so a sequence of terms T1, T2, ... is formatted with limits
    % triangular(N), triangular(N - 1), ... respectively.  When the
    % limit is exhausted, terms are output as just "...".
:- type formatting_limit
    --->    linear(int)                 % Print this many functors.
    ;       triangular(int).            % Print first arg with limit N-1,
                                        % second arg with limit N-2, ...

    % The type of generic formatting functions.
    % The first argument is the univ of the value to be formatted.
    % The second argument is the list of argument type_descs for
    % the type of the first argument.
:- type formatter == ( func(univ, list(type_desc)) = doc ).

    % A formatter_map maps types to pps.  Types are identified by module name,
    % type name, and type arity.
:- type formatter_map.

    % Construct a new formatter_map.
:- func new_formatter_map = formatter_map.

    % set_formatter(ModuleName, TypeName, TypeArity, Formatter, FMap):
    % Update FMap to use Formatter to format the type
    % ModuleName.TypeName/TypeArity.
:- func set_formatter(string, string, int, formatter, formatter_map) =

    % format(Stream, FMap, LineWidth, MaxLines, Limit, Doc, !State):
    % Format Doc to fit on lines of LineWidth chars, truncating after
    % MaxLines lines, fomatting format_univ(_) docs using specialised
    % formatters Formatters starting with pretty-printer limits Limit.
:- pred write_doc_to_stream(Stream, noncanon_handling, formatter_map, int, int,
    formatting_limit, doc, State, State)
    <= stream.writer(Stream, string, State).
:- mode write_doc_to_stream(in, in(canonicalize), in, in, in, in, in,
    di, uo) is det.
:- mode write_doc_to_stream(in, in(include_details_cc), in, in, in, in, in,
    di, uo) is cc_multi.

    % Convenience predicates.  A user-configurable set of type-specific
    % formatters and formatting parameters are attached to the I/O state.
    % The I/O state-specific format predicate below uses this settings.
:- type pp_params
    --->    pp_params(
                pp_line_width   :: int,             % Line width in characters.
                pp_max_lines    :: int,             % Max lines to output.
                pp_limit        :: formatting_limit % Term formatting limit.

    % An initial default formatter_map is provided for the most commonly
    % used types in the Mercury standard library (array, char, float,
    % int, map, string, etc.)
    % The default formatter_map may also be updated by users' modules
    % (e.g., in initialisation goals).
    % These defaults are thread local (i.e., changes made by one thread to
    % the default formatter_map will not be visible in another thread).
:- pred get_default_formatter_map(formatter_map::out, io::di, io::uo) is det.
:- pred set_default_formatter_map(formatter_map::in, io::di, io::uo) is det.
:- pred set_default_formatter(string::in, string::in, int::in, formatter::in,
    io::di, io::uo) is det.

    % The initial default pp_params are pp_params(78, 100, triangular(100)).
    % These defaults are thread local (i.e., changes made by one thread to
    % the default pp_params will not be visible in another thread).
:- pred get_default_params(pp_params::out, io::di, io::uo) is det.
:- pred set_default_params(pp_params::in, io::di, io::uo) is det.

    % write_doc(Doc, !IO):
    % write_doc(FileStream, Doc, !IO):
    % Format Doc to io.stdout_stream or FileStream respectively, using
    % write_doc_to stream, with include_details_cc, the default formatter_map,
    % and the default pp_params.
:- pred write_doc(doc::in, io::di, io::uo) is det.
:- pred write_doc(io.output_stream::in, doc::in, io::di, io::uo) is det.


Next: , Previous: , Up: Top   [Contents]