generate.bend
bend-kit-property@0.1.0.0 · 171 lines · 0 definitions
1# Pure generators: each takes a Rand.Rng and returns a value beside the next state.
2import Base
3# bend-kit-random@0.1.0.0
4import bend-kit-random@0.1.0.0/random.bend as Rand
5# bend-kit-bytes@0.3.1.0
6import bend-kit-bytes@0.3.1.0/bytes.bend as Bytes
7
8# A generator is `Rand.Rng -> T & Rand.Rng`. The same seed replays the same value:
9# (xs, r) = Gen.list(~U32, ~Gen.u32, 8, Rand.seed(42))
10# Sizes are capped at 1024 so lengths and Nats stay small enough to build and shrink.
11
12# n, capped at 1024.
13def cap(+n: U32) -> U32:
14 U32.min(n, 1024)
15
16# A uniform U32.
17def u32(r: Rand.Rng) -> U32 & Rand.Rng:
18 Rand.next(r)
19
20# A uniform U32 in [lo, hi); lo when hi <= lo + 1.
21def u32.range(+lo: U32, hi: U32, r: Rand.Rng) -> U32 & Rand.Rng:
22 Rand.range(lo, hi, r)
23
24# A uniform size in [0, min(max, 1024)].
25def size(+max: U32, r: Rand.Rng) -> U32 & Rand.Rng:
26 Rand.below((cap(max) + 1 : U32), r)
27
28def nat.fin(xr: U32 & Rand.Rng) -> Nat & Rand.Rng:
29 (x, r) = xr
30 (U32.to_nat(x), r)
31
32# A uniform Nat in [0, min(max, 1024)].
33def nat(+max: U32, r: Rand.Rng) -> Nat & Rand.Rng:
34 nat.fin(size(max, r))
35
36def char.fin(xr: U32 & Rand.Rng) -> Char & Rand.Rng:
37 (x, r) = xr
38 (Chr{x}, r)
39
40# A printable ASCII char, ' ' (32) through '~' (126).
41def char(r: Rand.Rng) -> Char & Rand.Rng:
42 char.fin(Rand.range(32, 127, r))
43
44# n + 1 chars prepended to acc; cr holds the next char.
45def string.go(n: Nat, acc: String, cr: Char & Rand.Rng) -> String & Rand.Rng:
46 match n:
47 case 0n:
48 (c, r) = cr
49 (SCon{c, acc}, r)
50 case 1n+p:
51 (c, r) = cr
52 string.go(p, SCon{c, acc}, char(r))
53
54# Exactly n printable ASCII chars.
55def string.of(n: Nat, r: Rand.Rng) -> String & Rand.Rng:
56 match n:
57 case 0n:
58 (SNil{}, r)
59 case 1n+p:
60 string.go(p, SNil{}, char(r))
61
62def string.fin(nr: Nat & Rand.Rng) -> String & Rand.Rng:
63 (n, r) = nr
64 string.of(n, r)
65
66# Printable ASCII of length in [0, min(max, 1024)].
67def string(+max: U32, r: Rand.Rng) -> String & Rand.Rng:
68 string.fin(nat(max, r))
69
70# Byte i and the n bytes after it; xr holds byte i (Bytes.set keeps its low 8 bits).
71def bytes.go(n: Nat, b: Bytes.Bytes, +i: U32, xr: U32 & Rand.Rng) -> Bytes.Bytes & Rand.Rng:
72 match n:
73 case 0n:
74 (x, r) = xr
75 (Bytes.set(b, i, x), r)
76 case 1n+p:
77 (x, r) = xr
78 bytes.go(p, Bytes.set(b, i, x), (i + 1 : U32), Rand.next(r))
79
80def bytes.start(n: Nat, +len: U32, r: Rand.Rng) -> Bytes.Bytes & Rand.Rng:
81 match n:
82 case 0n:
83 (Bytes.new(0), r)
84 case 1n+p:
85 bytes.go(p, Bytes.new(len), 0, Rand.next(r))
86
87# Exactly min(len, 1024) uniform octets, one draw each.
88def bytes.of(+len: U32, r: Rand.Rng) -> Bytes.Bytes & Rand.Rng:
89 +n = cap(len)
90 bytes.start(U32.to_nat(n), n, r)
91
92def bytes.fin(nr: U32 & Rand.Rng) -> Bytes.Bytes & Rand.Rng:
93 (n, r) = nr
94 bytes.of(n, r)
95
96# Uniform octets, length in [0, min(max, 1024)].
97def bytes(+max: U32, r: Rand.Rng) -> Bytes.Bytes & Rand.Rng:
98 bytes.fin(size(max, r))
99
100# n + 1 items prepended to acc; hr holds the next item.
101def list.go(~T: Data, ~gen: Rand.Rng -> T & Rand.Rng, n: Nat, acc: List<&2, T>, hr: T & Rand.Rng) -> List<&2, T> & Rand.Rng:
102 match n:
103 case 0n:
104 (h, r) = hr
105 (h <> acc, r)
106 case 1n+p:
107 (h, r) = hr
108 list.go(~T, ~gen, p, h <> acc, gen(r))
109
110# Exactly n items from gen. The first draw ends up last; replay is unaffected.
111def list.of(~T: Data, ~gen: Rand.Rng -> T & Rand.Rng, n: Nat, r: Rand.Rng) -> List<&2, T> & Rand.Rng:
112 match n:
113 case 0n:
114 (Nil{}, r)
115 case 1n+p:
116 list.go(~T, ~gen, p, Nil{}, gen(r))
117
118def list.fin(~T: Data, ~gen: Rand.Rng -> T & Rand.Rng, nr: Nat & Rand.Rng) -> List<&2, T> & Rand.Rng:
119 (n, r) = nr
120 list.of(~T, ~gen, n, r)
121
122# Items from gen, length in [0, min(max, 1024)].
123def list(~T: Data, ~gen: Rand.Rng -> T & Rand.Rng, +max: U32, r: Rand.Rng) -> List<&2, T> & Rand.Rng:
124 list.fin(~T, ~gen, nat(max, r))
125
126def maybe.some(~T: Data, hr: T & Rand.Rng) -> Maybe<&2, T> & Rand.Rng:
127 (h, r) = hr
128 (Some{h}, r)
129
130def maybe.if(~T: Data, ~gen: Rand.Rng -> T & Rand.Rng, none: Bool, r: Rand.Rng) -> Maybe<&2, T> & Rand.Rng:
131 match none:
132 case True{}:
133 (None{}, r)
134 case False{}:
135 maybe.some(~T, gen(r))
136
137def maybe.pick(~T: Data, ~gen: Rand.Rng -> T & Rand.Rng, xr: U32 & Rand.Rng) -> Maybe<&2, T> & Rand.Rng:
138 (+x, r) = xr
139 maybe.if(~T, ~gen, U32.is_eq(x, 0), r)
140
141# None one time in four, else Some from gen.
142def maybe(~T: Data, ~gen: Rand.Rng -> T & Rand.Rng, r: Rand.Rng) -> Maybe<&2, T> & Rand.Rng:
143 maybe.pick(~T, ~gen, Rand.below(4, r))
144
145def map.fin(~A: Type, ~B: Type, ~f: A -> B, ar: A & Rand.Rng) -> B & Rand.Rng:
146 (a, r) = ar
147 (f(a), r)
148
149# gen's value through f.
150def map(~A: Type, ~B: Type, ~f: A -> B, ~gen: Rand.Rng -> A & Rand.Rng, r: Rand.Rng) -> B & Rand.Rng:
151 map.fin(~A, ~B, ~f, gen(r))
152
153def bind.fin(~A: Type, ~B: Type, ~k: A -> Rand.Rng -> B & Rand.Rng, ar: A & Rand.Rng) -> B & Rand.Rng:
154 (a, r) = ar
155 k(a, r)
156
157# gen's value picks the next generator: bind(~Nat, ~List<&2, U32>, ~(r => Gen.nat(8, r)), ~(n => r => Gen.list.of(~U32, ~Gen.u32, n, r)), r).
158def bind(~A: Type, ~B: Type, ~gen: Rand.Rng -> A & Rand.Rng, ~k: A -> Rand.Rng -> B & Rand.Rng, r: Rand.Rng) -> B & Rand.Rng:
159 bind.fin(~A, ~B, ~k, gen(r))
160
161def pair.snd(~A: Type, ~B: Type, a: A, br: B & Rand.Rng) -> (A & B) & Rand.Rng:
162 (b, r) = br
163 ((a, b), r)
164
165def pair.fst(~A: Type, ~B: Type, ~gb: Rand.Rng -> B & Rand.Rng, ar: A & Rand.Rng) -> (A & B) & Rand.Rng:
166 (a, r) = ar
167 pair.snd(~A, ~B, a, gb(r))
168
169# ga's value then gb's, drawn in that order.
170def pair(~A: Type, ~B: Type, ~ga: Rand.Rng -> A & Rand.Rng, ~gb: Rand.Rng -> B & Rand.Rng, r: Rand.Rng) -> (A & B) & Rand.Rng:
171 pair.fst(~A, ~B, ~gb, ga(r))