Back to things working!

This commit is contained in:
2023-08-12 18:03:01 -07:00
parent 64405d5a06
commit f8b73fcdfd
7 changed files with 160 additions and 123 deletions

View File

@@ -6,9 +6,89 @@ use proptest::{
strategy::{Just, Union},
};
use std::collections::HashMap;
use std::ops::Range;
const VALID_VARIABLE_NAMES: &str = r"[a-z][a-zA-Z0-9_]*";
const OPERATORS: &[(&str, usize)] = &[("+", 2), ("-", 1), ("-", 2), ("*", 2), ("/", 2)];
impl ConstantType {
fn get_operators(&self) -> &'static [(&'static str, usize)] {
match self {
ConstantType::I8| ConstantType::I16 | ConstantType::I32 | ConstantType::I64 =>
&[("+", 2), ("-", 1), ("-", 2), ("*", 2), ("/", 2)],
ConstantType::U8| ConstantType::U16 | ConstantType::U32 | ConstantType::U64 =>
&[("+", 2), ("-", 2), ("*", 2), ("/", 2)],
}
}
}
#[derive(Clone)]
pub struct GenerationEnvironment {
allow_inference: bool,
block_length: Range<usize>,
bindings: HashMap<Name, ConstantType>,
return_type: ConstantType,
}
impl Default for GenerationEnvironment {
fn default() -> Self {
GenerationEnvironment {
allow_inference: true,
block_length: 2..10,
bindings: HashMap::new(),
return_type: ConstantType::U64,
}
}
}
impl GenerationEnvironment {
pub fn new(allow_inference: bool) -> Self {
GenerationEnvironment {
allow_inference,
..Default::default()
}
}
}
impl Arbitrary for Program {
type Parameters = GenerationEnvironment;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(genenv: Self::Parameters) -> Self::Strategy {
proptest::collection::vec(ProgramStatementInfo::arbitrary(), genenv.block_length.clone())
.prop_flat_map(move |mut items| {
let mut statements = Vec::new();
let mut genenv = genenv.clone();
for psi in items.drain(..) {
if genenv.bindings.is_empty() || psi.should_be_binding {
genenv.return_type = psi.binding_type;
let expr = Expression::arbitrary_with(genenv.clone());
genenv.bindings.insert(psi.name.clone(), psi.binding_type);
statements.push(
expr.prop_map(move |expr| {
Statement::Binding(Location::manufactured(), psi.name.clone(), expr)
})
.boxed(),
);
} else {
let printers = genenv.bindings.keys().map(|n| {
Just(Statement::Print(
Location::manufactured(),
Name::manufactured(n),
))
});
statements.push(Union::new(printers).boxed());
}
}
statements
.prop_map(|statements| Program { statements })
.boxed()
})
.boxed()
}
}
impl Arbitrary for Name {
type Parameters = ();
@@ -47,63 +127,14 @@ impl Arbitrary for ProgramStatementInfo {
}
}
impl Arbitrary for Program {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
proptest::collection::vec(ProgramStatementInfo::arbitrary(), 1..100)
.prop_flat_map(|mut items| {
let mut statements = Vec::new();
let mut defined_variables = HashMap::new();
for psi in items.drain(..) {
if defined_variables.is_empty() || psi.should_be_binding {
let expr = Expression::arbitrary_with(ExpressionGeneratorSettings {
bound_variables: defined_variables.clone(),
output_type: Some(psi.binding_type),
});
defined_variables.insert(psi.name.name.clone(), psi.binding_type);
statements.push(
expr.prop_map(move |expr| {
Statement::Binding(Location::manufactured(), psi.name.clone(), expr)
})
.boxed(),
);
} else {
let printers = defined_variables.keys().map(|n| {
Just(Statement::Print(
Location::manufactured(),
Name::manufactured(n),
))
});
statements.push(Union::new(printers).boxed());
}
}
statements
.prop_map(|statements| Program { statements })
.boxed()
})
.boxed()
}
}
#[derive(Default)]
pub struct ExpressionGeneratorSettings {
bound_variables: HashMap<String, ConstantType>,
output_type: Option<ConstantType>,
}
impl Arbitrary for Expression {
type Parameters = ExpressionGeneratorSettings;
type Parameters = GenerationEnvironment;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
fn arbitrary_with(genenv: Self::Parameters) -> Self::Strategy {
// Value(Location, Value). These are the easiest variations to create, because we can always
// create one.
let value_strategy = Value::arbitrary_with(params.output_type)
let value_strategy = Value::arbitrary_with(genenv.clone())
.prop_map(|x| Expression::Value(Location::manufactured(), x))
.boxed();
@@ -112,16 +143,10 @@ impl Arbitrary for Expression {
// can work with. So what we're going to do is combine this one with the previous one as a "leaf
// strategy" -- our non-recursive items -- if we can, or just set that to be the value strategy
// if we can't actually create an references.
let mut bound_variables_of_type = params
.bound_variables
let mut bound_variables_of_type = genenv
.bindings
.iter()
.filter(|(_, v)| {
params
.output_type
.as_ref()
.map(|ot| ot == *v)
.unwrap_or(true)
})
.filter(|(_, v)| genenv.return_type == **v)
.map(|(n, _)| n)
.collect::<Vec<_>>();
let leaf_strategy = if bound_variables_of_type.is_empty() {
@@ -129,7 +154,13 @@ impl Arbitrary for Expression {
} else {
let mut strats = bound_variables_of_type
.drain(..)
.map(|x| Just(Expression::Reference(Location::manufactured(), x.clone())).boxed())
.map(|x| {
Just(Expression::Reference(
Location::manufactured(),
x.name.clone(),
))
.boxed()
})
.collect::<Vec<_>>();
strats.push(value_strategy);
Union::new(strats).boxed()
@@ -138,7 +169,7 @@ impl Arbitrary for Expression {
// now we generate our recursive types, given our leaf strategy
leaf_strategy
.prop_recursive(3, 10, 2, move |strat| {
(select(OPERATORS), strat.clone(), strat).prop_map(
(select(genenv.return_type.get_operators()), strat.clone(), strat).prop_map(
|((oper, count), left, right)| {
let mut args = vec![left, right];
while args.len() > count {
@@ -153,10 +184,10 @@ impl Arbitrary for Expression {
}
impl Arbitrary for Value {
type Parameters = Option<ConstantType>;
type Parameters = GenerationEnvironment;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(target_type: Self::Parameters) -> Self::Strategy {
fn arbitrary_with(genenv: Self::Parameters) -> Self::Strategy {
let printed_base_strategy = Union::new([
Just(None::<u8>),
Just(Some(2)),
@@ -164,25 +195,24 @@ impl Arbitrary for Value {
Just(Some(10)),
Just(Some(16)),
]);
let type_strategy = match target_type {
None => proptest::option::of(ConstantType::arbitrary()).boxed(),
Some(target) => proptest::option::of(Just(target)).boxed(),
};
let value_strategy = u64::arbitrary();
(printed_base_strategy, type_strategy, value_strategy)
.prop_map(move |(base, ty, value)| {
let converted_value = match ty {
Some(ConstantType::I8) => value % (i8::MAX as u64),
Some(ConstantType::U8) => value % (u8::MAX as u64),
Some(ConstantType::I16) => value % (i16::MAX as u64),
Some(ConstantType::U16) => value % (u16::MAX as u64),
Some(ConstantType::I32) => value % (i32::MAX as u64),
Some(ConstantType::U32) => value % (u32::MAX as u64),
Some(ConstantType::I64) => value % (i64::MAX as u64),
Some(ConstantType::U64) => value,
None => value,
(printed_base_strategy, bool::arbitrary(), value_strategy)
.prop_map(move |(base, declare_type, value)| {
let converted_value = match genenv.return_type {
ConstantType::I8 => value % (i8::MAX as u64),
ConstantType::U8 => value % (u8::MAX as u64),
ConstantType::I16 => value % (i16::MAX as u64),
ConstantType::U16 => value % (u16::MAX as u64),
ConstantType::I32 => value % (i32::MAX as u64),
ConstantType::U32 => value % (u32::MAX as u64),
ConstantType::I64 => value % (i64::MAX as u64),
ConstantType::U64 => value,
};
let ty = if declare_type || !genenv.allow_inference {
Some(genenv.return_type)
} else {
None
};
Value::Number(base, ty, converted_value)
})