File: arbitrarySized.ml

package info (click to toggle)
coq-quickchick 2.1.1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 2,432 kB
  • sloc: ml: 4,367; ansic: 789; makefile: 388; sh: 27; python: 4; perl: 2; lisp: 2
file content (211 lines) | stat: -rw-r--r-- 7,967 bytes parent folder | download | duplicates (4)
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
open Util
open GenericLib
open GenLib

let arbitrarySized_decl (types : (ty_ctr * ty_param list * ctr_rep list) list) : (ty_ctr -> var list -> coq_expr) * ((var * arg list * var * coq_expr * coq_expr) list) =
  let impl_function_names : (ty_ctr * var) list =
    List.map (fun (ty, _, _) ->
      let type_name = ty_ctr_to_string ty in
      let function_name = fresh_name ("arbitrarySized_impl_" ^ type_name) in

      (ty, function_name)
    ) types in

  let generate_arbitrarySized_function ((ty, ty_params, ctors) : (ty_ctr * ty_param list * ctr_rep list)) : var * arg list * var * coq_expr * coq_expr =
    let function_name = List.assoc ty impl_function_names in

    let coqTyParams = List.map gTyParam ty_params in
    let full_type = gApp ~explicit:true (gTyCtr ty) coqTyParams in

    let arg = fresh_name "size" in
    let arg_type = (gInject "Coq.Init.Datatypes.nat") in

    (* G ty *)
    let return_type = gApp (gInject "QuickChick.Generators.G") [full_type] in

    let find_ty_ctr = function
    | TyCtr (ty_ctr', _) -> List.assoc_opt ty_ctr' impl_function_names
    | _ -> None in

    (* a base branch is a constructor that doesn't require our ty_ctr to be used *)
    let is_base_branch ty =
      fold_ty' (fun b ty' -> b && (None = (find_ty_ctr ty'))) true ty in
    let base_branches =
      List.filter (fun (_, ty) -> is_base_branch ty) ctors in

    let create_for_branch size (ctr, ty) =
      let rec aux i acc ty : coq_expr =
        match ty with
        | Arrow (ty1, ty2) ->
            bindGen
              (match find_ty_ctr ty1 with
                | Some name -> gApp (gVar name) [gVar size]
                | None -> gInject "arbitrary")
              (Printf.sprintf "p%d" i)
              (fun pi -> aux (i+1) ((gVar pi) :: acc) ty2)
        | _ -> returnGen (gApp ~explicit:true (gCtr ctr) (coqTyParams @ List.rev acc))
      in aux 0 [] ty in
    let body = gMatch (gVar arg) [
      (
        injectCtr "O", [],
        fun _ -> oneofThunked (List.map (create_for_branch arg) base_branches)
      );
      (
        injectCtr "S", ["size'"],
        fun [size'] ->
          frequencyThunked (List.map (fun (ctr, ty') ->
            (Weightmap.lookup_weight (is_base_branch ty') ctr size', create_for_branch size' (ctr, ty'))
          ) ctors)
      )
    ] in
    debug_coq_expr body;

    (function_name, [gArg ~assumName:(gVar arg) ~assumType:arg_type ()], arg, return_type, body) in

  let functions = List.map generate_arbitrarySized_function types in

  (* returns {| arbitrarySized := @arbitrarySized_impl_... |} *)
  let instance_record ty_ctr ivars : coq_expr =
    let impl_function_name = List.assoc ty_ctr impl_function_names in
    let implicit_arguments = List.map gVar ivars in

    gRecord [
      ("arbitrarySized",
        gApp ~explicit:true (gVar impl_function_name) implicit_arguments)
    ] in
  
  (instance_record, functions)

let rec replace v x = function
  | [] -> []
  | y::ys -> if y = v
      then x::ys
      else y::(replace v x ys)

let shrink_decl (types : (ty_ctr * ty_param list * ctr_rep list) list) : (ty_ctr -> var list -> coq_expr) * ((var * arg list * var * coq_expr * coq_expr) list) =
  let impl_function_names : (ty_ctr * var) list =
    List.map (fun (ty, _, _) -> 
      let type_name = ty_ctr_to_string ty in
      let function_name = fresh_name ("shrink_impl_" ^ type_name) in

      (ty, function_name)
    ) types in

  let generate_shrink_function ((ty, ty_params, ctors) : (ty_ctr * ty_param list * ctr_rep list)) : var * arg list * var * coq_expr * coq_expr =
    let function_name = List.assoc ty impl_function_names in

    let coqTyParams = List.map gTyParam ty_params in
    let full_type = gApp ~explicit:true (gTyCtr ty) coqTyParams in

    let arg = fresh_name "x" in
    let arg_type = full_type in

    (* full_type list *)
    let return_type = gApp (gInject "Coq.Init.Datatypes.list") [full_type] in

    let is_current_ty_crt = function
    | TyCtr (ty_ctr', _) -> ty = ty_ctr'
    | _ -> false in

    let find_ty_ctr = function
    | TyCtr (ty_ctr', _) -> List.assoc_opt ty_ctr' impl_function_names
    | _ -> None in

    let create_branch (ctr, ty) = (
      ctr,
      generate_names_from_type "p" ty,
      fold_ty_vars
        (fun allParams v ty' ->
          let liftNth =
            gFun ["shrunk"]
            (fun [shrunk] ->
              gApp ~explicit:true (gCtr ctr) (coqTyParams @ (replace (gVar v) (gVar shrunk) (List.map gVar allParams))))
          in

          lst_appends (match find_ty_ctr ty' with
          | Some name ->
              if is_current_ty_crt ty'
                (* [[v], List.map liftNth (name v)] *)
                then [ gList [gVar v] ; gApp (gInject "List.map") [liftNth; gApp (gVar name) [gVar v]] ]
                (* [List.map liftNth (name v)] *)
                else [ gApp (gInject "List.map") [liftNth; gApp (gVar name) [gVar v]] ]
            (* [List.map liftNth (shrink v)] *)
            | None -> [ gApp (gInject "List.map") [liftNth; gApp (gInject "shrink") [gVar v]] ]))
        lst_append
        list_nil ty
    ) in
    let body = gMatch (gVar arg) (List.map create_branch ctors) in
    debug_coq_expr body;

    (function_name, [gArg ~assumName:(gVar arg) ~assumType:arg_type ()], arg, return_type, body) in

  let functions = List.map generate_shrink_function types in

  (* returns {| shrink := @show_impl_... |} *)
  let instance_record ty_ctr ivars : coq_expr =
    let impl_function_name = List.assoc ty_ctr impl_function_names in
    let implicit_arguments = List.map gVar ivars in

    gRecord [
      ("shrink",
        gApp ~explicit:true (gVar impl_function_name) implicit_arguments)
    ] in
  
  (instance_record, functions)

let show_decl (types : (ty_ctr * ty_param list * ctr_rep list) list) : (ty_ctr -> var list -> coq_expr) * ((var * arg list * var * coq_expr * coq_expr) list) =
  let impl_function_names : (ty_ctr * var) list =
    List.map (fun (ty, _, _) -> 
      let type_name = ty_ctr_to_string ty in
      let function_name = fresh_name ("show_impl_" ^ type_name) in

      (ty, function_name)
    ) types in

  let generate_show_function ((ty, ty_params, ctors) : (ty_ctr * ty_param list * ctr_rep list)) : var * arg list * var * coq_expr * coq_expr =
    let function_name = List.assoc ty impl_function_names in

    let coqTyParams = List.map gTyParam ty_params in
    let full_type = gApp ~explicit:true (gTyCtr ty) coqTyParams in

    let arg = fresh_name "x" in
    let arg_type = full_type in

    let return_type = gInject "Coq.Strings.String.string" in

    let find_ty_ctr = function
    | TyCtr (ty_ctr', _) -> List.assoc_opt ty_ctr' impl_function_names
    | _ -> None in

    let branch (ctr, ty) = (
        ctr,
        generate_names_from_type "p" ty,
        fun vs -> match vs with
          | [] -> gStr (constructor_to_string ctr)
          | _ -> str_append
                  (gStr (constructor_to_string ctr ^ " "))
                  (fold_ty_vars
                    (fun _ v ty' ->
                      smart_paren @@
                      gApp (match find_ty_ctr ty' with
                        | Some name -> gVar name
                        | _ -> gInject "show"
                      ) [gVar v])
                    (fun s1 s2 ->
                      if s2 = emptyString then s1
                      else str_appends [s1; gStr " "; s2])
                    emptyString ty vs)
      ) in
    (* match x with | Ctr p0 p1 ... pn -> "Ctr " ++ (...) ++ " " ++ (...) *)
    let body = gMatch (gVar arg) (List.map branch ctors) in

    (function_name, [gArg ~assumName:(gVar arg) ~assumType:arg_type ()], arg, return_type, body) in

  let functions = List.map generate_show_function types in

  (* returns {| show := show_impl_... |} *)
  let instance_record ty_ctr _ivars : coq_expr =
    let impl_function_name = List.assoc ty_ctr impl_function_names in
    gRecord [("show", gVar impl_function_name)] in
  
  (instance_record, functions)