src/Pure/General/pretty.ML
author wenzelm
Tue, 04 Oct 2005 19:01:37 +0200
changeset 17756 d4a35f82fbb4
parent 17542 b588e06b6775
child 18603 04c2c702a3fb
permissions -rw-r--r--
minor tweaks for Poplog/ML;
     1 (*  Title:      Pure/General/pretty.ML
     2     ID:         $Id$
     3     Author:     Lawrence C Paulson, Cambridge University Computer Laboratory
     4     Author:     Markus Wenzel, TU Munich
     5 
     6 Generic pretty printing module.
     7 
     8 Loosely based on
     9   D. C. Oppen, "Pretty Printing",
    10   ACM Transactions on Programming Languages and Systems (1980), 465-483.
    11 
    12 The object to be printed is given as a tree with indentation and line
    13 breaking information.  A "break" inserts a newline if the text until
    14 the next break is too long to fit on the current line.  After the newline,
    15 text is indented to the level of the enclosing block.  Normally, if a block
    16 is broken then all enclosing blocks will also be broken.  Only "inconsistent
    17 breaks" are provided.
    18 
    19 The stored length of a block is used in breakdist (to treat each inner block as
    20 a unit for breaking).
    21 *)
    22 
    23 type pprint_args = (string -> unit) * (int -> unit) * (int -> unit) *
    24   (unit -> unit) * (unit -> unit);
    25 
    26 signature PRETTY =
    27 sig
    28   type T
    29   val raw_str: string * real -> T
    30   val str: string -> T
    31   val brk: int -> T
    32   val fbrk: T
    33   val blk: int * T list -> T
    34   val unbreakable: T -> T
    35   val quote: T -> T
    36   val commas: T list -> T list
    37   val breaks: T list -> T list
    38   val fbreaks: T list -> T list
    39   val block: T list -> T
    40   val strs: string list -> T
    41   val enclose: string -> string -> T list -> T
    42   val list: string -> string -> T list -> T
    43   val gen_list: string -> string -> string -> T list -> T
    44   val str_list: string -> string -> string list -> T
    45   val big_list: string -> T list -> T
    46   val chunks: T list -> T
    47   val indent: int -> T -> T
    48   val string_of: T -> string
    49   val output_buffer: T -> Buffer.T
    50   val output: T -> string
    51   val writeln: T -> unit
    52   val writelns: T list -> unit
    53   val str_of: T -> string
    54   val pprint: T -> pprint_args -> unit
    55   val setdepth: int -> unit
    56   val setmargin: int -> unit
    57   val setmp_margin: int -> ('a -> 'b) -> 'a -> 'b
    58   type pp
    59   val pp: (term -> T) * (typ -> T) * (sort -> T) * (class list -> T) * (arity -> T) -> pp
    60   val term: pp -> term -> T
    61   val typ: pp -> typ -> T
    62   val sort: pp -> sort -> T
    63   val classrel: pp -> class list -> T
    64   val arity: pp -> arity -> T
    65   val string_of_term: pp -> term -> string
    66   val string_of_typ: pp -> typ -> string
    67   val string_of_sort: pp -> sort -> string
    68   val string_of_classrel: pp -> class list -> string
    69   val string_of_arity: pp -> arity -> string
    70 end;
    71 
    72 structure Pretty : PRETTY =
    73 struct
    74 
    75 
    76 (** printing items: compound phrases, strings, and breaks **)
    77 
    78 datatype T =
    79   Block of T list * int * int |         (*body, indentation, length*)
    80   String of string * int |              (*text, length*)
    81   Break of bool * int;                  (*mandatory flag, width if not taken*)
    82 
    83 
    84 
    85 (** output text **)
    86 
    87 val output_spaces = Output.output o Symbol.spaces;
    88 val add_indent = Buffer.add o output_spaces;
    89 fun set_indent wd = Buffer.empty |> add_indent wd;
    90 
    91 type text = {tx: Buffer.T, ind: Buffer.T, pos: int, nl: int};
    92 
    93 val empty: text =
    94  {tx = Buffer.empty,
    95   ind = Buffer.empty,
    96   pos = 0,
    97   nl = 0};
    98 
    99 fun newline {tx, ind, pos, nl} : text =
   100  {tx = Buffer.add (Output.output "\n") tx,
   101   ind = Buffer.empty,
   102   pos = 0,
   103   nl = nl + 1};
   104 
   105 fun string (s, len) {tx, ind, pos: int, nl} : text =
   106  {tx = Buffer.add s tx,
   107   ind = Buffer.add s ind,
   108   pos = pos + len,
   109   nl = nl};
   110 
   111 fun blanks wd = string (output_spaces wd, wd);
   112 
   113 fun indentation (buf, len) {tx, ind, pos, nl} : text =
   114   let val s = Buffer.content buf in
   115    {tx = Buffer.add (Output.indent (s, len)) tx,
   116     ind = Buffer.add s ind,
   117     pos = pos + len,
   118     nl = nl}
   119   end;
   120 
   121 
   122 
   123 (** formatting **)
   124 
   125 (* margin *)
   126 
   127 fun make_margin_info m =
   128  {margin = m,                   (*right margin, or page width*)
   129   breakgain = m div 20,         (*minimum added space required of a break*)
   130   emergencypos = m div 2};      (*position too far to right*)
   131 
   132 val margin_info = ref (make_margin_info 76);
   133 fun setmargin m = margin_info := make_margin_info m;
   134 fun setmp_margin m f = setmp margin_info (make_margin_info m) f;
   135 
   136 
   137 (* format *)
   138 
   139 (*Add the lengths of the expressions until the next Break; if no Break then
   140   include "after", to account for text following this block.*)
   141 fun breakdist (Block (_, _, len) :: es, after) = len + breakdist (es, after)
   142   | breakdist (String (s, len) :: es, after) = len + breakdist (es, after)
   143   | breakdist (Break _ :: es, after) = 0
   144   | breakdist ([], after) = after;
   145 
   146 (*Search for the next break (at this or higher levels) and force it to occur.*)
   147 fun forcenext [] = []
   148   | forcenext (Break (_, wd) :: es) = Break (true, 0) :: es
   149   | forcenext (e :: es) = e :: forcenext es;
   150 
   151 (*es is list of expressions to print;
   152   blockin is the indentation of the current block;
   153   after is the width of the following context until next break.*)
   154 fun format ([], _, _) text = text
   155   | format (e :: es, block as (blockind, blockin), after) (text as {ind, pos, nl, ...}) =
   156       (case e of
   157         Block (bes, indent, wd) =>
   158           let
   159             val {emergencypos, ...} = ! margin_info;
   160             val pos' = pos + indent;
   161             val pos'' = pos' mod emergencypos;
   162             val block' =
   163               if pos' < emergencypos then (ind |> add_indent indent, pos')
   164               else (set_indent pos'', pos'');
   165             val btext: text = format (bes, block', breakdist (es, after)) text;
   166             (*if this block was broken then force the next break*)
   167             val es2 = if nl < #nl btext then forcenext es else es;
   168           in format (es2, block, after) btext end
   169       | String str => format (es, block, after) (string str text)
   170       | Break (force, wd) =>
   171           let val {margin, breakgain, ...} = ! margin_info in
   172             (*no break if text to next break fits on this line
   173               or if breaking would add only breakgain to space*)
   174             format (es, block, after)
   175               (if not force andalso
   176                   pos + wd <= Int.max (margin - breakdist (es, after), blockin + breakgain)
   177                 then text |> blanks wd  (*just insert wd blanks*)
   178                 else text |> newline |> indentation block)
   179           end);
   180 
   181 
   182 (** Exported functions to create formatting expressions **)
   183 
   184 fun length (Block (_, _, len)) = len
   185   | length (String (_, len)) = len
   186   | length (Break(_, wd)) = wd;
   187 
   188 fun raw_str (s, len) = String (s, Real.round len);
   189 val str = String o apsnd Real.round o Output.output_width;
   190 
   191 fun brk wd = Break (false, wd);
   192 val fbrk = Break (true, 2);
   193 
   194 fun blk (indent, es) =
   195   let
   196     fun sum([], k) = k
   197       | sum(e :: es, k) = sum (es, length e + k);
   198   in Block (es, indent, sum (es, 0)) end;
   199 
   200 fun unbreakable (Break (_, wd)) = String (output_spaces wd, wd)
   201   | unbreakable (Block (es, indent, wd)) = Block (map unbreakable es, indent, wd)
   202   | unbreakable (e as String _) = e;
   203 
   204 
   205 (* utils *)
   206 
   207 fun quote prt =
   208   blk (1, [str "\"", prt, str "\""]);
   209 
   210 fun separate_pretty sep prts =
   211   prts
   212   |> map single
   213   |> separate [str sep, brk 1]
   214   |> List.concat;
   215 
   216 val commas = separate_pretty ",";
   217 
   218 fun breaks prts = separate (brk 1) prts;
   219 fun fbreaks prts = separate fbrk prts;
   220 
   221 fun block prts = blk (2, prts);
   222 
   223 val strs = block o breaks o map str;
   224 
   225 fun enclose lpar rpar prts =
   226   block (str lpar :: (prts @ [str rpar]));
   227 
   228 fun gen_list sep lpar rpar prts = enclose lpar rpar (separate_pretty sep prts);
   229 val list = gen_list ",";
   230 fun str_list lpar rpar strs = list lpar rpar (map str strs);
   231 fun big_list name prts = block (fbreaks (str name :: prts));
   232 fun chunks prts = blk (0, fbreaks prts);
   233 
   234 fun indent 0 prt = prt
   235   | indent n prt = blk (0, [str (Symbol.spaces n), prt]);
   236 
   237 
   238 
   239 (** Pretty printing with depth limitation **)
   240 
   241 val depth       = ref 0;        (*maximum depth; 0 means no limit*)
   242 
   243 fun setdepth dp = (depth := dp);
   244 
   245 (*Recursively prune blocks, discarding all text exceeding depth dp*)
   246 fun pruning dp (Block(bes,indent,wd)) =
   247       if dp>0 then blk(indent, map (pruning(dp-1)) bes)
   248               else str "..."
   249   | pruning dp e = e;
   250 
   251 fun prune dp e = if dp > 0 then pruning dp e else e;
   252 
   253 fun output_buffer e = #tx (format ([prune (! depth) e], (Buffer.empty, 0), 0) empty);
   254 val output = Buffer.content o output_buffer;
   255 val string_of = Output.raw o output;
   256 val writeln = Output.writeln o string_of;
   257 fun writelns [] = () | writelns es = writeln (chunks es);
   258 
   259 
   260 (*Create a single flat string: no line breaking*)
   261 fun str_of prt =
   262   let
   263     fun s_of (Block (prts, _, _)) = implode (map s_of prts)
   264       | s_of (String (s, _)) = s
   265       | s_of (Break (false, wd)) = output_spaces wd
   266       | s_of (Break (true, _)) = output_spaces 1;
   267   in Output.raw (s_of (prune (! depth) prt)) end;
   268 
   269 (*part of the interface to the ML toplevel pretty printers*)
   270 fun pprint prt (put_str, begin_blk, put_brk, put_fbrk, end_blk) =
   271   let
   272     fun pp (Block (prts, ind, _)) = (begin_blk ind; pp_lst prts; end_blk ())
   273       | pp (String (s, _)) = put_str s
   274       | pp (Break (false, wd)) = put_brk wd
   275       | pp (Break (true, _)) = put_fbrk ()
   276     and pp_lst [] = ()
   277       | pp_lst (prt :: prts) = (pp prt; pp_lst prts);
   278   in
   279     pp (prune (! depth) prt)
   280   end;
   281 
   282 
   283 
   284 (** abstract pretty printing context **)
   285 
   286 datatype pp =
   287   PP of (term -> T) * (typ -> T) * (sort -> T) * (class list -> T) * (arity -> T);
   288 
   289 val pp = PP;
   290 
   291 fun pp_fun f (PP x) = f x;
   292 
   293 val term     = pp_fun #1;
   294 val typ      = pp_fun #2;
   295 val sort     = pp_fun #3;
   296 val classrel = pp_fun #4;
   297 val arity    = pp_fun #5;
   298 
   299 val string_of_term     = string_of oo term;
   300 val string_of_typ      = string_of oo typ;
   301 val string_of_sort     = string_of oo sort;
   302 val string_of_classrel = string_of oo classrel;
   303 val string_of_arity    = string_of oo arity;
   304 
   305 end;