Skip to content

Commit c15c941

Browse files
author
Armaël Guéneau
committed
Refactor the api to provide both AST with and without locations
1 parent 2a9df71 commit c15c941

File tree

4 files changed

+365
-116
lines changed

4 files changed

+365
-116
lines changed

lib/mustache.ml

Lines changed: 187 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,9 @@
2222
open MoreLabels
2323
include Mustache_types
2424

25-
let dummy_loc = {
26-
loc_start = Lexing.dummy_pos;
27-
loc_end = Lexing.dummy_pos;
28-
}
29-
3025
module List = ListLabels
3126
module String = StringLabels
3227

33-
module Infix = struct
34-
let (^) y x = Concat (dummy_loc, [x; y])
35-
end
36-
3728
module Json = struct
3829
type value =
3930
[ `Null
@@ -50,9 +41,6 @@ module Json = struct
5041
let value: t -> value = fun t -> (t :> value)
5142
end
5243

53-
let parse_lx = Mustache_parser.mustache Mustache_lexer.mustache
54-
let of_string s = parse_lx (Lexing.from_string s)
55-
5644
let escape_html s =
5745
let b = Buffer.create (String.length s) in
5846
String.iter ( function
@@ -65,73 +53,82 @@ let escape_html s =
6553
) s ;
6654
Buffer.contents b
6755

68-
let rec pp fmt = function
69-
70-
| String (_, s) ->
56+
(* Utility functions that allow converting between the ast with locations and
57+
without locations. *)
58+
59+
let dummy_loc =
60+
{ Locs.loc_start = Lexing.dummy_pos;
61+
Locs.loc_end = Lexing.dummy_pos }
62+
63+
let rec erase_locs { Locs.desc; _ } =
64+
erase_locs_desc desc
65+
and erase_locs_desc = function
66+
| Locs.String s -> No_locs.String s
67+
| Locs.Escaped s -> No_locs.Escaped s
68+
| Locs.Section s -> No_locs.Section (erase_locs_section s)
69+
| Locs.Unescaped s -> No_locs.Unescaped s
70+
| Locs.Partial s -> No_locs.Partial s
71+
| Locs.Inverted_section s -> No_locs.Inverted_section (erase_locs_section s)
72+
| Locs.Concat l -> No_locs.Concat (List.map erase_locs l)
73+
| Locs.Comment s -> No_locs.Comment s
74+
and erase_locs_section { Locs.name; Locs.contents } =
75+
{ No_locs.name; No_locs.contents = erase_locs contents }
76+
77+
let rec add_dummy_locs t =
78+
{ Locs.loc = dummy_loc;
79+
Locs.desc = add_dummy_locs_desc t }
80+
and add_dummy_locs_desc = function
81+
| No_locs.String s -> Locs.String s
82+
| No_locs.Escaped s -> Locs.Escaped s
83+
| No_locs.Section s -> Locs.Section (add_dummy_locs_section s)
84+
| No_locs.Unescaped s -> Locs.Unescaped s
85+
| No_locs.Partial s -> Locs.Partial s
86+
| No_locs.Inverted_section s ->
87+
Locs.Inverted_section (add_dummy_locs_section s)
88+
| No_locs.Concat l -> Locs.Concat (List.map add_dummy_locs l)
89+
| No_locs.Comment s -> Locs.Comment s
90+
and add_dummy_locs_section { No_locs.name; No_locs.contents } =
91+
{ Locs.name; Locs.contents = add_dummy_locs contents }
92+
93+
(* Printing: defined on the ast without locations. *)
94+
95+
let rec pp fmt =
96+
let open No_locs in
97+
function
98+
| String s ->
7199
Format.pp_print_string fmt s
72100

73-
| Escaped (_, s) ->
101+
| Escaped s ->
74102
Format.fprintf fmt "{{ %s }}" s
75103

76-
| Unescaped (_, s) ->
104+
| Unescaped s ->
77105
Format.fprintf fmt "{{& %s }}" s
78106

79-
| Inverted_section (_, s) ->
107+
| Inverted_section s ->
80108
Format.fprintf fmt "{{^%s}}%a{{/%s}}"
81109
s.name pp s.contents s.name
82110

83-
| Section (_, s) ->
111+
| Section s ->
84112
Format.fprintf fmt "{{#%s}}%a{{/%s}}"
85113
s.name pp s.contents s.name
86114

87-
| Partial (_, s) ->
115+
| Partial s ->
88116
Format.fprintf fmt "{{> %s }}" s
89117

90-
| Comment (_, s) ->
118+
| Comment s ->
91119
Format.fprintf fmt "{{! %s }}" s
92120

93-
| Concat (_, s) ->
121+
| Concat s ->
94122
List.iter (pp fmt) s
95123

96-
let to_formatter = pp
97-
98124
let to_string x =
99125
let b = Buffer.create 0 in
100126
let fmt = Format.formatter_of_buffer b in
101127
pp fmt x ;
102128
Format.pp_print_flush fmt () ;
103129
Buffer.contents b
104130

105-
let rec fold ~string ~section ~escaped ~unescaped ~partial ~comment ~concat t =
106-
let go = fold ~string ~section ~escaped ~unescaped ~partial ~comment ~concat in
107-
match t with
108-
| String (_, s) -> string s
109-
| Escaped (_, s) -> escaped s
110-
| Unescaped (_, s) -> unescaped s
111-
| Comment (_, s) -> comment s
112-
| Section (_, { name; contents }) ->
113-
section ~inverted:false name (go contents)
114-
| Inverted_section (_, { name; contents }) ->
115-
section ~inverted:true name (go contents)
116-
| Concat (_, ms) ->
117-
concat (List.map ms ~f:go)
118-
| Partial (_, p) -> partial p
119-
120-
let raw s = String (dummy_loc, s)
121-
let escaped s = Escaped (dummy_loc, s)
122-
let unescaped s = Unescaped (dummy_loc, s)
123-
let section n c = Section (dummy_loc, { name = n ; contents = c })
124-
let inverted_section n c = Inverted_section (dummy_loc, { name = n ; contents = c })
125-
let partial s = Partial (dummy_loc, s)
126-
let concat t = Concat (dummy_loc, t)
127-
let comment s = Comment (dummy_loc, s)
128-
129-
let rec expand_partials =
130-
let section ~inverted =
131-
if inverted then inverted_section else section
132-
in
133-
fun partial ->
134-
fold ~string:raw ~section ~escaped ~unescaped ~partial ~comment ~concat
131+
(* Rendering: defined on the ast without locations. *)
135132

136133
module Lookup = struct
137134
let scalar ?(strict=true) = function
@@ -177,48 +174,170 @@ module Lookup = struct
177174

178175
end
179176

180-
let render_fmt ?(strict=true) (fmt : Format.formatter) (m : t) (js : Json.t) =
181-
177+
let render_fmt ?(strict=true) (fmt : Format.formatter) (m : No_locs.t) (js : Json.t) =
178+
let open No_locs in
182179
let rec render' m (js : Json.value) = match m with
183180

184-
| String (_, s) ->
181+
| String s ->
185182
Format.pp_print_string fmt s
186183

187-
| Escaped (_, ".") ->
184+
| Escaped "." ->
188185
Format.pp_print_string fmt (escape_html (Lookup.scalar js))
189-
| Escaped (_, key) ->
186+
| Escaped key ->
190187
Format.pp_print_string fmt (escape_html (Lookup.str ~strict ~key js))
191188

192-
| Unescaped (_, ".") ->
189+
| Unescaped "." ->
193190
Format.pp_print_string fmt (Lookup.scalar js)
194-
| Unescaped (_, key) ->
191+
| Unescaped key ->
195192
Format.pp_print_string fmt (Lookup.str ~strict ~key js)
196193

197-
| Inverted_section (loc, s) ->
194+
| Inverted_section s ->
198195
if Lookup.inverted js s.name
199-
then render' (Section (loc, s)) js
196+
then render' (Section s) js
200197

201-
| Section (_, s) ->
198+
| Section s ->
202199
begin match Lookup.section ~strict js ~key:s.name with
203200
| `Bool false -> ()
204201
| `Bool true -> render' s.contents js
205202
| `A contexts -> List.iter (render' s.contents) contexts
206203
| context -> render' s.contents context
207204
end
208205

209-
| Partial (_, _) ->
206+
| Partial _ ->
210207
pp fmt m
211208

212-
| Comment (_, c) -> ()
209+
| Comment c -> ()
213210

214-
| Concat (_, templates) ->
211+
| Concat templates ->
215212
List.iter (fun x -> render' x js) templates
216213

217214
in render' m (Json.value js)
218215

219-
let render ?(strict=true) (m : t) (js : Json.t) =
216+
let render ?(strict=true) (m : No_locs.t) (js : Json.t) =
220217
let b = Buffer.create 0 in
221218
let fmt = Format.formatter_of_buffer b in
222219
render_fmt ~strict fmt m js ;
223220
Format.pp_print_flush fmt () ;
224221
Buffer.contents b
222+
223+
(* Parsing: produces an ast with locations. *)
224+
225+
let parse_lx : Lexing.lexbuf -> Locs.t =
226+
Mustache_parser.mustache Mustache_lexer.mustache
227+
228+
let of_string s = parse_lx (Lexing.from_string s)
229+
230+
(* Packing up everything in two modules of similar signature:
231+
[With_locations] and [Without_locations].
232+
*)
233+
234+
module With_locations = struct
235+
include Locs
236+
237+
let dummy_loc = dummy_loc
238+
let parse_lx = parse_lx
239+
let of_string = of_string
240+
241+
let pp fmt x = pp fmt (erase_locs x)
242+
let to_formatter = pp
243+
244+
let to_string x = to_string (erase_locs x)
245+
246+
let render_fmt ?strict fmt m js =
247+
render_fmt ?strict fmt (erase_locs m) js
248+
249+
let render ?strict m js =
250+
render ?strict (erase_locs m) js
251+
252+
let rec fold ~string ~section ~escaped ~unescaped ~partial ~comment ~concat t =
253+
let go = fold ~string ~section ~escaped ~unescaped ~partial ~comment ~concat in
254+
let { desc; loc } = t in
255+
match desc with
256+
| String s -> string ~loc s
257+
| Escaped s -> escaped ~loc s
258+
| Unescaped s -> unescaped ~loc s
259+
| Comment s -> comment ~loc s
260+
| Section { name; contents } ->
261+
section ~loc ~inverted:false name (go contents)
262+
| Inverted_section { name; contents } ->
263+
section ~loc ~inverted:true name (go contents)
264+
| Concat ms ->
265+
concat ~loc (List.map ms ~f:go)
266+
| Partial p -> partial ~loc p
267+
268+
module Infix = struct
269+
let (^) t1 t2 = { desc = Concat [t1; t2]; loc = dummy_loc }
270+
end
271+
272+
let raw ~loc s = { desc = String s; loc }
273+
let escaped ~loc s = { desc = Escaped s; loc }
274+
let unescaped ~loc s = { desc = Unescaped s; loc }
275+
let section ~loc n c =
276+
{ desc = Section { name = n; contents = c };
277+
loc }
278+
let inverted_section ~loc n c =
279+
{ desc = Inverted_section { name = n; contents = c };
280+
loc }
281+
let partial ~loc s = { desc = Partial s; loc }
282+
let concat ~loc t = { desc = Concat t; loc }
283+
let comment ~loc s = { desc = Comment s; loc }
284+
285+
let rec expand_partials =
286+
let section ~loc ~inverted =
287+
if inverted then inverted_section ~loc else section ~loc
288+
in
289+
fun partial ->
290+
fold ~string:raw ~section ~escaped ~unescaped ~partial ~comment ~concat
291+
end
292+
293+
module Without_locations = struct
294+
include No_locs
295+
296+
let parse_lx lexbuf = erase_locs (parse_lx lexbuf)
297+
let of_string s = erase_locs (of_string s)
298+
299+
let pp = pp
300+
let to_formatter = pp
301+
302+
let to_string = to_string
303+
304+
let rec fold ~string ~section ~escaped ~unescaped ~partial ~comment ~concat t =
305+
let go = fold ~string ~section ~escaped ~unescaped ~partial ~comment ~concat in
306+
match t with
307+
| String s -> string s
308+
| Escaped s -> escaped s
309+
| Unescaped s -> unescaped s
310+
| Comment s -> comment s
311+
| Section { name; contents } ->
312+
section ~inverted:false name (go contents)
313+
| Inverted_section { name; contents } ->
314+
section ~inverted:true name (go contents)
315+
| Concat ms ->
316+
concat (List.map ms ~f:go)
317+
| Partial p -> partial p
318+
319+
module Infix = struct
320+
let (^) y x = Concat [x; y]
321+
end
322+
323+
let raw s = String s
324+
let escaped s = Escaped s
325+
let unescaped s = Unescaped s
326+
let section n c = Section { name = n ; contents = c }
327+
let inverted_section n c = Inverted_section { name = n ; contents = c }
328+
let partial s = Partial s
329+
let concat t = Concat t
330+
let comment s = Comment s
331+
332+
let rec expand_partials =
333+
let section ~inverted =
334+
if inverted then inverted_section else section
335+
in
336+
fun partial ->
337+
fold ~string:raw ~section ~escaped ~unescaped ~partial ~comment ~concat
338+
end
339+
340+
(* Include [Without_locations] at the toplevel, to preserve backwards
341+
compatibility of the API. *)
342+
343+
include Without_locations

0 commit comments

Comments
 (0)