Commit | Line | Data |
---|---|---|
7f918cf1 CE |
1 | (* Copyright (C) 2009,2014,2017 Matthew Fluet. |
2 | * Copyright (C) 1999-2006 Henry Cejtin, Matthew Fluet, Suresh | |
3 | * Jagannathan, and Stephen Weeks. | |
4 | * | |
5 | * MLton is released under a BSD-style license. | |
6 | * See the file MLton-LICENSE for details. | |
7 | *) | |
8 | ||
9 | structure Layout: LAYOUT = | |
10 | struct | |
11 | ||
12 | structure Out = Outstream0 | |
13 | structure Int = Pervasive.Int | |
14 | val detailed = ref false | |
15 | ||
16 | fun switch {detailed = d,normal = n} x = | |
17 | if !detailed then d x else n x | |
18 | ||
19 | structure String = String0 | |
20 | ||
21 | datatype t = T of {length: int, | |
22 | tree: tree} | |
23 | and tree = | |
24 | Empty | |
25 | | String of string | |
26 | | Sequence of t list | |
27 | | Align of {force: bool, rows: t list} | |
28 | | Indent of t * int | |
29 | | Compact of t | |
30 | ||
31 | fun length (T {length, ...}) = length | |
32 | ||
33 | val empty = T {length = 0, tree = Empty} | |
34 | ||
35 | fun isEmpty (T {length = 0, ...}) = true | |
36 | | isEmpty _ = false | |
37 | ||
38 | fun str s = | |
39 | case s of | |
40 | "" => empty | |
41 | | _ => T {length = String.size s, tree = String s} | |
42 | ||
43 | fun fold (l, b, f) = foldl f b l | |
44 | ||
45 | fun seq ts = | |
46 | let val len = fold (ts, 0, fn (t,n) => n + length t) | |
47 | in case len of | |
48 | 0 => empty | |
49 | | _ => T {length = len, tree = Sequence ts} | |
50 | end | |
51 | ||
52 | local | |
53 | fun make force ts = | |
54 | let | |
55 | fun loop ts = | |
56 | case ts of | |
57 | [] => (ts, 0) | |
58 | | t :: ts => | |
59 | let val (ts, n) = loop ts | |
60 | in case length t of | |
61 | 0 => (ts, n) | |
62 | | n' => (t :: ts, n + n' + 1) | |
63 | end | |
64 | val (ts, len) = loop ts | |
65 | in case len of | |
66 | 0 => empty | |
67 | | _ => T {length = len - 1, tree = Align {force = force, rows = ts}} | |
68 | end | |
69 | in | |
70 | val align = make true | |
71 | val mayAlign = make false | |
72 | end | |
73 | ||
74 | fun indent (t, n) = T {length = length t, tree = Indent (t, n)} | |
75 | ||
76 | fun compact t = T {length = length t, tree = Compact t} | |
77 | ||
78 | fun blanks (n: int): string = | |
79 | String.make (n, #" ") | |
80 | ||
81 | fun outputTree (t, out) = | |
82 | let val print = Out.outputc out | |
83 | fun loop (T {tree, length}) = | |
84 | (print "(length " | |
85 | ; print (Int.toString length) | |
86 | ; print ")" | |
87 | ; (case tree of | |
88 | Empty => print "Empty" | |
89 | | String s => (print "(String "; print s; print ")") | |
90 | | Sequence ts => loops ("Sequence", ts) | |
91 | | Align {rows, ...} => loops ("Align", rows) | |
92 | | Indent (t, n) => (print "(Indent " | |
93 | ; print (Int.toString n) | |
94 | ; print " " | |
95 | ; loop t | |
96 | ; print ")") | |
97 | | Compact t => (print "(Compact " | |
98 | ; loop t | |
99 | ; print ")"))) | |
100 | and loops (s, ts) = (print "(" | |
101 | ; print s | |
102 | ; app (fn t => (print " " ; loop t)) ts | |
103 | ; print ")") | |
104 | in loop t | |
105 | end | |
106 | ||
107 | fun toString t = | |
108 | let | |
109 | fun loop (T {tree, ...}, accum) = | |
110 | case tree of | |
111 | Empty => accum | |
112 | | String s => s :: accum | |
113 | | Sequence ts => fold (ts, accum, loop) | |
114 | | Align {rows, ...} => | |
115 | (case rows of | |
116 | [] => accum | |
117 | | t :: ts => | |
118 | fold (ts, loop (t, accum), fn (t, ac) => | |
119 | loop (t, " " :: ac))) | |
120 | | Indent (t, _) => loop (t, accum) | |
121 | | Compact t => loop (t, accum) | |
122 | in | |
123 | String.concat (rev (loop (t, []))) | |
124 | end | |
125 | ||
126 | fun print {tree: t, | |
127 | print: string -> unit, | |
128 | lineWidth: int} = | |
129 | let | |
130 | (*val _ = outputTree (t, out)*) | |
131 | fun newline () = print "\n" | |
132 | ||
133 | fun outputCompact (t, {at, printAt = _}) = | |
134 | let | |
135 | fun loop (T {tree, ...}) = | |
136 | case tree of | |
137 | Empty => () | |
138 | | String s => print s | |
139 | | Sequence ts => app loop ts | |
140 | | Align {rows, ...} => | |
141 | (case rows of | |
142 | [] => () | |
143 | | t :: ts => (loop t | |
144 | ; app (fn t => (print " "; loop t)) ts)) | |
145 | | Indent (t, _) => loop t | |
146 | | Compact t => loop t | |
147 | val at = at + length t | |
148 | in loop t | |
149 | ; {at = at, printAt = at} | |
150 | end | |
151 | ||
152 | fun loop (t as T {length, tree}, state as {at, printAt}) = | |
153 | let | |
154 | fun prePrint () = | |
155 | if at >= printAt | |
156 | then () (* can't back up *) | |
157 | else print (blanks (printAt - at)) | |
158 | in (*Out.print (concat ["at ", Int.toString at, | |
159 | * " printAt ", Int.toString printAt, | |
160 | * "\n"]); | |
161 | *) | |
162 | (*outputTree (t, Out.error)*) | |
163 | case tree of | |
164 | Empty => state | |
165 | | String s => | |
166 | (prePrint () | |
167 | ; print s | |
168 | ; let val at = printAt + length | |
169 | in {at = at, printAt = at} | |
170 | end) | |
171 | | Sequence ts => fold (ts, state, loop) | |
172 | | Align {force, rows} => | |
173 | if not force andalso printAt + length <= lineWidth | |
174 | then (prePrint () | |
175 | ; outputCompact (t, state)) | |
176 | else (case rows of | |
177 | [] => state | |
178 | | t :: ts => | |
179 | fold | |
180 | (ts, loop (t, state), fn (t, _) => | |
181 | (newline () | |
182 | ; loop (t, {at = 0, printAt = printAt})))) | |
183 | | Indent (t, n) => loop (t, {at = at, printAt = printAt + n}) | |
184 | | Compact t => (prePrint () | |
185 | ; outputCompact (t, state)) | |
186 | ||
187 | end | |
188 | in ignore (loop (tree, {at = 0, printAt = 0})) | |
189 | end | |
190 | ||
191 | fun outputWidth (t, width, out) = | |
192 | print {tree = t, | |
193 | lineWidth = width, | |
194 | print = Out.outputc out} | |
195 | ||
196 | local | |
197 | val defaultWidth: int ref = ref 80 | |
198 | in | |
199 | fun setDefaultWidth w = defaultWidth := w | |
200 | fun output (t, out) = outputWidth (t, !defaultWidth, out) | |
201 | val print = | |
202 | fn (t, p) => print {tree = t, lineWidth = !defaultWidth, print = p} | |
203 | end | |
204 | ||
205 | fun outputl (t, out) = (output (t, out); Out.newline out) | |
206 | ||
207 | fun makeOutput layoutX (x, out) = output (layoutX x, out) | |
208 | ||
209 | fun ignore _ = empty | |
210 | ||
211 | fun separate (ts, s) = | |
212 | case ts of | |
213 | [] => [] | |
214 | | t :: ts => t :: (let val s = str s | |
215 | fun loop [] = [] | |
216 | | loop (t :: ts) = s :: t:: (loop ts) | |
217 | in loop ts | |
218 | end) | |
219 | ||
220 | fun separateLeft (ts, s) = | |
221 | case ts of | |
222 | [] => [] | |
223 | | [_] => ts | |
224 | | t :: ts => t :: (map (fn t => seq [str s, t]) ts) | |
225 | ||
226 | fun separateRight (ts, s) = | |
227 | rev (let val ts = rev ts | |
228 | in case ts of | |
229 | [] => [] | |
230 | | [_] => ts | |
231 | | t :: ts => t :: (map (fn t => seq [t, str s]) ts) | |
232 | end) | |
233 | ||
234 | fun alignPrefix (ts, prefix) = | |
235 | case ts of | |
236 | [] => empty | |
237 | | t :: ts => | |
238 | mayAlign (t::(map (fn t => indent (seq [str prefix, t], ~ (String.size prefix))) ts)) | |
239 | ||
240 | local | |
241 | fun fillAux ts = | |
242 | case ts of | |
243 | [] => [] | |
244 | | [t] => [t] | |
245 | | t1::t2::ts => mayAlign [t1, t2] :: fillAux ts | |
246 | in | |
247 | fun fill ts = | |
248 | case ts of | |
249 | [] => empty | |
250 | | [t] => t | |
251 | | _ => fill (fillAux ts) | |
252 | end | |
253 | ||
254 | local | |
255 | fun sequence (start, finish, sep) ts = | |
256 | seq [str start, mayAlign (separateRight (ts, sep)), str finish] | |
257 | in | |
258 | val list = sequence ("[", "]", ",") | |
259 | val schemeList = sequence ("(", ")", " ") | |
260 | val tuple = sequence ("(", ")", ",") | |
261 | fun record fts = | |
262 | sequence ("{", "}", ",") | |
263 | (map (fn (f, t) => seq [str (f ^ " = "), t]) fts) | |
264 | fun vector v = | |
265 | sequence ("#[", "]", ",") | |
266 | (Pervasive.Vector.foldr (op ::) [] v) | |
267 | fun array v = | |
268 | sequence ("#![", "]", ",") | |
269 | (Pervasive.Array.foldr (op ::) [] v) | |
270 | end | |
271 | ||
272 | fun namedRecord (name, fields) = seq [str name, str " ", record fields] | |
273 | ||
274 | fun paren t = seq [str "(", t, str ")"] | |
275 | ||
276 | fun tuple2 (l1, l2) (x1, x2) = tuple [l1 x1, l2 x2] | |
277 | fun tuple3 (l1, l2, l3) (x1, x2, x3) = tuple [l1 x1, l2 x2, l3 x3] | |
278 | fun tuple4 (l1, l2, l3, l4) (x1, x2, x3, x4) = tuple [l1 x1, l2 x2, l3 x3, l4 x4] | |
279 | fun tuple5 (l1, l2, l3, l4, l5) (x1, x2, x3, x4, x5) = | |
280 | tuple [l1 x1, l2 x2, l3 x3, l4 x4, l5 x5] | |
281 | ||
282 | end |