Source file ppx_matches.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
open! Ppxlib
open! Ast_builder.Default
module List = ListLabels
let ppx_namespace = "ppx_matches"
let pp_quoted pp ppf = Format.fprintf ppf "`%a`" pp
let raise_errorf ~loc fmt = Location.raise_errorf ~loc ("%s: " ^^ fmt) ppx_namespace
module Matches = struct
let is_binding_var var = not (String.starts_with ~prefix:"_" var.txt)
let rec is_binding_pattern pat =
match pat.ppat_desc with
| Ppat_any -> false
| Ppat_var var -> is_binding_var var
| Ppat_alias (pat, var) -> is_binding_pattern pat || is_binding_var var
| Ppat_array pats | Ppat_tuple pats -> List.exists pats ~f:is_binding_pattern
| Ppat_interval _ | Ppat_constant _ -> false
| Ppat_construct (_, None) | Ppat_variant (_, None) -> false
| Ppat_construct (_, Some (_, pat)) | Ppat_variant (_, Some pat) ->
is_binding_pattern pat
| Ppat_record (fields, _) ->
List.exists fields ~f:(fun (_field, pat) -> is_binding_pattern pat)
| Ppat_or (pat1, pat2) -> is_binding_pattern pat1 || is_binding_pattern pat2
| Ppat_constraint (pat, _type) -> is_binding_pattern pat
| Ppat_type _type_ident -> true
| Ppat_lazy pat -> is_binding_pattern pat
| Ppat_unpack mod_ident -> Option.is_some mod_ident.txt
| Ppat_exception pat -> is_binding_pattern pat
| Ppat_extension _ ->
true
| Ppat_open (_mod_ident, pat) -> is_binding_pattern pat
;;
let assert_pat_is_not_binding ~loc pat =
if is_binding_pattern pat
then
raise_errorf
~loc
"invalid [%%matches? ...] payload. Pattern %a cannot bind any variables."
(pp_quoted Pprintast.pattern)
pat
;;
let expand_inline ~loc matchee pat ?when_ () =
match when_ with
| None ->
assert_pat_is_not_binding ~loc pat;
[%expr
begin[@warning "-11"] match [%e matchee] with
| [%p pat] -> true
| _ -> false
end]
| Some when_ ->
[%expr
begin[@warning "-11"] match [%e matchee] with
| [%p pat] when [%e when_] -> true
| _ -> false
end]
;;
let expand ~loc pat ?when_ () =
match when_ with
| None ->
assert_pat_is_not_binding ~loc pat;
[%expr
function[@warning "-11"]
| [%p pat] -> true
| _ -> false]
| Some when_ ->
[%expr
function[@warning "-11"]
| [%p pat] when [%e when_] -> true
| _ -> false]
;;
end
let impl : structure -> structure =
object
inherit Ast_traverse.map as super
method! expression expr =
let loc = expr.pexp_loc in
match expr with
| [%expr [%matches? [%p? pat]] [%e? matchee]] ->
Matches.expand_inline ~loc matchee pat ()
| [%expr [%matches? [%p? pat] when [%e? when_]] [%e? matchee]] ->
Matches.expand_inline ~loc matchee pat ~when_ ()
| [%expr [%matches? [%p? pat]]] -> Matches.expand ~loc pat ()
| [%expr [%matches? [%p? pat] when [%e? when_]]] ->
Matches.expand ~loc pat ~when_ ()
| expr -> super#expression expr
end
#structure
;;
let () =
Reserved_namespaces.reserve ppx_namespace;
Driver.register_transformation ~impl ppx_namespace
;;