/
Rep.ml
346 lines (316 loc) · 10.5 KB
/
Rep.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
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
open Repr
(** Reps of Asai types *)
let string_source : Range.string_source t =
let open Range in
record "string_source" (fun title content -> { title; content })
|+ field "title" (option string) (fun s -> s.title)
|+ field "content" string (fun s -> s.content)
|> sealr
let source_repr : Range.source ty =
let open Range in
variant "source" (fun file string -> function
| `File s -> file s | `String s -> string s)
|~ case1 "File" string (fun s -> `File s)
|~ case1 "String" string_source (fun s -> `String s)
|> sealv
let position : Range.position ty =
let open Range in
record "position" (fun source offset start_of_line line_num ->
{ source; offset; start_of_line; line_num })
|+ field "source" source_repr (fun t -> t.source)
|+ field "offset" int (fun t -> t.offset)
|+ field "start_of_line" int (fun t -> t.offset)
|+ field "line_num" int (fun t -> t.offset)
|> sealr
let range : Range.t ty =
(* NOTE:
For the irmin-git backend, the functions we need are pp, of_string and
equal. I've worked around the need for a full of_string implementation
(parser), since in `located_sem_node` I am simply returning `None` for the
value of `loc`. This means even though we can serialize ranges, we can't
retrieve them. This is fine for now, since we don't need that info for
rendering, which is our primary use case.
*)
let open Range in
let pp = Range.dump in
let pos =
{ source = `File "todo"; offset = 0; start_of_line = 0; line_num = 0 }
in
let of_string str =
(* HACK: Should parse this kind of string (produced by Range.dump):
Range
({source=(`File "todo"); offset=0; start_of_line=0; line_num=0},
{source=(`File "todo"); offset=0; start_of_line=0; line_num=0})
*)
Ok (Range.make (pos, pos))
in
let r = (Range.make (pos, pos)) in
let encode encoder range = () in
let decode _ = Ok r in
let encode_bin : _ encode_bin = fun _ _ -> () in
let decode_bin _ _ = r in
let size_of : _ size_of =
(* NOTE: Named args of_value and of_encoding are optional.
Precompute the size that will be used by `encode_bin`. `of_encoding`
unused nowadays
*)
Size.custom_dynamic () in
let compare_pos p q =
p.source = q.source &&
p.offset = q.offset &&
p.start_of_line = q.start_of_line &&
p.line_num = q.line_num
in
let equal r1 r2 =
match Range.view r1, Range.view r2 with
| `End_of_file p, `End_of_file q -> compare_pos p q
| `Range (p1, p2), `Range (q1, q2) ->
compare_pos p1 q1 && compare_pos p2 q2
| _ -> false
in
let compare r1 r2 =
if equal r1 r2 then 0 else
(* FIXME: Is this used by the git-backend? If not, remove it.
*)
match Range.view r1, Range.view r2 with
| `End_of_file p, `End_of_file q ->
(if p.source = q.source then match p.source, q.source with
| `String s1, `String s2 -> String.compare s1.content s2.content
| `File s1, `File s2 -> String.compare s1 s2
| _ -> -1
else -1)
| `Range (p1, p2), `Range (q1, q2) -> -1
| _ -> -1
in
let short_hash ?seed a = 0 in
let pre_hash _ _ = () in
abstract ~pp ~of_string ~json:(encode, decode)
~bin:(encode_bin, decode_bin, size_of)
~equal ~compare ~short_hash ~pre_hash ()
let prim : Prim.t ty=
let open Prim in
enum "prim"
[
("P", `P);
("Ol", `Ol);
("Ul", `Ul);
("Li", `Li);
("Em", `Em);
("Strong", `Strong);
("Code", `Code);
("Blockquote", `Blockquote);
("Pre", `Pre);
]
let date : Prelude.Date.t ty =
let open Prelude.Date in
record "date" (fun yyyy mm dd -> {yyyy; mm; dd})
|+ field "yyyy" int (fun t -> t.yyyy)
|+ field "mm" (option int) (fun t -> t.mm)
|+ field "dd" (option int) (fun t -> t.dd)
|> sealr
module Tree : Irmin.Contents.S with type t = Sem.tree = struct
type t = Sem.tree
let math_mode : Base.math_mode ty =
let open Base in
enum "math_mode" [ ("inline", Inline); ("display", Display) ]
let addr : Base.Addr.t ty =
let open Base in
variant "addr"
(fun
user_addr
machine_addr
-> function
| User_addr x -> user_addr x
| Machine_addr x -> machine_addr x)
|~ case1 "User_addr" string (fun x -> User_addr x)
|~ case1 "Machine_addr" int (fun x -> Machine_addr x)
|> sealv
let xml_resolved_qname : Base.xml_resolved_qname ty =
let open Base in
record "xml_resolved_qname"
(fun prefix uname xmlns -> {prefix; uname; xmlns})
|+ field "prefix" (option string) (fun r -> r.prefix)
|+ field "uname" string (fun r -> r.uname)
|+ field "xmlns" (option string) (fun r -> r.xmlns)
|> sealr
let rec sem_node (t : Sem.t ty) (tree : Sem.tree ty) : Sem.node ty =
let open Sem in
variant "node"
(fun
text
transclude
subtree
query
link
xml_tag
unresolved
math
embed_tex
img
if_tex
prim
object_
ref
-> function
| Text s -> text s
| Transclude (x, y) -> transclude (x, y)
| Subtree (x, y) -> subtree (x, y)
| Query (x, y) -> query (x, y)
| Link (x, y, z) -> link (x, y, z)
| Xml_tag (x, y, z) -> xml_tag (x, y, z)
| Unresolved x -> unresolved x
| Math (x, y) -> math (x, y)
| Embed_tex x -> embed_tex x
| Img x -> img x
| If_tex (x, y) -> if_tex (x, y)
| Prim (x, y) -> prim (x, y)
| Object x -> object_ x
| Ref x -> ref x)
|~ case1 "Text" string (fun s -> Text s)
|~ case1 "Transclude"
(pair (tranclusion_opts t) addr)
(fun (x, y) -> Transclude (x, y))
|~ case1 "Subtree"
(pair (tranclusion_opts t) tree)
(fun (x, y) -> Subtree (x, y))
|~ case1 "Query"
(pair (tranclusion_opts t) (query tree t t))
(fun (x, y) -> Query (x, y))
|~ case1 "Link" (triple addr (option t) modifier) (fun (x, y, z) -> Link (x, y, z))
|~ case1 "Xml_tag"
(triple xml_resolved_qname (list @@ pair xml_resolved_qname t) t)
(fun (x, y, z) -> Xml_tag (x, y, z))
|~ case1 "Unresolved" string (fun s -> Unresolved s)
|~ case1 "Math" (pair math_mode t) (fun (x, y) -> Math (x, y))
|~ case1 "Embed_tex" (embed_tex t) (fun s -> Embed_tex s)
|~ case1 "Img" string (fun s -> Img s)
|~ case1 "If_tex" (pair t t) (fun (x, y) -> If_tex (x, y))
|~ case1 "Prim" (pair prim t) (fun (x, y) -> Prim (x, y))
|~ case1 "Object_" symbol (fun s -> Object s)
|~ case1 "Ref" addr (fun s -> Ref s)
|> sealv
and embed_tex (t : Sem.t ty) : Sem.embedded_tex ty =
let open Sem in
record "embed_tex" (fun preamble source -> { preamble; source })
|+ field "preamble" t (fun t -> t.preamble)
|+ field "source" t (fun t -> t.source)
|> sealr
and modifier : Sem.Text_modifier.t ty =
let open Sem.Text_modifier in
enum "modifier" [ ("sentence_case", Sentence_case); ("identity", Identity)]
and symbol : Symbol.t ty =
let open Symbol in
pair (list string) int
and query (tree : Sem.tree ty) (t : Sem.t ty) a : 'a Query.t ty =
let open Query in
mu @@ fun query ->
variant "query" (fun author tag taxon meta or_ and_ not_ true_ -> function
| Author x -> author x
| Tag x -> tag x
| Taxon x -> taxon x
| Meta (x, y) -> meta (x, y)
| Or x -> or_ x
| And x -> and_ x
| Not x -> not_ x
| True -> true_)
|~ case1 "Author" a (fun x -> Author x)
|~ case1 "Tag" a (fun x -> Tag x)
|~ case1 "Taxon" a (fun x -> Taxon x)
|~ case1 "Meta" (pair string a) (fun (x, y) -> Meta (x, y))
|~ case1 "Or" (list query ) (fun x -> Or x)
|~ case1 "And" (list query ) (fun x -> And x)
|~ case1 "Not" query (fun x -> Not x)
|~ case0 "True" True |> sealv
and tranclusion_opts (t : Sem.t ty) =
let open Sem in
record "tranclusion_opts"
(fun
toc
show_heading
show_metadata
title_override
taxon_override
expanded
numbered
->
{
toc;
show_heading;
show_metadata;
title_override;
taxon_override;
expanded;
numbered;
})
|+ field "toc" bool (fun t -> t.toc)
|+ field "show_heading" bool (fun t -> t.show_heading)
|+ field "show_metadata" bool (fun t -> t.show_metadata)
|+ field "title_override"
(option t)
(fun t -> t.title_override)
|+ field "taxon_override" (option string) (fun t -> t.taxon_override)
|+ field "expanded" bool (fun t -> t.expanded)
|+ field "numbered" bool (fun t -> t.numbered)
|> sealr
let frontmatter (t : Sem.t ty) =
let open Sem in
record "frontmatter"
(fun
title
taxon
authors
contributors
dates
addr
metas
tags
physical_parent
designated_parent
source_path
number
->
{
title;
taxon;
authors;
contributors;
dates;
addr;
metas;
tags;
physical_parent;
designated_parent;
source_path;
number;
})
|+ field "title" (option t) (fun t -> t.title)
|+ field "taxon" (option string) (fun t -> t.taxon)
|+ field "authors" (list addr) (fun t -> t.authors)
|+ field "contributors" (list addr) (fun t -> t.contributors)
|+ field "dates" (list date) (fun t -> t.dates)
|+ field "addr" addr (fun t -> t.addr)
|+ field "metas" (list (pair string t)) (fun t -> t.metas)
|+ field "tags" (list string) (fun t -> t.tags)
|+ field "physical_parent" (option addr) (fun t -> t.physical_parent)
|+ field "designated_parent" (option addr) (fun t -> t.designated_parent)
|+ field "source_path" (option string) (fun t -> t.source_path)
|+ field "number" (option string) (fun t -> t.number)
|> sealr
let located_sem_node (t : Sem.t ty) (tree : Sem.tree ty) : Sem.node Range.located ty =
let open Asai in
let open Range in
record "located_sem_node" (fun loc value -> { loc; value })
|+ field "loc" (option range) (fun t -> None)
|+ field "value" (sem_node t tree) (fun t -> t.value)
|> sealr
let tree : Sem.tree ty =
let open Sem in
mu (fun tree ->
let t = mu (fun t -> list (located_sem_node t tree)) in
record "tree" (fun fm body : Sem.tree -> { fm; body })
|+ field "fm" (frontmatter t) (fun t -> t.fm)
|+ field "body" t (fun (t : Sem.tree) -> t.body)
|> sealr)
let t = tree
let merge = Irmin.Merge.(option (idempotent t))
end