@@ -51,46 +51,75 @@ module Regexp = struct
51
51
| Capture_as (idr , e ) ->
52
52
fun (nG , bs ) ->
53
53
recurse must_match e (nG + 1 , (idr, Some nG, must_match) :: bs)
54
- | Call _ -> error ~loc " (&...) is not implemented for %%pcre. " )
54
+ | Call _ -> fun ( nG , bs ) -> (nG + 1 , bs) )
55
55
in
56
56
(function
57
57
| {Location. txt = Capture_as (idr , e ); _} ->
58
58
recurse true e (0 , [idr, None , true ])
59
59
| e ->
60
60
recurse true e (0 , [] ))
61
61
62
- let to_string =
63
- let p_alt, p_seq, p_suffix, p_atom = 0 , 1 , 2 , 3 in
64
- let delimit_if b s = if b then " (?:" ^ s ^ " )" else s in
65
- let rec recurse p (e' : _ Location.loc ) =
66
- let loc = e'.Location. loc in
67
- (match e'.Location. txt with
68
- | Code s ->
69
- (* Delimiters not needed as Regexp.parse_exn only returns single
70
- * chars, csets, and escape sequences. *)
71
- s
72
- | Seq es ->
73
- delimit_if (p > p_seq)
74
- (String. concat " " (List. map (recurse p_seq) es))
75
- | Alt es ->
76
- delimit_if (p > p_alt)
77
- (String. concat " |" (List. map (recurse p_alt) es))
78
- | Opt e ->
79
- delimit_if (p > p_suffix) (recurse p_atom e ^ " ?" )
80
- | Repeat ({Location. txt = (i , j_opt ); _} , e ) ->
81
- let j_str = match j_opt with None -> " " | Some j -> string_of_int j in
82
- delimit_if (p > p_suffix)
83
- (Printf. sprintf " %s{%d,%s}" (recurse p_atom e) i j_str)
84
- | Nongreedy e -> recurse p_suffix e ^ " ?"
85
- | Capture _ -> error ~loc " Unnamed capture is not allowed for %%pcre."
86
- | Capture_as (_ , e ) -> " (" ^ recurse p_alt e ^ " )"
87
- | Call _ -> error ~loc " (&...) is not implemented for %%pcre." )
62
+ let rec to_re_expr ~loc ~in_let (e : _ Location.loc ) =
63
+ let open Ast_builder.Default in
64
+ match e.Location. txt with
65
+ | Code s ->
66
+ [% expr Re.Perl. re [% e estring ~loc s]]
67
+ | Seq es ->
68
+ let exprs = List. map (to_re_expr ~loc ~in_let ) es in
69
+ [% expr Re. seq [% e elist ~loc exprs]]
70
+ | Alt es ->
71
+ let exprs = List. map (to_re_expr ~loc ~in_let ) es in
72
+ [% expr Re. alt [% e elist ~loc exprs]]
73
+ | Opt e ->
74
+ [% expr Re. opt [% e to_re_expr ~loc ~in_let e]]
75
+ | Repeat ({Location. txt = (i , j_opt ); _} , e ) ->
76
+ let e_i = eint ~loc i in
77
+ let e_j = match j_opt with
78
+ | None -> [% expr None ]
79
+ | Some j -> [% expr Some [% e eint ~loc j]]
80
+ in
81
+ [% expr Re. repn [% e to_re_expr ~loc ~in_let e] [% e e_i] [% e e_j]]
82
+ | Nongreedy e ->
83
+ [% expr Re. non_greedy [% e to_re_expr ~loc ~in_let e]]
84
+ | Capture e ->
85
+ [% expr Re. group [% e to_re_expr ~loc ~in_let e]]
86
+ | Capture_as (_ , e ) ->
87
+ [% expr Re. group [% e to_re_expr ~loc ~in_let e]]
88
+ | Call lid ->
89
+ if in_let then pexp_ident ~loc lid else
90
+ [% expr Re. group [% e pexp_ident ~loc lid]]
91
+
92
+ let rec squash_codes (e : _ Location.loc ) : _ Location.loc =
93
+ let open Location in
94
+ let rec combine (nodes : _ Location.loc list ) =
95
+ match nodes with
96
+ | [] -> []
97
+ | {Location. txt = Code s1 ; loc = loc1 } :: {Location. txt = Code s2 ; loc = loc2 } :: rest ->
98
+ let combined_loc =
99
+ if loc1 = Location. none || loc2 = Location. none then Location. none
100
+ else Location. {
101
+ loc_start = loc1.loc_start;
102
+ loc_end = loc2.loc_end;
103
+ loc_ghost = false ;
104
+ }
105
+ in
106
+ combine ({Location. txt = Code (s1 ^ s2); loc = combined_loc} :: rest)
107
+ | node :: rest -> node :: combine rest
88
108
in
89
- (function
90
- | {Location. txt = Capture_as (_ , e ); _} ->
91
- recurse 0 e
92
- | e ->
93
- recurse 0 e)
109
+ match e.txt with
110
+ | Code _ -> e
111
+ | Seq es ->
112
+ let es = List. map squash_codes es in
113
+ {e with txt = Seq (combine es)}
114
+ | Alt es ->
115
+ let es = List. map squash_codes es in
116
+ {e with txt = Alt es}
117
+ | Opt e' -> {e with txt = Opt (squash_codes e')}
118
+ | Repeat (range , e' ) -> {e with txt = Repeat (range, squash_codes e')}
119
+ | Nongreedy e' -> {e with txt = Nongreedy (squash_codes e')}
120
+ | Capture e' -> {e with txt = Capture (squash_codes e')}
121
+ | Capture_as (name , e' ) -> {e with txt = Capture_as (name, squash_codes e')}
122
+ | Call _ -> e
94
123
end
95
124
96
125
let fresh_var =
@@ -114,11 +143,11 @@ let rec must_match p i =
114
143
true
115
144
116
145
let extract_bindings ~pos s =
117
- let r = Regexp. parse_exn ~pos s in
146
+ let r = Regexp. (squash_codes @@ parse_exn ~pos s) in
118
147
let nG, bs = Regexp. bindings r in
119
- let re_str = Regexp. to_string r in
120
148
let loc = Location. none in
121
- (estring ~loc re_str, bs, nG)
149
+ let re_expr = Regexp. to_re_expr ~loc ~in_let: false r in
150
+ (re_expr, bs, nG)
122
151
123
152
let rec wrap_group_bindings ~loc rhs offG = function
124
153
| [] -> rhs
@@ -137,6 +166,19 @@ let rec wrap_group_bindings ~loc rhs offG = function
137
166
let [% p ppat_var ~loc varG] = [% e eG] in
138
167
[% e wrap_group_bindings ~loc rhs offG bs]]
139
168
169
+ let transform_let =
170
+ List. map
171
+ begin
172
+ fun vb ->
173
+ match vb.pvb_pat.ppat_desc, vb.pvb_expr.pexp_desc with
174
+ | Ppat_var { txt = _ ; loc } , Pexp_constant (Pconst_string (value , _ , _ )) ->
175
+ let parsed = Regexp. (squash_codes @@ parse_exn value) in
176
+ let re_expr = Regexp. to_re_expr ~loc ~in_let: true parsed in
177
+ let expr = [% expr [% e re_expr]] in
178
+ { vb with pvb_expr = expr }
179
+ | _ -> vb
180
+ end
181
+
140
182
let transform_cases ~loc cases =
141
183
let aux case =
142
184
if case.pc_guard <> None then
@@ -173,7 +215,7 @@ let transform_cases ~loc cases =
173
215
let cases = List. rev_map aux cases in
174
216
let res = pexp_array ~loc (List. map (fun (re , _ , _ , _ ) -> re) cases) in
175
217
let comp = [% expr
176
- let a = Array. map (fun s -> Re. mark ( Re.Perl. re s) ) [% e res] in
218
+ let a = Array. map (fun re -> Re. mark re ) [% e res] in
177
219
let marks = Array. map fst a in
178
220
let re = Re. compile (Re. alt (Array. to_list (Array. map snd a))) in
179
221
(re, marks)
@@ -202,33 +244,46 @@ let transform_cases ~loc cases =
202
244
(cases, re_binding)
203
245
204
246
let transformation = object
205
- inherit [value_binding list ] Ast_traverse. fold_map as super
247
+ inherit [value_binding list * value_binding list ] Ast_traverse. fold_map as super
248
+
249
+ method! structure_item item (acc, let_acc) =
250
+ match item.pstr_desc with
251
+ (* let % pcre x = {|some regex|} * )
252
+ | Pstr_extension (({ txt = "pcre" ; loc } , PStr [ { pstr_desc = Pstr_value (Nonrecursive, vbs ); _ } ]), _ ) ->
253
+ let bindings = transform_let vbs in
254
+ let dummy = {item with pstr_desc = Pstr_eval ([% expr () ], [] )} in
255
+ dummy, (acc, bindings @ let_acc)
256
+ | _ -> super#structure_item item (acc, let_acc)
206
257
207
258
method! expression e_ext acc =
208
- let e_ext, acc = super#expression e_ext acc in
259
+ let e_ext, ( acc, let_acc) = super#expression e_ext acc in
209
260
(match e_ext.pexp_desc with
210
261
| Pexp_extension
211
262
({txt = " pcre" ; _}, PStr [{pstr_desc = Pstr_eval (e, _); _}]) ->
212
263
let loc = e.pexp_loc in
213
264
(match e.pexp_desc with
214
265
| Pexp_match (e , cases ) ->
215
266
let cases, binding = transform_cases ~loc cases in
216
- ([% expr let _ppx_regexp_v = [% e e] in [% e cases]], binding :: acc)
267
+ ([% expr let _ppx_regexp_v = [% e e] in [% e cases]], ( binding :: acc, let_acc) )
217
268
| Pexp_function (cases ) ->
218
269
let cases, binding = transform_cases ~loc cases in
219
- ([% expr fun _ppx_regexp_v -> [% e cases]], binding :: acc)
270
+ ([% expr fun _ppx_regexp_v -> [% e cases]], ( binding :: acc, let_acc) )
220
271
| _ ->
221
272
error ~loc " [%%pcre] only applies to match an function." )
222
- | _ -> (e_ext, acc))
273
+ | _ -> (e_ext, ( acc, let_acc) ))
223
274
end
224
275
225
276
let impl str =
226
- let str, rev_bindings = transformation#structure str [] in
277
+ let str, ( rev_bindings, let_bindings) = transformation#structure str ( [] , [] ) in
227
278
if rev_bindings = [] then str else
228
- let re_str =
229
279
let loc = Location. none in
230
- [% str open (struct [%% i pstr_value ~loc Nonrecursive rev_bindings] end )]
231
- in
232
- re_str @ str
280
+ let all_bindings = List. rev let_bindings @ rev_bindings in
281
+ let struct_items =
282
+ List. fold_left (fun acc binding ->
283
+ acc @ [% str let [% p binding.pvb_pat] = [% e binding.pvb_expr]]
284
+ ) [] all_bindings
285
+ in
286
+ let mod_expr = pmod_structure ~loc struct_items in
287
+ [% str open [% m mod_expr]] @ str
233
288
234
289
let () = Driver. register_transformation ~impl " ppx_regexp"
0 commit comments