Source file ppx_observer_expander.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
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
open! Import

let any ~loc = [%expr Ppx_quickcheck_runtime.Base_quickcheck.Observer.opaque]

let arrow
  ~observer_of_core_type
  ~generator_of_core_type
  ~loc
  ~arg_label
  ~input_type
  ~output_type
  =
  let input_generator =
    match arg_label with
    | Nolabel | Labelled _ -> generator_of_core_type input_type
    | Optional _ ->
      [%expr
        Ppx_quickcheck_runtime.Base_quickcheck.Generator.option
          [%e generator_of_core_type input_type]]
  in
  let output_observer = observer_of_core_type output_type in
  let unlabelled =
    [%expr
      Ppx_quickcheck_runtime.Base_quickcheck.Observer.fn
        [%e input_generator]
        [%e output_observer]]
  in
  match arg_label with
  | Nolabel -> unlabelled
  | Labelled _ | Optional _ ->
    [%expr
      Ppx_quickcheck_runtime.Base_quickcheck.Observer.unmap
        ~f:[%e fn_map_label ~loc ~from:arg_label ~to_:Nolabel]
        [%e unlabelled]]
;;

let compound_hash ~loc ~size_expr ~hash_expr ~hash_pat ~observer_exprs ~field_exprs =
  let alist = List.zip_exn observer_exprs field_exprs in
  List.fold_right alist ~init:hash_expr ~f:(fun (observer_expr, field_expr) body_expr ->
    [%expr
      let [%p hash_pat] =
        Ppx_quickcheck_runtime.Base_quickcheck.Observer.observe
          [%e observer_expr]
          [%e field_expr]
          ~size:[%e size_expr]
          ~hash:[%e hash_expr]
      in
      [%e body_expr]])
;;

let compound
  (type field)
  ~observer_of_core_type
  ~loc
  ~fields
  (module Field : Field_syntax.S with type ast = field)
  =
  let pat, exp = gensym "x" loc in
  let fields = List.map fields ~f:Field.create in
  let field_pats, field_exprs = gensyms "x" (List.map fields ~f:Field.location) in
  let record_pat = Field.pattern fields ~loc field_pats in
  let observer_exprs =
    List.map fields ~f:(fun field -> observer_of_core_type (Field.core_type field))
  in
  let size_pat, size_expr = gensym "size" loc in
  let hash_pat, hash_expr = gensym "hash" loc in
  [%expr
    Ppx_quickcheck_runtime.Base_quickcheck.Observer.create
      (fun [%p pat] ~size:[%p size_pat] ~hash:[%p hash_pat] ->
      let [%p record_pat] = [%e exp] in
      [%e compound_hash ~loc ~size_expr ~hash_expr ~hash_pat ~observer_exprs ~field_exprs])]
;;

let variant
  (type clause)
  ~observer_of_core_type
  ~loc
  ~clauses
  (module Clause : Clause_syntax.S with type ast = clause)
  =
  let clauses = Clause.create_list clauses in
  let pat, expr = gensym "x" loc in
  let size_pat, size_expr = gensym "size" loc in
  let hash_pat, hash_expr = gensym "hash" loc in
  [%expr
    Ppx_quickcheck_runtime.Base_quickcheck.Observer.create
      (fun [%p pat] ~size:[%p size_pat] ~hash:[%p hash_pat] ->
      [%e
        pexp_match
          ~loc
          expr
          (List.map clauses ~f:(fun clause ->
             let core_type_list = Clause.core_type_list clause in
             let observer_exprs = List.map core_type_list ~f:observer_of_core_type in
             let field_pats, field_exprs =
               gensyms
                 "x"
                 (List.map core_type_list ~f:(fun core_type -> core_type.ptyp_loc))
             in
             let lhs = Clause.pattern clause ~loc field_pats in
             let body =
               compound_hash
                 ~loc
                 ~size_expr
                 ~hash_expr
                 ~hash_pat
                 ~observer_exprs
                 ~field_exprs
             in
             let rhs =
               match Clause.salt clause with
               | None -> body
               | Some salt ->
                 pexp_let
                   ~loc
                   Nonrecursive
                   [ value_binding
                       ~loc
                       ~pat:hash_pat
                       ~expr:
                         [%expr
                           Ppx_quickcheck_runtime.Base.hash_fold_int
                             [%e hash_expr]
                             [%e eint ~loc salt]]
                   ]
                   body
             in
             case ~lhs ~guard:None ~rhs))])]
;;