src/Pure/Syntax/pretty.ML
changeset 0 a5a9c433f639
child 18 c9ec452ff08f
equal deleted inserted replaced
-1:000000000000 0:a5a9c433f639
       
     1 (*  Title:      Pure/Syntax/pretty
       
     2     ID:         $Id$
       
     3     Author:     Lawrence C Paulson
       
     4     Copyright   1991  University of Cambridge
       
     5 
       
     6 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 (* FIXME improve? *)
       
    24 type pprint_args = (string -> unit) * (int -> unit) * (int -> unit) * (unit -> unit);
       
    25 
       
    26 signature PRETTY =
       
    27   sig
       
    28   type T
       
    29   val blk : int * T list -> T
       
    30   val brk : int -> T
       
    31   val fbrk : T
       
    32   val str : string -> T
       
    33   val lst : string * string -> T list -> T
       
    34   val quote : T -> T
       
    35   val string_of : T -> string
       
    36   val str_of : T -> string
       
    37   val pprint : T -> pprint_args -> unit
       
    38   val setdepth: int -> unit
       
    39   val setmargin: int -> unit
       
    40   end;
       
    41 
       
    42 functor PrettyFun () : PRETTY =
       
    43 struct
       
    44 
       
    45 (*Printing items: compound phrases, strings, and breaks*)
       
    46 datatype T = Block of T list * int * int (*indentation, length*)
       
    47            | String of string
       
    48            | Break of bool*int   (*mandatory flag; width if not taken*);
       
    49 
       
    50 (*Add the lengths of the expressions until the next Break; if no Break then
       
    51   include "after", to account for text following this block. *)
       
    52 fun breakdist (Block(_,_,len)::es, after) = len + breakdist(es, after)
       
    53   | breakdist (String s :: es, after) = size s + breakdist (es, after)
       
    54   | breakdist (Break _ :: es, after) = 0
       
    55   | breakdist ([], after) = after;
       
    56 
       
    57 fun repstring a 0 = ""
       
    58   | repstring a 1 = a
       
    59   | repstring a k =
       
    60       if k mod 2 = 0 then repstring(a^a) (k div 2)
       
    61                      else repstring(a^a) (k div 2) ^ a;
       
    62 
       
    63 (*** Type for lines of text: string, # of lines, position on line ***)
       
    64 
       
    65 type text = {tx: string, nl: int, pos: int};
       
    66 
       
    67 val emptytext = {tx="", nl=0, pos=0};
       
    68 
       
    69 fun blanks wd {tx,nl,pos} =
       
    70     {tx  = tx ^ repstring" "wd,
       
    71      nl  = nl,
       
    72      pos = pos+wd};
       
    73 
       
    74 fun newline {tx,nl,pos} =
       
    75     {tx  = tx ^ "\n",
       
    76      nl  = nl+1,
       
    77      pos = 0};
       
    78 
       
    79 fun string s {tx,nl,pos} =
       
    80     {tx  = tx ^ s,
       
    81      nl  = nl,
       
    82      pos = pos + size(s)};
       
    83 
       
    84 (*** Formatting ***)
       
    85 
       
    86 val margin      = ref 80        (*right margin, or page width*)
       
    87 and breakgain   = ref 4         (*minimum added space required of a break*)
       
    88 and emergencypos = ref 40;      (*position too far to right*)
       
    89 
       
    90 fun setmargin m =
       
    91     (margin     := m;
       
    92      breakgain  := !margin div 20;
       
    93      emergencypos := !margin div 2);
       
    94 
       
    95 fun forcenext [] = []
       
    96   | forcenext (Break(_,wd) :: es) = Break(true,0) :: es
       
    97   | forcenext (e :: es) = e :: forcenext es;
       
    98 
       
    99 fun format ([], _, _) text = text
       
   100   | format (e::es, blockin, after) (text as {pos,nl,...}) =
       
   101     (case e of
       
   102        Block(bes,indent,wd) =>
       
   103          let val blockin' = (pos + indent) mod !emergencypos
       
   104              val btext = format(bes, blockin', breakdist(es,after)) text
       
   105              val es2 = if nl < #nl(btext) then forcenext es
       
   106                        else es
       
   107          in  format (es2,blockin,after) btext  end
       
   108      | String s => format (es,blockin,after) (string s text)
       
   109      | Break(force,wd) => (* no break if text fits on this line
       
   110                       or if breaking would add only breakgain to space *)
       
   111            format (es,blockin,after)
       
   112                (if not force andalso
       
   113                    pos+wd <= max[!margin - breakdist(es,after),
       
   114                                  blockin + !breakgain]
       
   115                 then blanks wd text
       
   116                 else blanks blockin (newline text)));
       
   117 
       
   118 (*** Exported functions to create formatting expressions ***)
       
   119 
       
   120 fun length (Block(_,_,len)) = len
       
   121   | length (String s) = size s
       
   122   | length (Break(_,wd)) = wd;
       
   123 
       
   124 val str = String
       
   125 and fbrk = Break(true,0);
       
   126 
       
   127 fun brk wd = Break(false,wd);
       
   128 
       
   129 fun blk (indent,es) =
       
   130   let fun sum([], k) = k
       
   131         | sum(e::es, k) = sum(es, length e + k)
       
   132   in  Block(es,indent, sum(es,0))  end;
       
   133 
       
   134 fun lst(lp,rp) es =
       
   135   let fun add(e,es) = str"," :: brk 1 :: e :: es;
       
   136       fun list(e :: (es as _::_)) = str lp :: e :: foldr add (es,[str rp])
       
   137         | list(e::[]) = [str lp, e, str rp]
       
   138         | list([]) = []
       
   139   in blk(size lp, list es) end;
       
   140 
       
   141 fun quote prt = blk (1, [str "\"", prt, str "\""]);
       
   142 
       
   143 
       
   144 (*** Pretty printing with depth limitation ***)
       
   145 
       
   146 val depth       = ref 0;        (*maximum depth; 0 means no limit*)
       
   147 
       
   148 fun setdepth dp = (depth := dp);
       
   149 
       
   150 
       
   151 fun pruning dp (Block(bes,indent,wd)) =
       
   152       if dp>0 then blk(indent, map (pruning(dp-1)) bes)
       
   153               else String"..."
       
   154   | pruning dp e = e;
       
   155 
       
   156 fun prune dp e = if dp>0 then pruning dp e else e;
       
   157 
       
   158 
       
   159 fun string_of e = #tx (format ([prune (!depth) e],0,0) emptytext);
       
   160 
       
   161 
       
   162 fun brk_width (force, wd) = if force andalso wd = 0 then 1 else wd;
       
   163 
       
   164 fun str_of prt =
       
   165   let
       
   166     fun s_of (Block (prts, _, _)) = implode (map s_of prts)
       
   167       | s_of (String s) = s
       
   168       | s_of (Break f_w) = repstring " " (brk_width f_w);
       
   169   in
       
   170     s_of (prune (! depth) prt)
       
   171   end;
       
   172 
       
   173 
       
   174 fun pprint prt (put_str, begin_blk, put_brk, end_blk) =
       
   175   let
       
   176     fun pp (Block (prts, ind, _)) = (begin_blk ind; pp_lst prts; end_blk ())
       
   177       | pp (String s) = put_str s
       
   178       | pp (Break f_w) = put_brk (brk_width f_w)
       
   179     and pp_lst [] = ()
       
   180       | pp_lst (prt :: prts) = (pp prt; pp_lst prts);
       
   181   in
       
   182     pp (prune (! depth) prt)
       
   183   end;
       
   184 
       
   185 
       
   186 (*** Initialization ***)
       
   187 
       
   188 val () = setmargin 80;
       
   189 
       
   190 end;
       
   191