Module Ppxlib.Ast_pattern
First class AST patterns
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:
- 'ais the type of value matched by the pattern
- 'bis the continuation, for instance for a pattern that captures an- intand a- string,- 'bwill be- int -> string -> _
- 'cis the result of the continuation.
val parse : 
  ('a, 'b, 'c) t ->
  Location.t ->
  ?on_error:(unit -> 'c) ->
  'a ->
  'b ->
  'cMatches a value against a pattern.
val __ : ('a, 'a -> 'b, 'b) tPattern that captures its input.
val __' : ('a, 'a Loc.t -> 'b, 'b) tSame 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 alt : ('a, 'b, 'c) t -> ('a, 'b, 'c) t -> ('a, 'b, 'c) talt stands for `alternatives'. It matches either the first pattern or the second one.
val alt_option : 
  ('a, 'v -> 'b, 'c) t ->
  ('a, 'b, 'c) t ->
  ('a, 'v option -> 'b, 'c) tSame as alt, for the common case where the left-hand-side captures a value but not the right-hand-side.
val (|||) : ('a, 'b, 'c) t -> ('a, 'b, 'c) t -> ('a, 'b, 'c) tval map : ('a, 'b, 'c) t -> f:('d -> 'b) -> ('a, 'd, 'c) tval map' : ('a, 'b, 'c) t -> f:(Location.t -> 'd -> 'b) -> ('a, 'd, 'c) tval map_result : ('a, 'b, 'c) t -> f:('c -> 'd) -> ('a, 'b, 'd) tval (>>|) : ('a, 'b, 'c) t -> ('d -> 'b) -> ('a, 'd, 'c) tval map0 : ('a, 'b, 'c) t -> f:'v -> ('a, 'v -> 'b, 'c) tval map1 : ('a, 'v1 -> 'b, 'c) t -> f:('v1 -> 'v) -> ('a, 'v -> 'b, 'c) tval map2 : 
  ('a, 'v1 -> 'v2 -> 'b, 'c) t ->
  f:('v1 -> 'v2 -> 'v) ->
  ('a, 'v -> 'b, 'c) tval map0' : ('a, 'b, 'c) t -> f:(Location.t -> 'v) -> ('a, 'v -> 'b, 'c) tval map1' : 
  ('a, 'v1 -> 'b, 'c) t ->
  f:(Location.t -> 'v1 -> 'v) ->
  ('a, 'v -> 'b, 'c) tval map2' : 
  ('a, 'v1 -> 'v2 -> 'b, 'c) t ->
  f:(Location.t -> 'v1 -> 'v2 -> 'v) ->
  ('a, 'v -> 'b, 'c) tval nil : (_ list, 'a, 'a) tval (^::) : ('a, 'b, 'c) t -> ('a list, 'c, 'd) t -> ('a list, 'b, 'd) tval many : ('a, 'b -> 'b, 'c) t -> ('a list, 'c list -> 'd, 'd) tval int : int -> (int, 'a, 'a) tval char : char -> (char, 'a, 'a) tval string : string -> (string, 'a, 'a) tval float : float -> (float, 'a, 'a) tval int32 : int32 -> (int32, 'a, 'a) tval int64 : int64 -> (int64, 'a, 'a) tval nativeint : nativeint -> (nativeint, 'a, 'a) tval bool : bool -> (bool, 'a, 'a) tval cst : 
  to_string:('a -> string) ->
  ?equal:('a -> 'a -> bool) ->
  'a ->
  ('a, 'b, 'b) tval none : (_ option, 'a, 'a) tval some : ('a, 'b, 'c) t -> ('a option, 'b, 'c) tval pair : ('a1, 'b, 'c) t -> ('a2, 'c, 'd) t -> ('a1 * 'a2, 'b, 'd) tval (**) : ('a1, 'b, 'c) t -> ('a2, 'c, 'd) t -> ('a1 * 'a2, 'b, 'd) tval triple : 
  ('a1, 'b, 'c) t ->
  ('a2, 'c, 'd) t ->
  ('a3, 'd, 'e) t ->
  ('a1 * 'a2 * 'a3, 'b, 'e) tval loc : ('a, 'b, 'c) t -> ('a Loc.t, 'b, 'c) tval pack0 : ('a, 'b, 'c) t -> ('a, unit -> 'b, 'c) tval pack2 : ('a, 'b -> 'c -> 'd, 'e) t -> ('a, ('b * 'c) -> 'd, 'e) tval pack3 : 
  ('a, 'b -> 'c -> 'd -> 'e, 'f) t ->
  ('a, ('b * 'c * 'd) -> 'e, 'f) tAST patterns for each constructor/record of the parsetree are generated in the same way AST builders are generated. In addition, for every wrapper we generate a pattern to match the loc and attributes fields. For instance 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
val pcl_fun : 
  (Astlib.Ast_412.Asttypes.arg_label, 'a, 'b) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.expression option, 'b, 'c) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.pattern, 'c, 'd) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.class_expr, 'd, 'e) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.class_expr, 'a, 'e) Ppxlib__.Ast_pattern0.tval class_infos : 
  virt:(Astlib.Ast_412.Asttypes.virtual_flag, 'a, 'b) Ppxlib__.Ast_pattern0.t ->
  params:
    ((Astlib.Ast_412.Parsetree.core_type
      * (Astlib.Ast_412.Asttypes.variance * Astlib.Ast_412.Asttypes.injectivity))
       list,
      'b,
      'c)
      Ppxlib__.Ast_pattern0.t ->
  name:(string, 'c, 'd) Ppxlib__.Ast_pattern0.t ->
  expr:('e, 'd, 'f) Ppxlib__.Ast_pattern0.t ->
  ('e Astlib.Ast_412.Parsetree.class_infos, 'a, 'f) Ppxlib__.Ast_pattern0.tval pconst_integer : 
  (string, 'a, 'b) Ppxlib__.Ast_pattern0.t ->
  (char option, 'b, 'c) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.constant, 'a, 'c) Ppxlib__.Ast_pattern0.tval pconst_float : 
  (string, 'a, 'b) Ppxlib__.Ast_pattern0.t ->
  (char option, 'b, 'c) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.constant, 'a, 'c) Ppxlib__.Ast_pattern0.tval pexp_fun : 
  (Astlib.Ast_412.Asttypes.arg_label, 'a, 'b) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.expression option, 'b, 'c) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.pattern, 'c, 'd) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.expression, 'd, 'e) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.expression, 'a, 'e) Ppxlib__.Ast_pattern0.tval pexp_for : 
  (Astlib.Ast_412.Parsetree.pattern, 'a, 'b) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.expression, 'b, 'c) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.expression, 'c, 'd) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Asttypes.direction_flag, 'd, 'e) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.expression, 'e, 'f) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.expression, 'a, 'f) Ppxlib__.Ast_pattern0.tval lident : 
  (string, 'a, 'b) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Longident.t, 'a, 'b) Ppxlib__.Ast_pattern0.tval position : 
  fname:(string, 'a, 'b) Ppxlib__.Ast_pattern0.t ->
  lnum:(int, 'b, 'c) Ppxlib__.Ast_pattern0.t ->
  bol:(int, 'c, 'd) Ppxlib__.Ast_pattern0.t ->
  cnum:(int, 'd, 'e) Ppxlib__.Ast_pattern0.t ->
  (Lexing.position, 'a, 'e) Ppxlib__.Ast_pattern0.tval type_declaration : 
  name:(string, 'a, 'b) Ppxlib__.Ast_pattern0.t ->
  params:
    ((Astlib.Ast_412.Parsetree.core_type
      * (Astlib.Ast_412.Asttypes.variance * Astlib.Ast_412.Asttypes.injectivity))
       list,
      'b,
      'c)
      Ppxlib__.Ast_pattern0.t ->
  cstrs:
    ((Astlib.Ast_412.Parsetree.core_type
      * Astlib.Ast_412.Parsetree.core_type
      * Astlib.Location.t)
       list,
      'c,
      'd)
      Ppxlib__.Ast_pattern0.t ->
  kind:(Astlib.Ast_412.Parsetree.type_kind, 'd, 'e) Ppxlib__.Ast_pattern0.t ->
  private_:
    (Astlib.Ast_412.Asttypes.private_flag, 'e, 'f) Ppxlib__.Ast_pattern0.t ->
  manifest:
    (Astlib.Ast_412.Parsetree.core_type option, 'f, 'g) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.type_declaration, 'a, 'g) Ppxlib__.Ast_pattern0.tval type_extension : 
  path:(Astlib.Longident.t, 'a, 'b) Ppxlib__.Ast_pattern0.t ->
  params:
    ((Astlib.Ast_412.Parsetree.core_type
      * (Astlib.Ast_412.Asttypes.variance * Astlib.Ast_412.Asttypes.injectivity))
       list,
      'b,
      'c)
      Ppxlib__.Ast_pattern0.t ->
  constructors:
    (Astlib.Ast_412.Parsetree.extension_constructor list, 'c, 'd)
      Ppxlib__.Ast_pattern0.t ->
  private_:
    (Astlib.Ast_412.Asttypes.private_flag, 'd, 'e) Ppxlib__.Ast_pattern0.t ->
  (Astlib.Ast_412.Parsetree.type_extension, 'a, 'e) Ppxlib__.Ast_pattern0.tval true_ : (bool, 'a, 'a) tval false_ : (bool, 'a, 'a) t