%--------------------------------------------------%
% vim: ts=4 sw=4 expandtab ft=mercury
%--------------------------------------------------%
% Copyright (C) 2007, 2009-2011 The University of Melbourne
% Copyright (C) 2014-2016, 2018 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%--------------------------------------------------%
%
% 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 rules for operator precedence and
% bracketing.
%
% 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 a limit
% has been 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 and cannot be exploited
% by user code.
%
:- type pp_internal.
%--------------------------------------------------%
%
% Functions for constructing docs.
%
% 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(list(doc)) = 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.
%--------------------------------------------------%
%
% Functions for converting docs to strings and writing them out to streams.
%
% write_doc(Doc, !IO):
% write_doc(FileStream, Doc, !IO):
%
% Format Doc to io.stdout_stream or FileStream respectively using put_doc,
% 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.
% put_doc(Stream, Canonicalize, FMap, Params, Doc, !State):
%
% Format Doc to Stream. Format format_univ(_) docs using specialised
% formatters Formatters, and using Params as the pretty printer parameters.
% The Canonicalize argument controls how put_doc deconstructs values
% of noncanonical types (see the documentation of the noncanon_handling
% type for details).
%
:- pred put_doc(Stream, noncanon_handling, formatter_map, pp_params,
doc, State, State)
<= stream.writer(Stream, string, State).
:- mode put_doc(in, in(canonicalize), in, in, in, di, uo) is det.
:- mode put_doc(in, in(include_details_cc), in, in, in, di, uo) is cc_multi.
%--------------------------------------------------%
%
% Mechanisms for controlling *how* docs are converted to strings.
%
% 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.
%
:- pred set_formatter(string::in, string::in, int::in, formatter::in,
formatter_map::in, formatter_map::out) is det.
%--------------------------------------------------%
% The func_symbol_limit type controls *how many* of the function symbols
% stored in the term inside a format_univ, format_list, or format_term doc
% the write_doc family of functions should include in the resulting string.
%
% 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
% the following limits:
%
% - 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 func_symbol_limit
---> linear(int)
; triangular(int).
% The pp_params type contains the parameters of the prettyprinting process:
%
% - the width of each line,
% - the maximum number of lines to print, and
% - the controls for how many function symbols to print.
%
:- type pp_params
---> pp_params(
pp_line_width :: int,
pp_max_lines :: int,
pp_limit :: func_symbol_limit
).
%--------------------------------------------------%
% A user-configurable default set of type-specific formatters and
% formatting parameters is always attached to the I/O state.
% The write_doc predicate (in both its arities) uses these settings.
%
% The get_default_formatter_map predicate reads the default formatter_map
% from the current I/O state, while set_default_formatter_map writes
% the specified formatter_map to the I/O state to become the new default.
%
% The initial value of the default formatter_map provides the means
% to prettyprint the most commonly used types in the Mercury standard
% library, such as arrays, chars, floats, ints, maps, strings, etc.
%
% The default formatter_map may also be updated by users' modules
% (e.g. in initialisation goals).
%
% These defaults are thread local, and therefore 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.
% set_default_formatter(ModuleName, TypeName, TypeArity, Formatter, !IO):
%
% Update the default formatter in the I/O state to use Formatter
% to print values of the type ModuleName.TypeName/TypeArity.
%
:- pred set_default_formatter(string::in, string::in, int::in, formatter::in,
io::di, io::uo) is det.
% Alongside the default formatter_map, the I/O state also always stores
% a default set of pretty-printing parameters (pp_params) for use by
% the write_doc predicate (in both its arities).
%
% The get_default_params predicate reads the default parameters
% from the current I/O state, while set_default_params writes the specified
% parameters to the I/O state to become the new default.
%
% The initial default parameters are pp_params(78, 100, triangular(100)).
%
% These defaults are thread local, and therefore 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.
%--------------------------------------------------%
%--------------------------------------------------%