Old Fuzzer, new Sampler
This commit is contained in:
parent
c0fabcd26a
commit
84a0abeb0f
|
@ -258,7 +258,7 @@ fn str_to_keyword(word: &str) -> Option<Token> {
|
|||
"or" => Some(Token::Or),
|
||||
"validator" => Some(Token::Validator),
|
||||
"via" => Some(Token::Via),
|
||||
"benchmark" => Some(Token::Benchmark),
|
||||
"bench" => Some(Token::Benchmark),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ pub const BOOL: &str = "Bool";
|
|||
pub const BOOL_CONSTRUCTORS: &[&str] = &["False", "True"];
|
||||
pub const BYTE_ARRAY: &str = "ByteArray";
|
||||
pub const DATA: &str = "Data";
|
||||
pub const GENERATOR: &str = "Generator";
|
||||
pub const FUZZER: &str = "Fuzzer";
|
||||
pub const SAMPLER: &str = "Sampler";
|
||||
pub const G1_ELEMENT: &str = "G1Element";
|
||||
pub const G2_ELEMENT: &str = "G2Element";
|
||||
pub const INT: &str = "Int";
|
||||
|
@ -179,7 +180,7 @@ impl Type {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn generator(c: Rc<Type>, a: Rc<Type>) -> Rc<Type> {
|
||||
pub fn fuzzer(a: Rc<Type>) -> Rc<Type> {
|
||||
let prng_annotation = Annotation::Constructor {
|
||||
location: Span::empty(),
|
||||
module: None,
|
||||
|
@ -188,15 +189,15 @@ impl Type {
|
|||
};
|
||||
|
||||
Rc::new(Type::Fn {
|
||||
args: vec![c, Type::prng()],
|
||||
args: vec![Type::prng()],
|
||||
ret: Type::option(Type::tuple(vec![Type::prng(), a])),
|
||||
alias: Some(
|
||||
TypeAliasAnnotation {
|
||||
alias: GENERATOR.to_string(),
|
||||
parameters: vec!["c".to_string(), "a".to_string()],
|
||||
alias: FUZZER.to_string(),
|
||||
parameters: vec!["a".to_string()],
|
||||
annotation: Annotation::Fn {
|
||||
location: Span::empty(),
|
||||
arguments: vec![Annotation::data(Span::empty()), prng_annotation.clone()],
|
||||
arguments: vec![prng_annotation.clone()],
|
||||
ret: Annotation::Constructor {
|
||||
location: Span::empty(),
|
||||
module: None,
|
||||
|
@ -220,6 +221,53 @@ impl Type {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn sampler(a: Rc<Type>) -> Rc<Type> {
|
||||
let prng_annotation = Annotation::Constructor {
|
||||
location: Span::empty(),
|
||||
module: None,
|
||||
name: PRNG.to_string(),
|
||||
arguments: vec![],
|
||||
};
|
||||
|
||||
Rc::new(Type::Fn {
|
||||
args: vec![Type::int()],
|
||||
ret: Type::fuzzer(a),
|
||||
alias: Some(
|
||||
TypeAliasAnnotation {
|
||||
alias: SAMPLER.to_string(),
|
||||
parameters: vec!["a".to_string()],
|
||||
annotation: Annotation::Fn {
|
||||
location: Span::empty(),
|
||||
arguments: vec![Annotation::int(Span::empty())],
|
||||
ret: Annotation::Fn {
|
||||
location: Span::empty(),
|
||||
arguments: vec![prng_annotation.clone()],
|
||||
ret: Annotation::Constructor {
|
||||
location: Span::empty(),
|
||||
module: None,
|
||||
name: OPTION.to_string(),
|
||||
arguments: vec![Annotation::Tuple {
|
||||
location: Span::empty(),
|
||||
elems: vec![
|
||||
prng_annotation,
|
||||
Annotation::Var {
|
||||
location: Span::empty(),
|
||||
name: "a".to_string(),
|
||||
},
|
||||
],
|
||||
}],
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn map(k: Rc<Type>, v: Rc<Type>) -> Rc<Type> {
|
||||
Rc::new(Type::App {
|
||||
public: true,
|
||||
|
|
|
@ -477,23 +477,38 @@ pub fn prelude(id_gen: &IdGenerator) -> TypeInfo {
|
|||
),
|
||||
);
|
||||
|
||||
// Generator
|
||||
// Fuzzer
|
||||
//
|
||||
// pub type Generator<c, a> =
|
||||
// fn(Data, PRNG) -> Option<(PRNG, a)>
|
||||
let generator_context = Type::generic_var(id_gen.next());
|
||||
let generator_value = Type::generic_var(id_gen.next());
|
||||
// pub type Fuzzer<a> =
|
||||
// fn(PRNG) -> Option<(PRNG, a)>
|
||||
let fuzzer_generic = Type::generic_var(id_gen.next());
|
||||
prelude.types.insert(
|
||||
well_known::GENERATOR.to_string(),
|
||||
well_known::FUZZER.to_string(),
|
||||
TypeConstructor {
|
||||
location: Span::empty(),
|
||||
parameters: vec![generator_context.clone(), generator_value.clone()],
|
||||
tipo: Type::generator(generator_context, generator_value),
|
||||
parameters: vec![fuzzer_generic.clone()],
|
||||
tipo: Type::fuzzer(fuzzer_generic),
|
||||
module: "".to_string(),
|
||||
public: true,
|
||||
},
|
||||
);
|
||||
|
||||
// Sampler
|
||||
//
|
||||
// pub type Sampler<a> =
|
||||
// fn(Int) -> Fuzzer<a>
|
||||
let sampler_generic = Type::generic_var(id_gen.next());
|
||||
prelude.types.insert(
|
||||
well_known::SAMPLER.to_string(),
|
||||
TypeConstructor {
|
||||
location: Span::empty(),
|
||||
parameters: vec![sampler_generic.clone()],
|
||||
tipo: Type::sampler(sampler_generic),
|
||||
module: "".to_string(),
|
||||
public: true,
|
||||
}
|
||||
);
|
||||
|
||||
prelude
|
||||
}
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ mod tests {
|
|||
fn def_benchmark() {
|
||||
assert_definition!(
|
||||
r#"
|
||||
benchmark foo(x via fuzz.any_int) {
|
||||
bench foo(x via fuzz.any_int) {
|
||||
True
|
||||
}
|
||||
"#
|
||||
|
@ -146,7 +146,7 @@ mod tests {
|
|||
fn def_invalid_benchmark() {
|
||||
assert_definition!(
|
||||
r#"
|
||||
benchmark foo(x via f, y via g) {
|
||||
bench foo(x via f, y via g) {
|
||||
True
|
||||
}
|
||||
"#
|
||||
|
@ -157,7 +157,7 @@ mod tests {
|
|||
fn def_benchmark_annotated_fuzzer() {
|
||||
assert_definition!(
|
||||
r#"
|
||||
benchmark foo(x: Int via foo()) {
|
||||
bench foo(x: Int via foo()) {
|
||||
True
|
||||
}
|
||||
"#
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
source: crates/aiken-lang/src/parser/definition/benchmark.rs
|
||||
assertion_line: 136
|
||||
description: "Code:\n\nbenchmark foo(x via fuzz.any_int) {\n True\n}\n"
|
||||
description: "Code:\n\nbench foo(x via fuzz.any_int) {\n True\n}\n"
|
||||
snapshot_kind: text
|
||||
---
|
||||
Benchmark(
|
||||
|
@ -13,35 +13,35 @@ Benchmark(
|
|||
Named {
|
||||
name: "x",
|
||||
label: "x",
|
||||
location: 14..15,
|
||||
location: 10..11,
|
||||
},
|
||||
),
|
||||
location: 14..15,
|
||||
location: 10..11,
|
||||
annotation: None,
|
||||
doc: None,
|
||||
is_validator_param: false,
|
||||
},
|
||||
via: FieldAccess {
|
||||
location: 20..32,
|
||||
location: 16..28,
|
||||
label: "any_int",
|
||||
container: Var {
|
||||
location: 20..24,
|
||||
location: 16..20,
|
||||
name: "fuzz",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
body: Var {
|
||||
location: 40..44,
|
||||
location: 36..40,
|
||||
name: "True",
|
||||
},
|
||||
doc: None,
|
||||
location: 0..33,
|
||||
location: 0..29,
|
||||
name: "foo",
|
||||
public: false,
|
||||
return_annotation: None,
|
||||
return_type: (),
|
||||
end_position: 45,
|
||||
end_position: 41,
|
||||
on_test_failure: FailImmediately,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
source: crates/aiken-lang/src/parser/definition/benchmark.rs
|
||||
assertion_line: 158
|
||||
description: "Code:\n\nbenchmark foo(x: Int via foo()) {\n True\n}\n"
|
||||
description: "Code:\n\nbench foo(x: Int via foo()) {\n True\n}\n"
|
||||
snapshot_kind: text
|
||||
---
|
||||
Benchmark(
|
||||
|
@ -13,13 +13,13 @@ Benchmark(
|
|||
Named {
|
||||
name: "x",
|
||||
label: "x",
|
||||
location: 14..15,
|
||||
location: 10..11,
|
||||
},
|
||||
),
|
||||
location: 14..20,
|
||||
location: 10..16,
|
||||
annotation: Some(
|
||||
Constructor {
|
||||
location: 17..20,
|
||||
location: 13..16,
|
||||
module: None,
|
||||
name: "Int",
|
||||
arguments: [],
|
||||
|
@ -31,24 +31,24 @@ Benchmark(
|
|||
via: Call {
|
||||
arguments: [],
|
||||
fun: Var {
|
||||
location: 25..28,
|
||||
location: 21..24,
|
||||
name: "foo",
|
||||
},
|
||||
location: 25..30,
|
||||
location: 21..26,
|
||||
},
|
||||
},
|
||||
],
|
||||
body: Var {
|
||||
location: 38..42,
|
||||
location: 34..38,
|
||||
name: "True",
|
||||
},
|
||||
doc: None,
|
||||
location: 0..31,
|
||||
location: 0..27,
|
||||
name: "foo",
|
||||
public: false,
|
||||
return_annotation: None,
|
||||
return_type: (),
|
||||
end_position: 43,
|
||||
end_position: 39,
|
||||
on_test_failure: FailImmediately,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
source: crates/aiken-lang/src/parser/definition/benchmark.rs
|
||||
assertion_line: 147
|
||||
description: "Code:\n\nbenchmark foo(x via f, y via g) {\n True\n}\n"
|
||||
description: "Code:\n\nbench foo(x via f, y via g) {\n True\n}\n"
|
||||
snapshot_kind: text
|
||||
---
|
||||
Benchmark(
|
||||
|
@ -13,16 +13,16 @@ Benchmark(
|
|||
Named {
|
||||
name: "x",
|
||||
label: "x",
|
||||
location: 14..15,
|
||||
location: 10..11,
|
||||
},
|
||||
),
|
||||
location: 14..15,
|
||||
location: 10..11,
|
||||
annotation: None,
|
||||
doc: None,
|
||||
is_validator_param: false,
|
||||
},
|
||||
via: Var {
|
||||
location: 20..21,
|
||||
location: 16..17,
|
||||
name: "f",
|
||||
},
|
||||
},
|
||||
|
@ -32,31 +32,31 @@ Benchmark(
|
|||
Named {
|
||||
name: "y",
|
||||
label: "y",
|
||||
location: 23..24,
|
||||
location: 19..20,
|
||||
},
|
||||
),
|
||||
location: 23..24,
|
||||
location: 19..20,
|
||||
annotation: None,
|
||||
doc: None,
|
||||
is_validator_param: false,
|
||||
},
|
||||
via: Var {
|
||||
location: 29..30,
|
||||
location: 25..26,
|
||||
name: "g",
|
||||
},
|
||||
},
|
||||
],
|
||||
body: Var {
|
||||
location: 38..42,
|
||||
location: 34..38,
|
||||
name: "True",
|
||||
},
|
||||
doc: None,
|
||||
location: 0..31,
|
||||
location: 0..27,
|
||||
name: "foo",
|
||||
public: false,
|
||||
return_annotation: None,
|
||||
return_type: (),
|
||||
end_position: 43,
|
||||
end_position: 39,
|
||||
on_test_failure: FailImmediately,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -243,7 +243,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
|||
"when" => Token::When,
|
||||
"validator" => Token::Validator,
|
||||
"via" => Token::Via,
|
||||
"benchmark" => Token::Benchmark,
|
||||
"bench" => Token::Benchmark,
|
||||
_ => {
|
||||
if s.chars().next().map_or(false, |c| c.is_uppercase()) {
|
||||
Token::UpName {
|
||||
|
|
|
@ -183,7 +183,7 @@ impl fmt::Display for Token {
|
|||
Token::Once => "once",
|
||||
Token::Validator => "validator",
|
||||
Token::Via => "via",
|
||||
Token::Benchmark => "benchmark",
|
||||
Token::Benchmark => "bench",
|
||||
};
|
||||
write!(f, "{s}")
|
||||
}
|
||||
|
|
|
@ -332,14 +332,12 @@ impl PropertyTest {
|
|||
) -> Result<Option<Counterexample<'a>>, FuzzerError> {
|
||||
let mut prng = initial_prng;
|
||||
let mut counterexample = None;
|
||||
let mut iteration = 0;
|
||||
|
||||
while *remaining > 0 && counterexample.is_none() {
|
||||
let (next_prng, cex) = self.run_once(prng, labels, plutus_version, iteration)?;
|
||||
let (next_prng, cex) = self.run_once(prng, labels, plutus_version)?;
|
||||
prng = next_prng;
|
||||
counterexample = cex;
|
||||
*remaining -= 1;
|
||||
iteration += 1;
|
||||
}
|
||||
|
||||
Ok(counterexample)
|
||||
|
@ -350,12 +348,11 @@ impl PropertyTest {
|
|||
prng: Prng,
|
||||
labels: &mut BTreeMap<String, usize>,
|
||||
plutus_version: &'a PlutusVersion,
|
||||
iteration: usize,
|
||||
) -> Result<(Prng, Option<Counterexample<'a>>), FuzzerError> {
|
||||
use OnTestFailure::*;
|
||||
|
||||
let (next_prng, value) = prng
|
||||
.sample(&self.fuzzer.program, iteration)?
|
||||
.sample(&self.fuzzer.program)?
|
||||
.expect("A seeded PRNG returned 'None' which indicates a fuzzer is ill-formed and implemented wrongly; please contact library's authors.");
|
||||
|
||||
let mut result = self.eval(&value, plutus_version);
|
||||
|
@ -386,8 +383,8 @@ impl PropertyTest {
|
|||
value,
|
||||
choices: next_prng.choices(),
|
||||
cache: Cache::new(move |choices| {
|
||||
match Prng::from_choices(choices, iteration)
|
||||
.sample(&self.fuzzer.program, iteration)
|
||||
match Prng::from_choices(choices)
|
||||
.sample(&self.fuzzer.program)
|
||||
{
|
||||
Err(..) => Status::Invalid,
|
||||
Ok(None) => Status::Invalid,
|
||||
|
@ -454,7 +451,7 @@ pub struct Benchmark {
|
|||
pub name: String,
|
||||
pub on_test_failure: OnTestFailure,
|
||||
pub program: Program<Name>,
|
||||
pub fuzzer: Fuzzer<Name>,
|
||||
pub sampler: Fuzzer<Name>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Benchmark {}
|
||||
|
@ -467,11 +464,12 @@ impl Benchmark {
|
|||
plutus_version: &PlutusVersion,
|
||||
) -> Vec<BenchmarkResult> {
|
||||
let mut results = Vec::with_capacity(n);
|
||||
let mut remaining = n;
|
||||
let mut iteration = 0;
|
||||
let mut prng = Prng::from_seed(seed);
|
||||
|
||||
while remaining > 0 {
|
||||
match prng.sample(&self.fuzzer.program, n - remaining) {
|
||||
while n > iteration {
|
||||
let fuzzer = self.sampler.program.apply_data(Data::integer(num_bigint::BigInt::from(iteration as i64)));
|
||||
match prng.sample(&fuzzer) {
|
||||
Ok(Some((new_prng, value))) => {
|
||||
prng = new_prng;
|
||||
let mut eval_result = self.eval(&value, plutus_version);
|
||||
|
@ -496,7 +494,7 @@ impl Benchmark {
|
|||
break;
|
||||
}
|
||||
}
|
||||
remaining -= 1;
|
||||
iteration += 1;
|
||||
}
|
||||
|
||||
results
|
||||
|
@ -534,12 +532,10 @@ pub enum Prng {
|
|||
Seeded {
|
||||
choices: Vec<u8>,
|
||||
uplc: PlutusData,
|
||||
iteration: usize,
|
||||
},
|
||||
Replayed {
|
||||
choices: Vec<u8>,
|
||||
uplc: PlutusData,
|
||||
iteration: usize,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -588,12 +584,11 @@ impl Prng {
|
|||
Data::bytestring(vec![]), // Random choices
|
||||
],
|
||||
),
|
||||
iteration: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a Pseudo-random number generator from a pre-defined list of choices.
|
||||
pub fn from_choices(choices: &[u8], iteration: usize) -> Prng {
|
||||
pub fn from_choices(choices: &[u8]) -> Prng {
|
||||
Prng::Replayed {
|
||||
uplc: Data::constr(
|
||||
Prng::REPLAYED,
|
||||
|
@ -603,7 +598,6 @@ impl Prng {
|
|||
],
|
||||
),
|
||||
choices: choices.to_vec(),
|
||||
iteration,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -611,11 +605,10 @@ impl Prng {
|
|||
pub fn sample(
|
||||
&self,
|
||||
fuzzer: &Program<Name>,
|
||||
iteration: usize,
|
||||
// iteration: usize,
|
||||
) -> Result<Option<(Prng, PlutusData)>, FuzzerError> {
|
||||
let program = Program::<NamedDeBruijn>::try_from(
|
||||
fuzzer
|
||||
.apply_data(Data::integer(num_bigint::BigInt::from(iteration as i64)))
|
||||
.apply_data(self.uplc())).unwrap();
|
||||
let mut result = program.eval(ExBudget::max());
|
||||
result
|
||||
|
@ -624,7 +617,7 @@ impl Prng {
|
|||
traces: result.logs(),
|
||||
uplc_error,
|
||||
})
|
||||
.map(|term| Prng::from_result(term, iteration))
|
||||
.map(|term| Prng::from_result(term))
|
||||
}
|
||||
|
||||
/// Obtain a Prng back from a fuzzer execution. As a reminder, fuzzers have the following
|
||||
|
@ -639,10 +632,9 @@ impl Prng {
|
|||
/// aborted altogether with 'None'.
|
||||
pub fn from_result(
|
||||
result: Term<NamedDeBruijn>,
|
||||
iteration: usize,
|
||||
) -> Option<(Self, PlutusData)> {
|
||||
/// Interpret the given 'PlutusData' as one of two Prng constructors.
|
||||
fn as_prng(cst: &PlutusData, iteration: usize) -> Prng {
|
||||
fn as_prng(cst: &PlutusData) -> Prng {
|
||||
if let PlutusData::Constr(Constr { tag, fields, .. }) = cst {
|
||||
if *tag == 121 + Prng::SEEDED {
|
||||
if let [PlutusData::BoundedBytes(bytes), PlutusData::BoundedBytes(choices)] =
|
||||
|
@ -659,7 +651,6 @@ impl Prng {
|
|||
PlutusData::BoundedBytes(vec![].into()),
|
||||
],
|
||||
),
|
||||
iteration,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -670,7 +661,6 @@ impl Prng {
|
|||
return Prng::Replayed {
|
||||
choices: choices.to_vec(),
|
||||
uplc: cst.clone(),
|
||||
iteration,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -684,7 +674,7 @@ impl Prng {
|
|||
if *tag == 121 + Prng::SOME {
|
||||
if let [PlutusData::Array(elems)] = &fields[..] {
|
||||
if let [new_seed, value] = &elems[..] {
|
||||
return Some((as_prng(new_seed, iteration), value.clone()));
|
||||
return Some((as_prng(new_seed), value.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1773,17 +1773,27 @@ fn pipe_wrong_arity_fully_saturated_return_fn() {
|
|||
#[test]
|
||||
fn fuzzer_ok_basic() {
|
||||
let source_code = r#"
|
||||
fn int() -> Generator<Void, Int> { todo }
|
||||
fn int() -> Fuzzer<Int> { todo }
|
||||
test prop(n via int()) { True }
|
||||
"#;
|
||||
|
||||
assert!(check(parse(source_code)).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sampler_ok_basic() {
|
||||
let source_code = r#"
|
||||
fn int() -> Sampler<Int> { todo }
|
||||
bench prop(n via int()) { True }
|
||||
"#;
|
||||
|
||||
assert!(check(parse(source_code)).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fuzzer_ok_explicit() {
|
||||
let source_code = r#"
|
||||
fn int(void: Void, prng: PRNG) -> Option<(PRNG, Int)> { todo }
|
||||
fn int(prng: PRNG) -> Option<(PRNG, Int)> { todo }
|
||||
test prop(n via int) { Void }
|
||||
"#;
|
||||
|
||||
|
@ -1793,8 +1803,8 @@ fn fuzzer_ok_explicit() {
|
|||
#[test]
|
||||
fn fuzzer_ok_list() {
|
||||
let source_code = r#"
|
||||
fn int() -> Generator<Void, Int> { todo }
|
||||
fn list(a: Generator<Void, a>) -> Generator<Void, List<a>> { todo }
|
||||
fn int() -> Fuzzer<Int> { todo }
|
||||
fn list(a: Fuzzer<a>) -> Fuzzer<List<a>> { todo }
|
||||
|
||||
test prop(xs via list(int())) { True }
|
||||
"#;
|
||||
|
@ -1805,8 +1815,8 @@ fn fuzzer_ok_list() {
|
|||
#[test]
|
||||
fn fuzzer_err_unbound() {
|
||||
let source_code = r#"
|
||||
fn any() -> Generator<Void, a> { todo }
|
||||
fn list(a: Generator<Void, a>) -> Generator<Void, List<a>> { todo }
|
||||
fn any() -> Fuzzer<a> { todo }
|
||||
fn list(a: Fuzzer<a>) -> Fuzzer<List<a>> { todo }
|
||||
|
||||
test prop(xs via list(any())) { todo }
|
||||
"#;
|
||||
|
@ -1838,7 +1848,7 @@ fn fuzzer_err_unify_1() {
|
|||
#[test]
|
||||
fn fuzzer_err_unify_2() {
|
||||
let source_code = r#"
|
||||
fn any() -> Generator<Void, a> { todo }
|
||||
fn any() -> Fuzzer<a> { todo }
|
||||
test prop(xs via any) { todo }
|
||||
"#;
|
||||
|
||||
|
@ -1857,8 +1867,8 @@ fn fuzzer_err_unify_2() {
|
|||
#[test]
|
||||
fn fuzzer_err_unify_3() {
|
||||
let source_code = r#"
|
||||
fn list(a: Generator<Void, a>) -> Generator<Void, List<a>> { todo }
|
||||
fn int() -> Generator<Void, Int> { todo }
|
||||
fn list(a: Fuzzer<a>) -> Fuzzer<List<a>> { todo }
|
||||
fn int() -> Fuzzer<Int> { todo }
|
||||
|
||||
test prop(xs: Int via list(int())) { todo }
|
||||
"#;
|
||||
|
|
|
@ -838,8 +838,7 @@ fn infer_fuzzer(
|
|||
) -> Result<(Annotation, Rc<Type>), Error> {
|
||||
let could_not_unify = || Error::CouldNotUnify {
|
||||
location: *location,
|
||||
expected: Type::generator(
|
||||
Type::void(),
|
||||
expected: Type::fuzzer(
|
||||
expected_inner_type
|
||||
.clone()
|
||||
.unwrap_or_else(|| Type::generic_var(0)),
|
||||
|
@ -863,7 +862,7 @@ fn infer_fuzzer(
|
|||
contains_opaque: _,
|
||||
alias: _,
|
||||
} if module.is_empty() && name == "Option" && args.len() == 1 => {
|
||||
match args.first().expect("args.len() == 2 && args[0].is_void()").borrow() {
|
||||
match args.first().expect("args.len() == 1").borrow() {
|
||||
Type::Tuple { elems, .. } if elems.len() == 2 => {
|
||||
let wrapped = elems.get(1).expect("Tuple has two elements");
|
||||
|
||||
|
@ -878,7 +877,7 @@ fn infer_fuzzer(
|
|||
// `unify` now that we have figured out the type carried by the fuzzer.
|
||||
environment.unify(
|
||||
tipo.clone(),
|
||||
Type::generator(Type::void(), wrapped.clone()),
|
||||
Type::fuzzer(wrapped.clone()),
|
||||
*location,
|
||||
false,
|
||||
)?;
|
||||
|
@ -916,8 +915,7 @@ fn infer_sampler(
|
|||
) -> Result<(Annotation, Rc<Type>), Error> {
|
||||
let could_not_unify = || Error::CouldNotUnify {
|
||||
location: *location,
|
||||
expected: Type::generator(
|
||||
Type::int(),
|
||||
expected: Type::sampler(
|
||||
expected_inner_type
|
||||
.clone()
|
||||
.unwrap_or_else(|| Type::generic_var(0)),
|
||||
|
@ -930,34 +928,12 @@ fn infer_sampler(
|
|||
match tipo.borrow() {
|
||||
Type::Fn {
|
||||
ret,
|
||||
args: _,
|
||||
args,
|
||||
alias: _,
|
||||
} => match ret.borrow() {
|
||||
Type::App {
|
||||
module,
|
||||
name,
|
||||
args,
|
||||
public: _,
|
||||
contains_opaque: _,
|
||||
alias: _,
|
||||
} if module.is_empty() && name == "Option" && args.len() == 1 => {
|
||||
match args.first().expect("args.len() == 2 && args[0].is_int()").borrow() {
|
||||
Type::Tuple { elems, .. } if elems.len() == 2 => {
|
||||
let wrapped = elems.get(1).expect("Tuple has two elements");
|
||||
|
||||
environment.unify(
|
||||
tipo.clone(),
|
||||
Type::generator(Type::int(), wrapped.clone()),
|
||||
*location,
|
||||
false,
|
||||
)?;
|
||||
|
||||
Ok((annotate_fuzzer(wrapped, location)?, wrapped.clone()))
|
||||
}
|
||||
_ => Err(could_not_unify()),
|
||||
}
|
||||
}
|
||||
_ => Err(could_not_unify()),
|
||||
} => if args.len() == 1 && args[0].is_int() {
|
||||
infer_fuzzer(environment, expected_inner_type, ret, &Span::empty())
|
||||
} else {
|
||||
Err(could_not_unify())
|
||||
},
|
||||
|
||||
Type::Var { tipo, alias } => match &*tipo.deref().borrow() {
|
||||
|
|
|
@ -193,7 +193,7 @@ impl Error {
|
|||
test.input_path.to_path_buf(),
|
||||
test.program.to_pretty(),
|
||||
),
|
||||
TestResult::Benchmark(_) => ("benchmark".to_string(), PathBuf::new(), String::new()), // todo
|
||||
TestResult::Benchmark(_) => ("bench".to_string(), PathBuf::new(), String::new()), // todo
|
||||
};
|
||||
|
||||
Error::TestFailure {
|
||||
|
|
|
@ -112,10 +112,8 @@ mod test {
|
|||
|
||||
const max_int: Int = 255
|
||||
|
||||
pub type Fuzzer<a> = Generator<Void, a>
|
||||
|
||||
pub fn int() -> Fuzzer<Int> {
|
||||
fn(v: Void, prng: PRNG) -> Option<(PRNG, Int)> {
|
||||
fn(prng: PRNG) -> Option<(PRNG, Int)> {
|
||||
when prng is {
|
||||
Seeded { seed, choices } -> {
|
||||
let choice =
|
||||
|
@ -163,21 +161,21 @@ mod test {
|
|||
}
|
||||
|
||||
pub fn constant(a: a) -> Fuzzer<a> {
|
||||
fn(v, s0) { Some((s0, a)) }
|
||||
fn(s0) { Some((s0, a)) }
|
||||
}
|
||||
|
||||
pub fn and_then(fuzz_a: Fuzzer<a>, f: fn(a) -> Fuzzer<b>) -> Fuzzer<b> {
|
||||
fn(v, s0) {
|
||||
when fuzz_a(v, s0) is {
|
||||
Some((s1, a)) -> f(a)(v, s1)
|
||||
fn(s0) {
|
||||
when fuzz_a(s0) is {
|
||||
Some((s1, a)) -> f(a)(s1)
|
||||
None -> None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map(fuzz_a: Fuzzer<a>, f: fn(a) -> b) -> Fuzzer<b> {
|
||||
fn(v, s0) {
|
||||
when fuzz_a(v, s0) is {
|
||||
fn(s0) {
|
||||
when fuzz_a(s0) is {
|
||||
Some((s1, a)) -> Some((s1, f(a)))
|
||||
None -> None
|
||||
}
|
||||
|
@ -185,10 +183,10 @@ mod test {
|
|||
}
|
||||
|
||||
pub fn map2(fuzz_a: Fuzzer<a>, fuzz_b: Fuzzer<b>, f: fn(a, b) -> c) -> Fuzzer<c> {
|
||||
fn(v, s0) {
|
||||
when fuzz_a(v, s0) is {
|
||||
fn(s0) {
|
||||
when fuzz_a(s0) is {
|
||||
Some((s1, a)) ->
|
||||
when fuzz_b(Void, s1) is {
|
||||
when fuzz_b(s1) is {
|
||||
Some((s2, b)) -> Some((s2, f(a, b)))
|
||||
None -> None
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue