%--------------------------------------------------% % 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. %--------------------------------------------------% %--------------------------------------------------%