wenzelm@6118
|
1 |
(* Title: Pure/General/pretty.ML
|
wenzelm@6116
|
2 |
ID: $Id$
|
wenzelm@6116
|
3 |
Author: Lawrence C Paulson
|
wenzelm@6116
|
4 |
Copyright 1991 University of Cambridge
|
wenzelm@6116
|
5 |
|
wenzelm@6116
|
6 |
Generic pretty printing module.
|
wenzelm@6116
|
7 |
|
wenzelm@6116
|
8 |
Loosely based on
|
wenzelm@6116
|
9 |
D. C. Oppen, "Pretty Printing",
|
wenzelm@6116
|
10 |
ACM Transactions on Programming Languages and Systems (1980), 465-483.
|
wenzelm@6116
|
11 |
|
wenzelm@6116
|
12 |
The object to be printed is given as a tree with indentation and line
|
wenzelm@6116
|
13 |
breaking information. A "break" inserts a newline if the text until
|
wenzelm@6116
|
14 |
the next break is too long to fit on the current line. After the newline,
|
wenzelm@6116
|
15 |
text is indented to the level of the enclosing block. Normally, if a block
|
wenzelm@6116
|
16 |
is broken then all enclosing blocks will also be broken. Only "inconsistent
|
wenzelm@6116
|
17 |
breaks" are provided.
|
wenzelm@6116
|
18 |
|
wenzelm@6116
|
19 |
The stored length of a block is used in breakdist (to treat each inner block as
|
wenzelm@6116
|
20 |
a unit for breaking).
|
wenzelm@6116
|
21 |
*)
|
wenzelm@6116
|
22 |
|
wenzelm@6116
|
23 |
type pprint_args = (string -> unit) * (int -> unit) * (int -> unit) *
|
wenzelm@6116
|
24 |
(unit -> unit) * (unit -> unit);
|
wenzelm@6116
|
25 |
|
wenzelm@6116
|
26 |
signature PRETTY =
|
wenzelm@6116
|
27 |
sig
|
wenzelm@6116
|
28 |
type T
|
wenzelm@6116
|
29 |
val str: string -> T
|
wenzelm@6116
|
30 |
val strlen: string -> int -> T
|
wenzelm@6116
|
31 |
val sym: string -> T
|
wenzelm@6116
|
32 |
val spc: int -> T
|
wenzelm@6116
|
33 |
val brk: int -> T
|
wenzelm@6116
|
34 |
val fbrk: T
|
wenzelm@6116
|
35 |
val blk: int * T list -> T
|
wenzelm@6116
|
36 |
val lst: string * string -> T list -> T
|
wenzelm@6116
|
37 |
val quote: T -> T
|
wenzelm@6116
|
38 |
val commas: T list -> T list
|
wenzelm@6116
|
39 |
val breaks: T list -> T list
|
wenzelm@6116
|
40 |
val fbreaks: T list -> T list
|
wenzelm@6116
|
41 |
val block: T list -> T
|
wenzelm@6116
|
42 |
val strs: string list -> T
|
wenzelm@6116
|
43 |
val enclose: string -> string -> T list -> T
|
wenzelm@6116
|
44 |
val list: string -> string -> T list -> T
|
wenzelm@6116
|
45 |
val str_list: string -> string -> string list -> T
|
wenzelm@6116
|
46 |
val big_list: string -> T list -> T
|
wenzelm@6116
|
47 |
val string_of: T -> string
|
wenzelm@6116
|
48 |
val writeln: T -> unit
|
wenzelm@6116
|
49 |
val str_of: T -> string
|
wenzelm@6116
|
50 |
val pprint: T -> pprint_args -> unit
|
wenzelm@6116
|
51 |
val setdepth: int -> unit
|
wenzelm@6116
|
52 |
val setmargin: int -> unit
|
wenzelm@6116
|
53 |
end;
|
wenzelm@6116
|
54 |
|
wenzelm@6116
|
55 |
structure Pretty : PRETTY =
|
wenzelm@6116
|
56 |
struct
|
wenzelm@6116
|
57 |
|
wenzelm@6116
|
58 |
(*printing items: compound phrases, strings, and breaks*)
|
wenzelm@6116
|
59 |
datatype T =
|
wenzelm@6116
|
60 |
Block of T list * int * int | (*body, indentation, length*)
|
wenzelm@6116
|
61 |
String of string * int | (*text, length*)
|
wenzelm@6116
|
62 |
Break of bool * int; (*mandatory flag, width if not taken*);
|
wenzelm@6116
|
63 |
|
wenzelm@6116
|
64 |
(*Add the lengths of the expressions until the next Break; if no Break then
|
wenzelm@6116
|
65 |
include "after", to account for text following this block. *)
|
wenzelm@6116
|
66 |
fun breakdist (Block(_,_,len)::es, after) = len + breakdist(es, after)
|
wenzelm@6116
|
67 |
| breakdist (String (s, len) :: es, after) = len + breakdist (es, after)
|
wenzelm@6116
|
68 |
| breakdist (Break _ :: es, after) = 0
|
wenzelm@6116
|
69 |
| breakdist ([], after) = after;
|
wenzelm@6116
|
70 |
|
wenzelm@6116
|
71 |
fun repstring a 0 = ""
|
wenzelm@6116
|
72 |
| repstring a 1 = a
|
wenzelm@6116
|
73 |
| repstring a k =
|
wenzelm@6116
|
74 |
if k mod 2 = 0 then repstring(a^a) (k div 2)
|
wenzelm@6116
|
75 |
else repstring(a^a) (k div 2) ^ a;
|
wenzelm@6116
|
76 |
|
wenzelm@6116
|
77 |
(*** Type for lines of text: string, # of lines, position on line ***)
|
wenzelm@6116
|
78 |
|
wenzelm@6116
|
79 |
type text = {tx: string, nl: int, pos: int};
|
wenzelm@6116
|
80 |
|
wenzelm@6116
|
81 |
val emptytext = {tx="", nl=0, pos=0};
|
wenzelm@6116
|
82 |
|
wenzelm@6116
|
83 |
fun blanks wd {tx,nl,pos} =
|
wenzelm@6116
|
84 |
{tx = tx ^ repstring " " wd,
|
wenzelm@6116
|
85 |
nl = nl,
|
wenzelm@6116
|
86 |
pos = pos+wd};
|
wenzelm@6116
|
87 |
|
wenzelm@6116
|
88 |
fun newline {tx,nl,pos} =
|
wenzelm@6116
|
89 |
{tx = tx ^ "\n",
|
wenzelm@6116
|
90 |
nl = nl+1,
|
wenzelm@6116
|
91 |
pos = 0};
|
wenzelm@6116
|
92 |
|
wenzelm@6116
|
93 |
fun string s len {tx,nl,pos:int} =
|
wenzelm@6116
|
94 |
{tx = tx ^ s,
|
wenzelm@6116
|
95 |
nl = nl,
|
wenzelm@6116
|
96 |
pos = pos + len};
|
wenzelm@6116
|
97 |
|
wenzelm@6116
|
98 |
|
wenzelm@6116
|
99 |
(*** Formatting ***)
|
wenzelm@6116
|
100 |
|
wenzelm@6116
|
101 |
(* margin *)
|
wenzelm@6116
|
102 |
|
wenzelm@6116
|
103 |
(*example values*)
|
wenzelm@6116
|
104 |
val margin = ref 80 (*right margin, or page width*)
|
wenzelm@6116
|
105 |
and breakgain = ref 4 (*minimum added space required of a break*)
|
wenzelm@6116
|
106 |
and emergencypos = ref 40; (*position too far to right*)
|
wenzelm@6116
|
107 |
|
wenzelm@6116
|
108 |
fun setmargin m =
|
wenzelm@6116
|
109 |
(margin := m;
|
wenzelm@6116
|
110 |
breakgain := !margin div 20;
|
wenzelm@6116
|
111 |
emergencypos := !margin div 2);
|
wenzelm@6116
|
112 |
|
wenzelm@6116
|
113 |
val () = setmargin 76;
|
wenzelm@6116
|
114 |
|
wenzelm@6116
|
115 |
|
wenzelm@6116
|
116 |
(*Search for the next break (at this or higher levels) and force it to occur*)
|
wenzelm@6116
|
117 |
fun forcenext [] = []
|
wenzelm@6116
|
118 |
| forcenext (Break(_,wd) :: es) = Break(true,0) :: es
|
wenzelm@6116
|
119 |
| forcenext (e :: es) = e :: forcenext es;
|
wenzelm@6116
|
120 |
|
wenzelm@6116
|
121 |
(*es is list of expressions to print;
|
wenzelm@6116
|
122 |
blockin is the indentation of the current block;
|
wenzelm@6116
|
123 |
after is the width of the following context until next break. *)
|
wenzelm@6116
|
124 |
fun format ([], _, _) text = text
|
wenzelm@6116
|
125 |
| format (e::es, blockin, after) (text as {pos,nl,...}) =
|
wenzelm@6116
|
126 |
(case e of
|
wenzelm@6116
|
127 |
Block(bes,indent,wd) =>
|
wenzelm@6116
|
128 |
let val blockin' = (pos + indent) mod !emergencypos
|
wenzelm@6116
|
129 |
val btext = format(bes, blockin', breakdist(es,after)) text
|
wenzelm@6116
|
130 |
(*If this block was broken then force the next break.*)
|
wenzelm@6116
|
131 |
val es2 = if nl < #nl(btext) then forcenext es else es
|
wenzelm@6116
|
132 |
in format (es2,blockin,after) btext end
|
wenzelm@6116
|
133 |
| String (s, len) => format (es,blockin,after) (string s len text)
|
wenzelm@6116
|
134 |
| Break(force,wd) => (*no break if text to next break fits on this line
|
wenzelm@6116
|
135 |
or if breaking would add only breakgain to space *)
|
wenzelm@6116
|
136 |
format (es,blockin,after)
|
wenzelm@6116
|
137 |
(if not force andalso
|
wenzelm@6116
|
138 |
pos+wd <= Int.max(!margin - breakdist(es,after),
|
wenzelm@6116
|
139 |
blockin + !breakgain)
|
wenzelm@6116
|
140 |
then blanks wd text (*just insert wd blanks*)
|
wenzelm@6116
|
141 |
else blanks blockin (newline text)));
|
wenzelm@6116
|
142 |
|
wenzelm@6116
|
143 |
|
wenzelm@6116
|
144 |
(*** Exported functions to create formatting expressions ***)
|
wenzelm@6116
|
145 |
|
wenzelm@6116
|
146 |
fun length (Block (_, _, len)) = len
|
wenzelm@6116
|
147 |
| length (String (_, len)) = len
|
wenzelm@6116
|
148 |
| length (Break(_, wd)) = wd;
|
wenzelm@6116
|
149 |
|
wenzelm@6116
|
150 |
fun str s = String (s, size s);
|
wenzelm@6116
|
151 |
fun strlen s len = String (s, len);
|
wenzelm@6271
|
152 |
val sym = String o apsnd Real.round o Symbol.output_width;
|
wenzelm@6116
|
153 |
|
wenzelm@6116
|
154 |
fun spc n = str (repstring " " n);
|
wenzelm@6116
|
155 |
|
wenzelm@6116
|
156 |
fun brk wd = Break (false, wd);
|
wenzelm@6116
|
157 |
val fbrk = Break (true, 0);
|
wenzelm@6116
|
158 |
|
wenzelm@6116
|
159 |
fun blk (indent, es) =
|
wenzelm@6116
|
160 |
let
|
wenzelm@6116
|
161 |
fun sum([], k) = k
|
wenzelm@6116
|
162 |
| sum(e :: es, k) = sum (es, length e + k);
|
wenzelm@6116
|
163 |
in Block (es, indent, sum (es, 0)) end;
|
wenzelm@6116
|
164 |
|
wenzelm@6116
|
165 |
(*Join the elements of es as a comma-separated list, bracketted by lp and rp*)
|
wenzelm@6116
|
166 |
fun lst(lp,rp) es =
|
wenzelm@6116
|
167 |
let fun add(e,es) = str"," :: brk 1 :: e :: es;
|
wenzelm@6116
|
168 |
fun list(e :: (es as _::_)) = str lp :: e :: foldr add (es,[str rp])
|
wenzelm@6116
|
169 |
| list(e::[]) = [str lp, e, str rp]
|
wenzelm@6116
|
170 |
| list([]) = []
|
wenzelm@6116
|
171 |
in blk(size lp, list es) end;
|
wenzelm@6116
|
172 |
|
wenzelm@6116
|
173 |
|
wenzelm@6116
|
174 |
(* utils *)
|
wenzelm@6116
|
175 |
|
wenzelm@6116
|
176 |
fun quote prt =
|
wenzelm@6116
|
177 |
blk (1, [str "\"", prt, str "\""]);
|
wenzelm@6116
|
178 |
|
wenzelm@6116
|
179 |
fun commas prts =
|
wenzelm@6116
|
180 |
flat (separate [str ",", brk 1] (map (fn x => [x]) prts));
|
wenzelm@6116
|
181 |
|
wenzelm@6116
|
182 |
fun breaks prts = separate (brk 1) prts;
|
wenzelm@6116
|
183 |
|
wenzelm@6116
|
184 |
fun fbreaks prts = separate fbrk prts;
|
wenzelm@6116
|
185 |
|
wenzelm@6116
|
186 |
fun block prts = blk (2, prts);
|
wenzelm@6116
|
187 |
|
wenzelm@6116
|
188 |
val strs = block o breaks o (map str);
|
wenzelm@6116
|
189 |
|
wenzelm@6116
|
190 |
fun enclose lpar rpar prts =
|
wenzelm@6116
|
191 |
block (str lpar :: (prts @ [str rpar]));
|
wenzelm@6116
|
192 |
|
wenzelm@6116
|
193 |
fun list lpar rpar prts =
|
wenzelm@6116
|
194 |
enclose lpar rpar (commas prts);
|
wenzelm@6116
|
195 |
|
wenzelm@6116
|
196 |
fun str_list lpar rpar strs =
|
wenzelm@6116
|
197 |
list lpar rpar (map str strs);
|
wenzelm@6116
|
198 |
|
wenzelm@6116
|
199 |
fun big_list name prts =
|
wenzelm@6116
|
200 |
block (fbreaks (str name :: prts));
|
wenzelm@6116
|
201 |
|
wenzelm@6116
|
202 |
|
wenzelm@6116
|
203 |
|
wenzelm@6116
|
204 |
(*** Pretty printing with depth limitation ***)
|
wenzelm@6116
|
205 |
|
wenzelm@6116
|
206 |
val depth = ref 0; (*maximum depth; 0 means no limit*)
|
wenzelm@6116
|
207 |
|
wenzelm@6116
|
208 |
fun setdepth dp = (depth := dp);
|
wenzelm@6116
|
209 |
|
wenzelm@6116
|
210 |
(*Recursively prune blocks, discarding all text exceeding depth dp*)
|
wenzelm@6116
|
211 |
fun pruning dp (Block(bes,indent,wd)) =
|
wenzelm@6116
|
212 |
if dp>0 then blk(indent, map (pruning(dp-1)) bes)
|
wenzelm@6116
|
213 |
else str "..."
|
wenzelm@6116
|
214 |
| pruning dp e = e;
|
wenzelm@6116
|
215 |
|
wenzelm@6116
|
216 |
fun prune dp e = if dp>0 then pruning dp e else e;
|
wenzelm@6116
|
217 |
|
wenzelm@6116
|
218 |
|
wenzelm@6116
|
219 |
fun string_of e = #tx (format ([prune (!depth) e], 0, 0) emptytext);
|
wenzelm@6116
|
220 |
|
wenzelm@6116
|
221 |
val writeln = writeln o string_of;
|
wenzelm@6116
|
222 |
|
wenzelm@6116
|
223 |
|
wenzelm@6116
|
224 |
(*Create a single flat string: no line breaking*)
|
wenzelm@6116
|
225 |
fun str_of prt =
|
wenzelm@6116
|
226 |
let
|
wenzelm@6116
|
227 |
fun s_of (Block (prts, _, _)) = implode (map s_of prts)
|
wenzelm@6116
|
228 |
| s_of (String (s, _)) = s
|
wenzelm@6116
|
229 |
| s_of (Break (false, wd)) = repstring " " wd
|
wenzelm@6116
|
230 |
| s_of (Break (true, _)) = " ";
|
wenzelm@6116
|
231 |
in
|
wenzelm@6116
|
232 |
s_of (prune (! depth) prt)
|
wenzelm@6116
|
233 |
end;
|
wenzelm@6116
|
234 |
|
wenzelm@6116
|
235 |
(*Part of the interface to the Poly/ML and New Jersey ML pretty printers*)
|
wenzelm@6116
|
236 |
fun pprint prt (put_str, begin_blk, put_brk, put_fbrk, end_blk) =
|
wenzelm@6116
|
237 |
let
|
wenzelm@6116
|
238 |
fun pp (Block (prts, ind, _)) = (begin_blk ind; pp_lst prts; end_blk ())
|
wenzelm@6116
|
239 |
| pp (String (s, _)) = put_str s
|
wenzelm@6116
|
240 |
| pp (Break (false, wd)) = put_brk wd
|
wenzelm@6116
|
241 |
| pp (Break (true, _)) = put_fbrk ()
|
wenzelm@6116
|
242 |
and pp_lst [] = ()
|
wenzelm@6116
|
243 |
| pp_lst (prt :: prts) = (pp prt; pp_lst prts);
|
wenzelm@6116
|
244 |
in
|
wenzelm@6116
|
245 |
pp (prune (! depth) prt)
|
wenzelm@6116
|
246 |
end;
|
wenzelm@6116
|
247 |
|
wenzelm@6116
|
248 |
|
wenzelm@6116
|
249 |
end;
|