File: boxes.ml

package info (click to toggle)
hol88 2.02.19940316-28
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 65,924 kB
  • ctags: 21,595
  • sloc: ml: 199,939; ansic: 9,666; sh: 7,118; makefile: 6,075; lisp: 2,747; yacc: 894; sed: 201; cpp: 87; awk: 5
file content (461 lines) | stat: -rw-r--r-- 20,622 bytes parent folder | download | duplicates (11)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
% boxes.ml                                                                    %
%-----------------------------------------------------------------------------%


%  Datatype for boxes of text.  %

%  A box looks like this:                                                     %
%                                                                             %
%         <-io-->@__________________                                          %
%          ______|                  |   |                                     %
%         |                         | height                                  %
%         |                _________|   |                                     %
%         |_______________|             |                                     %
%         <------fo------->                                                   %
%         <----------width---------->                                         %
%                                                                             %

%  `N_box' (null box) is a box with dimensions of length zero.                %
%  `A_box ((width,s),_)' (atomic box) is a box of height 1, width the length  %
%     of the string s (so width is redundant, but useful for efficiency),     %
%     io = 0 and fo = width.                                                  %
%  `L_box ((width,separation,pb1,pb2),_)' (linear box) is a box of height 1,  %
%     io = 0 and fo = width. separation is the number of spaces between the   %
%     back of pb1 and the front of pb2. A linear box is a special case of a   %
%     compound box. The advantage of using a linear box when possible is      %
%     that it takes up less memory.                                           %
%  `C_box (((io,width,fo),height,(x,y),pb1,pb2),_)' (compound box) is a box   %
%     made from two other boxes, pb1 and pb2. The dimensions of the compound  %
%     box are included. (x,y) are the horizontal (to the right) and vertical  %
%     (downwards) offset of pb2 within the compound box. The offsets are      %
%     measured between the origins of the boxes (labelled by @ in the         %
%     diagram). The offsets of pb1 are (0,0).                                 %

rectype * print_box = N_box
                    | A_box of (nat # string) # *
                    | L_box of (nat # nat # * print_box # * print_box) # *
                    | C_box of ((nat # nat # nat) # nat # (int # nat) #
                                   * print_box # * print_box) # *;;


%  Functions to extract the dimensions of a box.  %

let print_box_io pb =

   % : (* print_box -> int) %

   case pb
   of N_box . 0
    | (A_box _) . 0
    | (L_box _) . 0
    | (C_box (((io,_,_),_),_)) . (Int io);;


let print_box_width pb =

   % : (* print_box -> int) %

   case pb
   of N_box . 0
    | (A_box ((width,_),_)) . (Int width)
    | (L_box ((width,_),_)) . (Int width)
    | (C_box (((_,width,_),_),_)) . (Int width);;


let print_box_fo pb =

   % : (* print_box -> int) %

   case pb
   of N_box . 0
    | (A_box ((width,_),_)) . (Int width)
    | (L_box ((width,_),_)) . (Int width)
    | (C_box (((_,_,fo),_),_)) . (Int fo);;


let print_box_height pb =

   % : (* print_box -> int) %

   case pb
   of N_box . 0
    | (A_box _) . 1
    | (L_box _) . 1
    | (C_box ((_,height,_),_)) . (Int height);;


let print_box_sizes pb =

   % : (* print_box -> (int # int # int) # int) %

   case pb
   of N_box . ((0,0,0),0)
    | (A_box ((w,_),_)) . (let w' = Int w in ((0,w',w'),1))
    | (L_box ((w,_),_)) . (let w' = Int w in ((0,w',w'),1))
    | (C_box (((io,w,fo),h,_),_)) . ((Int io,Int w,Int fo),Int h);;


%  Function for changing the `label' of a print_box.  %

let replace_box_label v pb =

   % : (* -> * print_box -> * print_box) %

   case pb
   of N_box . pb
    | (A_box (x,_)) . (A_box (x,v))
    | (L_box (x,_)) . (L_box (x,v))
    | (C_box (x,_)) . (C_box (x,v));;


%  Datatype for indentation values.  %

%  `Abs' is absolute indentation (relative to first sub-box).        %
%  `Inc' is incremental indentation (relative to previous sub-box).  %

type print_indent = Abs of int
                  | Inc of int;;


%  Datatype for boxes ready to be built.  %

%  The boxes are just waiting to be told what horizontal space is available  %
%  to them before taking on their final form.                                %

%  The sub-boxes can be composed horizontally (H), vertically (V),           %
%  horizontal/vertically (HV), or horizontal-or-vertically (HoV).            %

%  Each sub-box is represented by an object of type                          %
%  (int -> int -> * print_box). This is a function which when given the      %
%  current output width and the current indentation from the left margin,    %
%  yields a box.                                                             %

%  The first sub-box is fixed. All the others carry offset information with  %
%  them.                                                                     %

type * unbuilt_box = UB_H of (int -> int -> * print_box) #
                             (nat # (int -> int -> * print_box)) list
                   | UB_V of (int -> int -> * print_box) #
                             ((print_indent # nat) #
                                 (int -> int -> * print_box)) list
                   | UB_HV of (int -> int -> * print_box) #
                              ((nat # print_indent # nat) #
                                  (int -> int -> * print_box)) list
                   | UB_HoV of (int -> int -> * print_box) #
                               ((nat # print_indent # nat) #
                                   (int -> int -> * print_box)) list;;

begin_section boxes;;

%  Function for joining two boxes together.  %

%  `x' and `y' have rather strange definitions which allow the one function   %
%  to be used for joining boxes both horizontally and vertically. Note that   %
%  `join_boxes' does not work properly with boxes of zero height.             %

%  The intermediate values `lo' and `ro' are illustrated (both with positive  %
%  values) in the diagram below:                                              %
%                                                                             %
%                          _______ <-ro->                                     %
%                     ____|  _____|_____                                      %
%                    |______|_|      ___|                                     %
%                        |__________|                                         %
%                    <lo>                                                     %
%                                                                             %
%  The composition of the two boxes looks like this:                          %
%                                                                             %
%                          _____________                                      %
%                     ____|             |                                     %
%                    |               ___|                                     %
%                    |______________|                                         %
%                                                                             %

let join_boxes x y pb1 pb2 v =

   % : (int -> int -> * print_box -> * print_box -> * -> * print_box) %

   let ((io1,w1,fo1),h1) = print_box_sizes pb1
   and ((io2,w2,fo2),h2) = print_box_sizes pb2
   in  let lo = x - io2
       and ro = (w2 - io2) - (w1 - x)
   in  let io = if (lo < 0) then (io1 - lo) else io1
       and w  = if (lo < 0)
                then if (ro < 0) then (w1 - lo) else w2
                else if (ro < 0) then w1 else (w2 + lo)
       and fo = if (lo < 0) then fo2 else (fo2 + lo)
       and h  = h1 + h2 + y
       and x2 = x - io1
       and y2 = h1 + y
   in  if (h = 1)
       then L_box ((Nat w,Nat (x2 - w1),pb1,pb2),v)
       else C_box (((Nat io,Nat w,Nat fo),Nat h,(x2,Nat y2),pb1,pb2),v);;


%  Function to join boxes horizontally with separation `dx'.  %

%  Composition with an `N_box' leaves the other box unchanged.                %

%  Composing two boxes horizontally:                                          %
%                                                                             %
%                           |dx|                                              %
%                          _______                                            %
%                     ____|  _____|_____                                      %
%                    |______|__|     ___|   | -y                              %
%                        |__________|                                         %
%                    <----x---->                                              %
%                                                                             %

let join_H_boxes dx pb1 pb2 v =

   % : (nat -> * print_box -> * print_box -> * -> * print_box) %

   case (pb1,pb2)
   of (N_box,_) . pb2
    | (_,N_box) . pb1
    | (_) . (join_boxes ((print_box_fo pb1) + (Int dx)) (-1) pb1 pb2 v);;


%  Function to join boxes vertically with separation `dh'  %
%  and indentation `di'.                                   %

%  Composition with an `N_box' leaves the other box unchanged.                %

%  Composing two boxes vertically:                                            %
%                                                                             %
%                          _______                                            %
%                     ____|     __|                                           %
%                    |_________|                                              %
%                         <di>_______    | y = dh                             %
%                       _____|     __|                                        %
%                      |__________|                                           %
%                    <---x--->                                                %
%                                                                             %

let join_V_boxes di dh pb1 pb2 v =

   % : (int -> nat -> * print_box -> * print_box -> * -> * print_box) %

   case (pb1,pb2)
   of (N_box,_) . pb2
    | (_,N_box) . pb1
    | (_) . (join_boxes ((print_box_io pb1) + di) (Int dh) pb1 pb2 v);;


%  Function to build a horizontal (H) box.  %

%  The sub-function `gaps' is used to compute the total separation between    %
%  the sub-boxes. To this is added the number of sub-boxes (less the first).  %
%  The available width (m) is then reduced by this total to give the new      %
%  available width (m'). This is an attempt to guess how much space to leave  %
%  on the line for the remainder of the sub-boxes. The effective width of     %
%  each sub-box is assumed to be one. In fact it could be any value, even     %
%  negative. The heuristic seems to work well in practice though, probably    %
%  because most horizontal boxes that are large enough to spread over more    %
%  than one line are of the form parenthesis - large block - parenthesis, or  %
%  in place of the parentheses, some other single character.                  %

%  As each sub-box is built, the gap between it and the previous sub-box      %
%  plus one is added back on to the available width, and the indentation      %
%  from the left margin is changed by the genuine amount. In fact, the        %
%  indentation is computed each time from the original indentation, the       %
%  effective width of the box built so far, and the effective width of the    %
%  latest sub-box.                                                            %

let build_H_box m i v box boxl =

   % : (int -> int -> * -> (int -> int -> * print_box) ->           %
   %       (nat # (int -> int -> * print_box)) list -> * print_box) %

   letrec f pb m' boxl' =

      % : (* print_box -> int ->                                       %
      %       (nat # (int -> int -> * print_box)) list -> * print_box) %

      if (null boxl')
      then pb
      else let (dx,pbfn) = hd boxl'
           in  let m'' = m' + 1 + (Int dx)
               and i' = i + ((print_box_fo pb) - (print_box_io pb)) + (Int dx)
               in  f (join_H_boxes dx pb (pbfn m'' i') v) m'' (tl boxl')

   and gaps boxl' =

      % : ((nat # (int -> int -> * print_box)) list -> int) %

      itlist (\x n. (Int (fst x)) + n) boxl' 0

   in let m' = m - ((gaps boxl) + (length boxl))
      in  f (box m' i) m' boxl;;


%  Function to build a vertical (V) box.  %

%  The sub-boxes are composed vertically. The indentation from the left  %
%  margin is modified as each sub-box is added.                          %

let build_V_box (m:int) i v box boxl =

   % : (int -> int -> * -> (int -> int -> * print_box) ->            %
   %    ((print_indent # nat) # (int -> int -> * print_box)) list -> %
   %    * print_box)                                                 %

   letrec f pb i' boxl' =

      % : (* print_box -> int ->                                        %
      %    ((print_indent # nat) # (int -> int -> * print_box)) list -> %
      %    * print_box)                                                 %

      if (null boxl')
      then pb
      else let ((pi,dh),pbfn) = hd boxl'
           in  let di = case pi
                        of (Abs n) . n
                         | (Inc n) . (n + i' - i)
               in  f (join_V_boxes di dh pb (pbfn m (i + di)) v)
                                                (i + di) (tl boxl')

   in f (box m i) i boxl;;


%  Function to build a horizontal/vertical (HV) box.  %

%  The sub-function `fH' generates a list of boxes to be composed vertically  %
%  where each box has been made up by composing one or more of the original   %
%  sub-boxes horizontally. The list generated is in reverse order and the     %
%  indentations for the vertical composition are offsets from the first       %
%  box. Note that the function used with `itlist' reverses its arguments.     %
%  Consideration of the call to `itlist' should reveal the rather delicate    %
%  nature of the composition occurring. The order in which the composition    %
%  is done is crucially linked with whether the indentations are absolute or  %
%  relative.                                                                  %

%  The sub-function builds horizontal boxes until they are too big, and then  %
%  adds them to a list of boxes built so far. When trying to add a sub-box    %
%  to the current horizontal box, the function evaluates by how much the      %
%  offset from the left margin (i') will be increased if a line-break is not  %
%  used. If this is less than or equal to the increase that will occur with   %
%  a line-break, the sub-box is added to the horizontal box regardless.       %

%  The function uses two criteria for determining when to break. If the new   %
%  box is wider than the available space, a break must occur. There must      %
%  also be a break if the right-hand edge of the box exceeds the right-hand   %
%  margin. The two criteria are not necessarily the same because the          %
%  indentation may force the box further to the right. Since the indentation  %
%  can also be negative, it could pull the box to the left, giving a false    %
%  result. For this reason negative indentations are taken to be zero.        %

%  The vertical composition parameters of the first sub-box of a horizontal   %
%  box are remembered when it is started, so that they become the parameters  %
%  for the composite horizontal box.                                          %

let build_HV_box m i v box boxl =

   % : (int -> int -> * -> (int -> int -> * print_box) ->                  %
   %    ((nat # print_indent # nat) # (int -> int -> * print_box)) list -> %
   %    * print_box)                                                       %

   letrec fH newboxl newbox i' boxl' =

      % : ((int # nat # * print_box) list ->                                  %
      %    (int # nat # * print_box) -> int ->                                %
      %    ((nat # print_indent # nat) # (int -> int -> * print_box)) list -> %
      %    (int # nat # * print_box) list)                                    %

      if (null boxl')
      then newbox.newboxl
      else let ((dx,pi,dh),pbfn) = hd boxl'
           and (newdi,newdh,pb) = newbox
           in  let di = case pi
                        of (Abs n) . n
                         | (Inc n) . (n + i' - i)
               and no_break_indent =
                      (Int dx) + (print_box_fo pb) - (print_box_io pb)
               in  if ((di - (i' - i)) < no_break_indent)
                   then let newb = pbfn m (i + di)
                        in  let newhb = join_H_boxes dx pb newb v
                            in  if (((print_box_width newhb) > m) or
                                    ((print_box_width newhb) -
                                             (print_box_io newhb)
                                        > (m - max [i';0])))
                                then fH (newbox.newboxl) (di,dh,newb)
                                                          (i + di) (tl boxl')
                                else fH newboxl (newdi,newdh,newhb) i'
                                                                   (tl boxl')
                   else let newhb = join_H_boxes dx pb
                                       (pbfn m (i' + no_break_indent)) v
                        in  fH newboxl (newdi,newdh,newhb) i' (tl boxl')

   in let newboxl = fH [] (0,Nat 0,box m i) i boxl
      in  itlist (\(di,dh,pb2) pb1. join_V_boxes di dh pb1 pb2 v) newboxl
                                                                     N_box;;


%  Function to build a horizontal-or-vertical (HoV) box.  %

%  The sub-function `f' computes the indentations for each box and builds     %
%  the sub-boxes under the assumption that each will go on a new line.        %

%  The body of the main function composes the boxes horizontally. If the      %
%  resulting box is too big (see comments for `build_HV_box'), the boxes are  %
%  composed vertically. The narrower of the two compositions is then used.    %
%  See comments for `build_HV_box' regarding use of `itlist' for composing.   %

let build_HoV_box m i v box boxl =

   % : (int -> int -> * -> (int -> int -> * print_box) ->                  %
   %    ((nat # print_indent # nat) # (int -> int -> * print_box)) list -> %
   %    * print_box)                                                       %

   letrec f newboxl i' boxl' =

      % : ((nat # int # nat # * print_box) list -> int ->                     %
      %    ((nat # print_indent # nat) # (int -> int -> * print_box)) list -> %
      %    (nat # int # nat # * print_box) list)                              %

      if (null boxl')
      then newboxl
      else let ((dx,pi,dh),pbfn) = hd boxl'
           in  let di = case pi
                        of (Abs n) . n
                         | (Inc n) . (n + i' - i)
               in  f ((dx,di,dh,pbfn m (i + di)).newboxl) (i + di) (tl boxl')

   in let newb = box m i
      and newboxl = f [] i boxl
      in  let newhb = itlist (\(dx,di,dh,pb2) pb1. join_H_boxes dx pb1 pb2 v)
                                                                  newboxl newb
          in  let hw = print_box_width newhb
              and hio = print_box_io newhb
              in  if ((hw > m) or (hw - hio > (m - max [i;0])))
                  then let newvb =
                          itlist
                           (\(dx,di,dh,pb2) pb1. join_V_boxes di dh pb1 pb2 v)
                                                                   newboxl newb
                       in  let vw = print_box_width newvb
                           and vio = print_box_io newvb
                           in  if ((hw > vw) or (hw - hio > vw - vio))
                               then newvb
                               else newhb
                  else newhb;;


%  Function for building a box.  %

%  The value v is used as the `label' for all sub-boxes constructed.  %

let build_print_box m i v unbox =

   % : (int -> int -> * -> * unbuilt_box -> * print_box) %

   case unbox
   of (UB_H (box,boxl))   . (build_H_box m i v box boxl)
    | (UB_V (box,boxl))   . (build_V_box m i v box boxl)
    | (UB_HV (box,boxl))  . (build_HV_box m i v box boxl)
    | (UB_HoV (box,boxl)) . (build_HoV_box m i v box boxl);;

build_print_box;;
end_section boxes;;
let build_print_box = it;;


%-----------------------------------------------------------------------------%