Import Upstream version 20180207
[hcoop/debian/mlton.git] / lib / mlton / basic / layout.sml
CommitLineData
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
9structure Layout: LAYOUT =
10struct
11
12structure Out = Outstream0
13structure Int = Pervasive.Int
14val detailed = ref false
15
16fun switch {detailed = d,normal = n} x =
17 if !detailed then d x else n x
18
19structure String = String0
20
21datatype t = T of {length: int,
22 tree: tree}
23and 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
31fun length (T {length, ...}) = length
32
33val empty = T {length = 0, tree = Empty}
34
35fun isEmpty (T {length = 0, ...}) = true
36 | isEmpty _ = false
37
38fun str s =
39 case s of
40 "" => empty
41 | _ => T {length = String.size s, tree = String s}
42
43fun fold (l, b, f) = foldl f b l
44
45fun 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
52local
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
69in
70 val align = make true
71 val mayAlign = make false
72end
73
74fun indent (t, n) = T {length = length t, tree = Indent (t, n)}
75
76fun compact t = T {length = length t, tree = Compact t}
77
78fun blanks (n: int): string =
79 String.make (n, #" ")
80
81fun 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
107fun 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
126fun 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
191fun outputWidth (t, width, out) =
192 print {tree = t,
193 lineWidth = width,
194 print = Out.outputc out}
195
196local
197 val defaultWidth: int ref = ref 80
198in
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}
203end
204
205fun outputl (t, out) = (output (t, out); Out.newline out)
206
207fun makeOutput layoutX (x, out) = output (layoutX x, out)
208
209fun ignore _ = empty
210
211fun 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
220fun separateLeft (ts, s) =
221 case ts of
222 [] => []
223 | [_] => ts
224 | t :: ts => t :: (map (fn t => seq [str s, t]) ts)
225
226fun 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
234fun 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
240local
241 fun fillAux ts =
242 case ts of
243 [] => []
244 | [t] => [t]
245 | t1::t2::ts => mayAlign [t1, t2] :: fillAux ts
246in
247 fun fill ts =
248 case ts of
249 [] => empty
250 | [t] => t
251 | _ => fill (fillAux ts)
252end
253
254local
255 fun sequence (start, finish, sep) ts =
256 seq [str start, mayAlign (separateRight (ts, sep)), str finish]
257in
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)
270end
271
272fun namedRecord (name, fields) = seq [str name, str " ", record fields]
273
274fun paren t = seq [str "(", t, str ")"]
275
276fun tuple2 (l1, l2) (x1, x2) = tuple [l1 x1, l2 x2]
277fun tuple3 (l1, l2, l3) (x1, x2, x3) = tuple [l1 x1, l2 x2, l3 x3]
278fun tuple4 (l1, l2, l3, l4) (x1, x2, x3, x4) = tuple [l1 x1, l2 x2, l3 x3, l4 x4]
279fun tuple5 (l1, l2, l3, l4, l5) (x1, x2, x3, x4, x5) =
280 tuple [l1 x1, l2 x2, l3 x3, l4 x4, l5 x5]
281
282end