File: ast_pattern.mli

package info (click to toggle)
ppxlib 0.15.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, sid
  • size: 1,284 kB
  • sloc: ml: 17,184; sh: 149; makefile: 36; python: 36
file content (225 lines) | stat: -rw-r--r-- 8,355 bytes parent folder | download
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
(** First class AST patterns *)

open! Import

(** PPX rewriters often need to recognize fragments the OCaml AST, for instance to parse
    the payload of an attribute/expression. You can do that with a pattern matching and
    manual error reporting when the input is not what you expect but this has proven to
    quickly become extremely verbose and unreadable.

    This module aims to help with that by providing first class AST patterns.

    To understand how to use it, let's consider the example of ppx_inline_test. We want to
    recognize patterns of the form:

    {[
      let%test "name" = expr
    ]}

    Which is a syntactic sugar for:

    {[
      [%%test let "name" = expr]
    ]}

    If we wanted to write a function that recognizes the payload of [%%test] using normal
    pattern matching we would write:

    {[
      let match_payload = function
        | Pstr [ { pstr_desc = Pstr_value (Nonrecursive,
                                           [ { pvb_pat = Ppat_constant (Constant_string
                                                                          (name, None))
                                             ; pvb_expr = e
                                             ; _ } ])
                 ; _ } ] ->
          (name, e)
        | _ -> Location.raisef ...
    ]}

    This is quite cumbersome, and this is still not right: this function drops all
    attributes without notice.

    Now let's imagine we wanted to construct the payload instead, using [Ast_builder] one
    would write:

    {[
      let build_payload ~loc name expr =
        let (module B) = Ast_builder.with_loc loc in
        let open B in
        pstr [ pstr_value Nonrecursive (value_binding ~pat:(pstring name) ~expr) ]
    ]}

    Constructing a first class pattern is almost as simple as replacing [Ast_builder] by
    [Ast_pattern]:

    {[
      let payload_pattern name expr =
        let open Ast_pattern in
        pstr (pstr_value nonrecursive (value_binding ~pat:(pstring __) ~expr:__) ^:: nil)
    ]}

    Notice that the place-holders for [name] and [expr] have been replaced by [__]. The
    following pattern with have type:

    {[ (payload, string -> expression -> 'a, 'a) Ast_pattern.t ]}

    which means that it matches values of type [payload] and captures a string and
    expression from it. The two captured elements comes from the use of [__].
*)

(** Type of a pattern:

    - ['a] is the type of value matched by the pattern
    - ['b] is the continuation, for instance for a pattern that captures an [int] and a
      [string], ['b] will be [int -> string -> _]
    - ['c] is the result of the continuation.
*)
type ('a, 'b, 'c) t = ('a, 'b, 'c) Ast_pattern0.t

(** Matches a value against a pattern. *)
val parse : ('a, 'b, 'c) t -> Location.t -> ?on_error:(unit -> 'c) -> 'a -> 'b -> 'c

module Packed : sig
  type ('a, 'b, 'c) pattern = ('a, 'b, 'c) t
  type ('a, 'b) t

  val create : ('a, 'b, 'c) pattern -> 'b -> ('a, 'c) t
  val parse : ('a, 'b) t -> Location.t -> 'a -> 'b
end with type ('a, 'b, 'c) pattern := ('a, 'b, 'c) t

(** Pattern that captures its input. *)
val __ : ('a, 'a -> 'b, 'b) t

(** Same as [__] but also captures the location.

    Note: this should only be used for types that do not embed a location. For instance
    you can use it to capture a string constant:

    {[
      estring __'
    ]}

    but using it to capture an expression would not yield the expected result:

    {[
      pair (eint (int 42)) __'
    ]}

    In the latter case you should use the [pexp_loc] field of the captured expression
    instead.
*)
val __' : ('a, 'a Loc.t -> 'b, 'b) t

(** [alt] stands for `alternatives'. It matches either the first pattern or the second
    one. *)
val alt : ('a, 'b, 'c) t -> ('a, 'b, 'c) t -> ('a, 'b, 'c) t

(** Same as [alt], for the common case where the left-hand-side captures a value but not
    the right-hand-side. *)
val alt_option : ('a, 'v -> 'b, 'c) t -> ('a, 'b, 'c) t -> ('a, 'v option -> 'b, 'c) t

(** Same as [alt] *)
val ( ||| ) : ('a, 'b, 'c) t -> ('a, 'b, 'c) t -> ('a, 'b, 'c) t

val map : ('a, 'b, 'c) t -> f:('d -> 'b) -> ('a, 'd, 'c) t
val map' : ('a, 'b, 'c) t -> f:(Location.t -> 'd -> 'b) -> ('a, 'd, 'c) t
val map_result : ('a, 'b, 'c) t -> f:('c -> 'd) -> ('a, 'b, 'd) t

(** Same as [map] *)
val ( >>| ) : ('a, 'b, 'c) t -> ('d -> 'b) -> ('a, 'd, 'c) t

val map0 : ('a,               'b, 'c) t -> f:               'v  -> ('a, 'v -> 'b, 'c) t
val map1 : ('a, 'v1 ->        'b, 'c) t -> f:('v1 ->        'v) -> ('a, 'v -> 'b, 'c) t
val map2 : ('a, 'v1 -> 'v2 -> 'b, 'c) t -> f:('v1 -> 'v2 -> 'v) -> ('a, 'v -> 'b, 'c) t

val map0' : ('a,               'b, 'c) t -> f:(Location.t ->               'v) -> ('a, 'v -> 'b, 'c) t
val map1' : ('a, 'v1 ->        'b, 'c) t -> f:(Location.t -> 'v1 ->        'v) -> ('a, 'v -> 'b, 'c) t
val map2' : ('a, 'v1 -> 'v2 -> 'b, 'c) t -> f:(Location.t -> 'v1 -> 'v2 -> 'v) -> ('a, 'v -> 'b, 'c) t

val nil : (_ list, 'a, 'a) t
val ( ^:: ) : ('a, 'b, 'c) t -> ('a list, 'c, 'd) t -> ('a list, 'b, 'd) t
val many : ('a, 'b -> 'b, 'c) t -> ('a list, 'c list -> 'd, 'd) t

val int       : int       -> (int       , 'a, 'a) t
val char      : char      -> (char      , 'a, 'a) t
val string    : string    -> (string    , 'a, 'a) t
val float     : float     -> (float     , 'a, 'a) t
val int32     : int32     -> (int32     , 'a, 'a) t
val int64     : int64     -> (int64     , 'a, 'a) t
val nativeint : nativeint -> (nativeint , 'a, 'a) t
val bool      : bool      -> (bool      , 'a, 'a) t

val cst
  :  to_string:('a -> string)
  -> ?equal:('a -> 'a -> bool)
  -> 'a
  -> ('a, 'b, 'b) t

val none : (_ option, 'a, 'a) t
val some : ('a, 'b, 'c) t -> ('a option, 'b, 'c) t

val pair : ('a1, 'b, 'c) t -> ('a2, 'c, 'd) t -> ('a1 * 'a2, 'b, 'd) t
val ( ** ) : ('a1, 'b, 'c) t -> ('a2, 'c, 'd) t -> ('a1 * 'a2, 'b, 'd) t
val triple
  :  ('a1, 'b, 'c) t
  -> ('a2, 'c, 'd) t
  -> ('a3, 'd, 'e) t
  -> ('a1 * 'a2 * 'a3, 'b, 'e) t

val loc : ('a, 'b, 'c) t -> ('a Loc.t, 'b, 'c) t

val pack0 : ('a, 'b, 'c) t -> ('a, unit -> 'b, 'c) t
val pack2 : ('a, 'b -> 'c -> 'd, 'e) t -> ('a, 'b * 'c -> 'd, 'e) t
val pack3 : ('a, 'b -> 'c -> 'd -> 'e, 'f) t -> ('a, 'b * 'c * 'd -> 'e, 'f) t

(** AST patterns for each constructur/record of the parsetree are generated in the same
    way AST builders are generated. In addition, for every {it wrapper} we generate a
    pattern to match the [loc] and [attributes] fields. For instanct for the [expression]
    type:

    {[
      val pexp_loc
        :  (Location.t, 'a, 'b) t
        -> (expression, 'b, 'c) t
        -> (expression, 'a, 'c) t

      val pexp_attributes
        :  (attributes, 'a, 'b) t
        -> (expression, 'b, 'c) t
        -> (expression, 'a, 'c) t
    ]}
*)
include module type of Ast_pattern_generated

val true_  : (bool, 'a, 'a) t
val false_ : (bool, 'a, 'a) t

val eint       : (int       , 'a, 'b) t -> (expression, 'a, 'b) t
val echar      : (char      , 'a, 'b) t -> (expression, 'a, 'b) t
val estring    : (string    , 'a, 'b) t -> (expression, 'a, 'b) t
val efloat     : (string    , 'a, 'b) t -> (expression, 'a, 'b) t
val eint32     : (int32     , 'a, 'b) t -> (expression, 'a, 'b) t
val eint64     : (int64     , 'a, 'b) t -> (expression, 'a, 'b) t
val enativeint : (nativeint , 'a, 'b) t -> (expression, 'a, 'b) t

val pint       : (int       , 'a, 'b) t -> (pattern, 'a, 'b) t
val pchar      : (char      , 'a, 'b) t -> (pattern, 'a, 'b) t
val pstring    : (string    , 'a, 'b) t -> (pattern, 'a, 'b) t
val pfloat     : (string    , 'a, 'b) t -> (pattern, 'a, 'b) t
val pint32     : (int32     , 'a, 'b) t -> (pattern, 'a, 'b) t
val pint64     : (int64     , 'a, 'b) t -> (pattern, 'a, 'b) t
val pnativeint : (nativeint , 'a, 'b) t -> (pattern, 'a, 'b) t

val single_expr_payload : (expression, 'a, 'b) t -> (payload, 'a, 'b) t

val no_label : (expression, 'a, 'b) t -> (Asttypes.arg_label * expression, 'a, 'b) t

val attribute : name:(string, 'a, 'b) t -> payload:(payload, 'b, 'c) t -> (attribute, 'a, 'c) t
val extension : (string, 'a, 'b) t -> (payload, 'b, 'c) t -> (extension, 'a, 'c) t

val elist : (expression, 'a -> 'a, 'b) t -> (expression, 'b list -> 'c, 'c) t

type context
val of_func : (context -> Location.t -> 'a -> 'b -> 'c) -> ('a, 'b, 'c) t
val to_func : ('a, 'b, 'c) t -> (context -> Location.t -> 'a -> 'b -> 'c)