0
|
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 |
|