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
[@@@ocaml.text "/*"]
(** Copyright 2021-2026, Kakadu and contributors *)
(** SPDX-License-Identifier: LGPL-3.0-or-later *)
[@@@ocaml.text "/*"]
open Miniml_lib
open Ast
open QCheck
(* let gen_varname = Gen.oneof_list [ "a"; "b"; "c"; "d"; "e"; "f"; "h"; "g" ] *)
let gen_varname =
let open Gen in
let first_char = char_range 'a' 'z' in
let other_char =
oneof
[ char_range 'a' 'z'; char_range 'A' 'Z'; char_range '0' '9'; char_range '_' '_' ]
in
let size = int_range 6 20 in
map
(fun (c, rest) -> String.of_seq (List.to_seq (c :: rest)))
(pair first_char (list_size size other_char))
;;
let gen_binop = Gen.(oneof_list [ Plus; Minus; Times; Divide; Eq; Neq; Lt; Gt; Le; Ge ])
let rec gen_ast depth =
let open Gen in
let base = [ map (fun v -> Var v) gen_varname; map (fun i -> Int i) nat ] in
if depth = 0
then oneof base
else
oneof
(base
@ [ map2 (fun x t -> Abs (x, t)) gen_varname (gen_ast (depth - 1))
; map2 (fun f g -> App (f, g)) (gen_ast (depth - 1)) (gen_ast (depth - 1))
; map3
(fun op a b -> Binop (op, a, b))
gen_binop
(gen_ast (depth - 1))
(gen_ast (depth - 1))
; map (fun e -> Neg e) (gen_ast (depth - 1))
; map3
(fun c t e -> If (c, t, e))
(gen_ast (depth - 1))
(gen_ast (depth - 1))
(gen_ast (depth - 1))
; map3
(fun p e1 e2 -> Let (Nonrec, p, e1, e2))
gen_varname
(gen_ast (depth - 1))
(gen_ast (depth - 1))
; map4
(fun n x b e2 -> Let (Rec, n, Abs (x, b), e2))
gen_varname
gen_varname
(gen_ast (depth - 1))
(gen_ast (depth - 1))
])
;;
let equal = Ast.equal String.equal
let pprint ast = Format.asprintf "%a" Pprintast.pp ast
let gen_ast_sized = Gen.sized (fun s -> gen_ast (3 + (s mod 3)))
let arb_ast = make ~print:pprint gen_ast_sized
let pprint_test =
Test.make ~count:1000 ~name:"pprint& parse test" arb_ast (fun ast ->
match Parser.parse (pprint ast) with
| Result.Ok ast' -> equal ast ast'
| Result.Error _ -> false)
;;
(*TODO: gen_varname can generate a keyword*)
let _ = QCheck_runner.run_tests [ pprint_test ]