-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathppx_compose.ml
76 lines (67 loc) · 2.88 KB
/
ppx_compose.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
(* Copyright (C) 2017--2021 Petter A. Urkedal <paurkedal@gmail.com>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version, with the LGPL-3.0 Linking Exception.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* and the LGPL-3.0 Linking Exception along with this library. If not, see
* <http://www.gnu.org/licenses/> and <https://spdx.org>, respectively.
*)
open Ppxlib
open Ast_builder.Default
(* Is there an existing function? *)
let fresh_var_for e =
Printf.sprintf "_ppx_compose_%d" e.pexp_loc.Location.loc_start.Lexing.pos_cnum
let apply ~loc f xs =
(match f.pexp_desc with
| Pexp_apply (f', xs') ->
pexp_apply ~loc f' (List.append xs' xs)
| _ ->
pexp_apply ~loc f xs)
let rec reduce_compose h x =
(match h.pexp_desc with
| Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "%"; _}; _},
[(Nolabel, g); (Nolabel, f)])
| Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "%>"; _}; _},
[(Nolabel, f); (Nolabel, g)]) ->
let fx = reduce_compose f x in
reduce_compose g fx
| _ ->
(apply ~loc:h.pexp_loc h [Nolabel, x]))
let classify e =
(match e.pexp_desc with
| Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "%"; _}; _},
[(Nolabel, _); (Nolabel, _)]) -> `Compose
| Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "%>"; _}; _},
[(Nolabel, _); (Nolabel, _)]) -> `Compose_fw
| _ -> `Other)
let eta_expand_composition ~is_fw e =
let name = fresh_var_for e in
let var_loc =
if is_fw then {e.pexp_loc with loc_end = e.pexp_loc.loc_start}
else {e.pexp_loc with loc_start = e.pexp_loc.loc_end} in
let pat = ppat_var ~loc:var_loc {txt = name; loc = var_loc} in
let arg = pexp_ident ~loc:var_loc {txt = Lident name; loc = var_loc} in
let body = reduce_compose e arg in
pexp_fun ~loc:e.pexp_loc Nolabel None pat body
let rewrite_compose e =
(match e.pexp_desc with
| Pexp_apply (h, ((Nolabel, x) :: xs)) when classify h <> `Other ->
Some (apply ~loc:e.pexp_loc (reduce_compose h x) xs)
| _ ->
(match classify e with
| `Compose -> Some (eta_expand_composition ~is_fw:false e)
| `Compose_fw -> Some (eta_expand_composition ~is_fw:true e)
| `Other -> None))
let rules = [
Context_free.Rule.special_function "%" rewrite_compose;
Context_free.Rule.special_function "%>" rewrite_compose;
]
let () = Driver.register_transformation ~rules "ppx_compose"