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
[@@@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
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"