Source file mod.ml

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
open Printf
open Util

type t =
  { name : string
  ; path : string list
  ; types : Type.t list
  ; values : Val.t list
  ; submodules : t StringMap.t
  ; recursive : bool
  ; descr : string option
  }

let module_name s =
  let s =
    let last = String.length s - 1 in
    if s.[0] = '{' && s.[last] = '}' then "By_" ^ String.sub s 1 (last - 1)
    else s in
  s
  |> snake_case
  |> String.capitalize_ascii
  |> String.split_on_char '.'
  |> List.hd

let create ~name
           ?descr
           ?(recursive = false)
           ?(path = [])
           ?(types = [])
           ?(submodules = StringMap.empty)
           ?(values = []) () =
  { name = module_name name
  ; path = List.map module_name path
  ; types
  ; values
  ; submodules
  ; recursive
  ; descr
  }

let empty name ?(recursive = false) ?(path = []) () =
  create ~name ~recursive ~path ()

let with_values name ?(recursive = false) ?(path = []) values =
  create ~name ~recursive ~path ~values ()

let name m = m.name

let submodules m =
  m.submodules
  |> StringMap.bindings
  |> List.map snd

(* Unused values. *)
[@@@ocaml.warning "-32"]

let add_type t m =
  { m with types = t :: m.types }

let add_val v m =
  { m with values = v :: m.values }

let add_types ts m =
  { m with types = m.types @ ts }

let map_submodules f m =
  { m with submodules = StringMap.map f m.submodules }

[@@@end]

let has_submodules m =
  StringMap.is_empty m.submodules

let add_vals vs m =
  { m with values = m.values @ vs }

let add_mod subm m =
  { m with submodules = StringMap.add subm.name subm m.submodules }


let find_submodule name m =
  StringMap.find_opt (module_name name) m.submodules

let iter f m =
  f m;
  StringMap.iter
    (fun _name sub -> f sub)
    m.submodules

let path m =
  m.path

let qualified_name m =
  match m.path with
  | [] -> m.name
  | _p -> sprintf "%s.%s" (String.concat "." m.path) m.name

let qualified_path m =
  m.path @ [m.name]

let has_type_named n m =
  List.exists (fun t -> Type.name t = n) m.types

let object_module_val ?(indent = 0) () =
  let pad = String.make indent ' ' in
  "\n" ^ pad ^ "module Object : Object.S with type value := t\n"

let object_module_impl ?(indent = 0) () =
  let pad = String.make indent ' ' in
  "\n" ^ pad ^ "module Object = Object.Make (struct type value = t [@@deriving yojson] end)\n"

let rec sig_to_string ?(indent = 0) m =
  let pad = String.make indent ' ' in
  let doc =
    match m.descr with
    | Some d -> pad ^ sprintf "(** %s *)\n" (format_comment d)
    | None -> "" in
  let submods =
    m.submodules
    |> StringMap.bindings
    |> List.fold_left
         (fun acc (name, m) ->
           let s = sig_to_string ~indent:(indent + 2) m in
           (* Definitions first to simplify references *)
           if name = "Definitions" then s ^ acc
           else acc ^ s)
         "" in
  let indent = indent + 2 in
  sprintf "\n%s%smodule%s%s : sig\n%s%s\n%s%s%send\n"
    doc
    pad
    (if m.recursive then " rec " else " ")
    m.name
    submods
    (String.concat "\n\n"
      (List.map
        (fun t -> Type.Sig.to_string ~indent (Type.signature t))
        m.types))
    (String.concat "\n"
      (List.map
        (fun v -> Val.Sig.to_string ~indent (Val.signature v))
        m.values))
    (if has_type_named "t" m then object_module_val ~indent () else "")
    pad

let rec impl_to_string ?(indent = 0) m =
  let pad = String.make indent ' ' in
  let submods =
    m.submodules
    |> StringMap.bindings
    |> List.fold_left
         (fun acc (_name, m) ->
           acc ^ impl_to_string ~indent:(indent + 2) m)
         "" in
  let decl =
    if m.recursive
    then ""
    else sprintf "%smodule %s " pad m.name in

  let indent = indent + 2 in
  sprintf "%s= struct\n%s%s\n%s%s%send\n"
    decl
    submods
    (String.concat "\n\n"
      (List.map
        (fun t ->
          Type.Impl.to_string ~indent (Type.implementation t))
        m.types))
    (String.concat "\n"
      (List.map
        (fun v -> Val.Impl.to_string ~indent (Val.implementation v))
        m.values))
    (if has_type_named "t" m then object_module_impl ~indent () else "")
    pad

let to_string ?indent m =
  sprintf "%s %s"
    (sig_to_string ?indent m)
    (impl_to_string ?indent m)

let strip_base base path =
  let plen = String.length path in
  let blen = String.length base in
  if plen >= blen then
    let pref = String.sub path 0 blen in
    if String.lowercase_ascii base = String.lowercase_ascii pref then
      String.sub path blen (plen - blen)
    else
      path
  else
    path

let split_ref ref =
  ref
  |> String.split_on_char '.'
  |> List.filter ((<>)"")
  |> List.map module_name

let reference_module_path ~reference_base ~reference_root ref =
  let path =
    ref
    |> strip_base reference_base
    |> split_ref in
  qualified_name reference_root :: path

let reference_module ~reference_base ~reference_root ref =
  reference_module_path ~reference_base ~reference_root ref
  |> String.concat "."

let reference_type ~reference_base ~reference_root ref =
  reference_module ~reference_base ~reference_root ref ^ ".t"