File: table.ml

package info (click to toggle)
coq 8.16.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 40,596 kB
  • sloc: ml: 219,376; sh: 3,545; python: 3,231; ansic: 2,529; makefile: 767; lisp: 279; javascript: 63; xml: 24; sed: 2
file content (160 lines) | stat: -rw-r--r-- 4,953 bytes parent folder | download | duplicates (2)
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
type header = string

type row = string list list

module Align = struct
  type t =
    | Left
    | MidLeft
    | Middle
    | MidRight
    | Right
end

let rec map3 f l1 l2 l3 =
  match (l1, l2, l3) with
  | ([], [], []) -> []
  | (a1::l1, a2::l2, a3::l3) -> let r = f a1 a2 a3 in r :: map3 f l1 l2 l3
  | (_, _, _) -> invalid_arg "Table.map3"

let val_padding = 2
(* Padding between data in the same row *)
let row_padding = 1
(* Padding between rows *)

let homogeneous b = if b then () else failwith "Heterogeneous data"

let vert_split (ls : 'a list list) =
  let split l = match l with
  | [] -> failwith "vert_split"
  | x :: l -> (x, l)
  in
  let ls = List.map split ls in
  List.split ls

let justify align n s =
  let len = String.length s in
  let () = assert (len <= n) in
  let pad = n - len in
  match align with
  | Align.Left -> s ^ String.make pad ' '
  | Align.Right -> String.make pad ' ' ^ s
  | Align.Middle ->
    let pad = pad / 2 in
    String.make pad ' ' ^ s ^ String.make (n - pad - len) ' '
  | Align.MidLeft ->
    let pad = pad / 5 in
    String.make pad ' ' ^ s ^ String.make (n - pad - len) ' '
  | Align.MidRight ->
      let pad = pad / 5 in
      String.make (n - pad - len) ' ' ^ s ^ String.make pad ' '

let justify_row align_row layout data =
  let data = map3 justify align_row layout data in
  String.concat (String.make val_padding ' ') data

let angle hkind vkind = match hkind, vkind with
| `Lft, `Top -> "┌"
| `Rgt, `Top -> "┐"
| `Mid, `Top -> "┬"
| `Lft, `Mid -> "├"
| `Rgt, `Mid -> "┤"
| `Mid, `Mid -> "┼"
| `Lft, `Bot -> "└"
| `Rgt, `Bot -> "┘"
| `Mid, `Bot -> "┴"

let print_separator vkind col_size =
  let rec dashes n = if n = 0 then "" else "─" ^ dashes (n - 1) in
  let len = List.length col_size in
  let pad = dashes row_padding in
  let () = assert (0 < len) in
  let map n = dashes n in
  angle `Lft vkind ^ pad ^
  String.concat (pad ^ angle `Mid vkind ^ pad) (List.map map col_size) ^
  pad ^ angle `Rgt vkind

let print_blank col_size =
  let len = List.length col_size in
  let () = assert (0 < len) in
  let pad = String.make row_padding ' ' in
  let map n = String.make n ' ' in
  "│" ^ pad ^ String.concat (pad ^ "│" ^ pad) (List.map map col_size) ^ pad ^ "│"

let print_row row =
  let len = List.length row in
  let () = assert (0 < len) in
  let pad = String.make row_padding ' ' in
  "│" ^ pad ^ String.concat (pad ^ "│" ^ pad) row ^ pad ^ "│"

let default_align_headers = List.map (fun _ -> Align.Middle)
let default_align_top = List.map @@ List.map (fun _ -> Align.Middle)
let default_align_rows rows =
  List.hd rows
  |> List.map @@ List.map (fun _ -> Align.Right)

(* Invariant : all rows must have the same shape *)

let print (headers : header list) (top : row) (rows : row list)
  ?(align_headers = default_align_headers headers)
  ?(align_top = default_align_top top)
  ?(align_rows = default_align_rows rows)
  () =
  (* Sanitize input *)
  let ncolums = List.length headers in
  let shape = ref None in
  let check row =
    let () = homogeneous (List.length row = ncolums) in
    let rshape : int list = List.map (fun data -> List.length data) row in
    match !shape with
    | None -> shape := Some rshape
    | Some s -> homogeneous (rshape = s)
  in
  let () = List.iter check rows in
  (* TODO: check is broken please fix *)
  (* let () = List.iter check (List.map (List.map (fun _ -> [])) align_rows) in *)
  let () = homogeneous (List.length align_headers = ncolums) in
  (* Compute layout *)
  let rec layout n (rows : row list) =
    if n = 0 then []
    else
      let (col, rows) = vert_split rows in
      let ans = layout (n - 1) rows in
      let data = ref None in
      let iter args =
        let size = List.map String.length args in
        match !data with
        | None -> data := Some size
        | Some s ->
          data := Some (List.map2 (fun len1 len2 -> max len1 len2) s size)
      in
      let () = List.iter iter col in
      let data = match !data with None -> [] | Some s -> s in
      data :: ans
  in
  let layout = layout ncolums (top::rows) in
  let map hd shape =
    let data_size = match shape with
    | [] -> 0
    | n :: shape -> List.fold_left (fun accu n -> accu + n + val_padding) n shape
    in
    max (String.length hd) data_size
  in
  let col_size = List.map2 map headers layout in
  (* Justify the data *)
  let headers = map3 justify align_headers col_size headers in
  let top = List.map2 (justify Align.Middle) col_size (map3 justify_row align_top layout top) in

  let rows = List.map (fun row -> List.map2 (justify Align.Right) col_size (map3 justify_row align_rows layout row)) rows in
  (* Print the table *)
  let lines =
    print_separator `Top col_size ::
    print_row headers ::
    print_blank col_size ::
    print_row top ::
    print_separator `Mid col_size ::
    List.map print_row rows @
    print_separator `Bot col_size ::
    []
  in
  String.concat "\n" lines