Keep pushing forward on type inference.

This commit is contained in:
2023-07-21 21:34:35 -07:00
parent 1ad3d6c517
commit 9fb6bf3b86
11 changed files with 577 additions and 275 deletions

View File

@@ -10,7 +10,7 @@ use std::collections::HashMap;
const VALID_VARIABLE_NAMES: &str = r"[a-z][a-zA-Z0-9_]*";
const OPERATORS: &[(&str, usize)] = &[("+", 2), ("-", 1), ("-", 2), ("*", 2), ("/", 2)];
#[derive(Debug)]
#[derive(Clone, Debug)]
struct Name(String);
impl Arbitrary for Name {
@@ -22,153 +22,133 @@ impl Arbitrary for Name {
}
}
#[derive(Debug)]
struct ProgramStatementInfo {
should_be_binding: bool,
name: Name,
binding_type: ConstantType,
}
impl Arbitrary for ProgramStatementInfo {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(
Union::new(vec![Just(true), Just(true), Just(false)]),
Name::arbitrary(),
ConstantType::arbitrary(),
)
.prop_map(
|(should_be_binding, name, binding_type)| ProgramStatementInfo {
should_be_binding,
name,
binding_type,
},
)
.boxed()
}
}
impl Arbitrary for Program {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
let optionals = Vec::<(Name, ConstantType, u8)>::arbitrary();
optionals
.prop_flat_map(|mut possible_names| {
proptest::collection::vec(ProgramStatementInfo::arbitrary(), 1..100)
.prop_flat_map(|mut items| {
let mut statements = Vec::new();
let mut defined_variables: HashMap<String, ConstantType> = HashMap::new();
let mut defined_variables = HashMap::new();
for (possible_name, possible_type, dice_roll) in possible_names.drain(..) {
if !defined_variables.is_empty() && dice_roll < 100 {
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.0.clone(), psi.binding_type);
statements.push(
Union::new(defined_variables.keys().map(|name| {
Just(Statement::Print(Location::manufactured(), name.to_string()))
}))
expr.prop_map(move |expr| {
Statement::Binding(
Location::manufactured(),
psi.name.0.clone(),
expr,
)
})
.boxed(),
);
} else {
let closures_name = possible_name.0.clone();
let retval = Expression::arbitrary_with((
Some(defined_variables.clone()),
Some(possible_type),
))
.prop_map(move |exp| {
Statement::Binding(Location::manufactured(), closures_name.clone(), exp)
})
.boxed();
defined_variables.insert(possible_name.0, possible_type);
statements.push(retval);
let printers = defined_variables
.keys()
.map(|n| Just(Statement::Print(Location::manufactured(), n.clone())));
statements.push(Union::new(printers).boxed());
}
}
statements
.prop_map(|statements| Program { statements })
.boxed()
})
.prop_map(|statements| Program { statements })
.boxed()
}
}
impl Arbitrary for Statement {
type Parameters = Option<HashMap<String, ConstantType>>;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
let duplicated_args = args.clone();
let defined_variables = args.unwrap_or_default();
let binding_strategy = (
VALID_VARIABLE_NAMES,
Expression::arbitrary_with((duplicated_args, None)),
)
.prop_map(|(name, exp)| Statement::Binding(Location::manufactured(), name, exp))
.boxed();
if defined_variables.is_empty() {
binding_strategy
} else {
let print_strategy = Union::new(
defined_variables
.keys()
.map(|x| Just(Statement::Print(Location::manufactured(), x.to_string()))),
)
.boxed();
Union::new([binding_strategy, print_strategy]).boxed()
}
}
#[derive(Default)]
pub struct ExpressionGeneratorSettings {
bound_variables: HashMap<String, ConstantType>,
output_type: Option<ConstantType>,
}
impl Arbitrary for Expression {
type Parameters = (Option<HashMap<String, ConstantType>>, Option<ConstantType>);
type Parameters = ExpressionGeneratorSettings;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with((env, target_type): Self::Parameters) -> Self::Strategy {
let defined_variables = env.unwrap_or_default();
let mut acceptable_variables = defined_variables
.iter()
.filter(|(_, ctype)| Some(**ctype) == target_type)
.map(|(x, _)| x)
.peekable();
let value_strategy = Value::arbitrary_with(target_type)
.prop_map(move |x| Expression::Value(Location::manufactured(), x))
fn arbitrary_with(params: 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)
.prop_map(|x| Expression::Value(Location::manufactured(), x))
.boxed();
let leaf_strategy = if acceptable_variables.peek().is_none() {
// Reference(Location, String), These are slightly trickier, because we can end up in a situation
// where either no variables are defined, or where none of the defined variables have a type we
// 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
.iter()
.filter(|(_, v)| {
params
.output_type
.as_ref()
.map(|ot| ot == *v)
.unwrap_or(true)
})
.map(|(n, _)| n)
.collect::<Vec<_>>();
let leaf_strategy = if bound_variables_of_type.is_empty() {
value_strategy
} else {
let reference_strategy = Union::new(acceptable_variables.map(|x| {
Just(Expression::Reference(
Location::manufactured(),
x.to_owned(),
))
}))
.boxed();
Union::new([value_strategy, reference_strategy]).boxed()
let mut strats = bound_variables_of_type
.drain(..)
.map(|x| Just(Expression::Reference(Location::manufactured(), x.clone())).boxed())
.collect::<Vec<_>>();
strats.push(value_strategy);
Union::new(strats).boxed()
};
let cast_strategy = if let Some(bigger_type) = target_type {
let mut smaller_types = bigger_type.safe_casts_to();
if smaller_types.is_empty() {
leaf_strategy
} else {
let duplicated_env = defined_variables.clone();
let cast_exp = |t, e| Expression::Cast(Location::manufactured(), t, Box::new(e));
let smaller_strats: Vec<BoxedStrategy<Expression>> = smaller_types
.drain(..)
.map(|t| {
Expression::arbitrary_with((Some(duplicated_env.clone()), Some(t)))
.prop_map(move |e| cast_exp(t.name(), e))
.boxed()
})
.collect();
Union::new(smaller_strats).boxed()
}
} else {
leaf_strategy
};
cast_strategy
.prop_recursive(3, 64, 2, move |inner| {
(select(OPERATORS), proptest::collection::vec(inner, 2)).prop_map(
move |((operator, arg_count), mut exprs)| {
if arg_count == 1 && operator == "-" {
if target_type.map(|x| x.is_signed()).unwrap_or(false) {
Expression::Primitive(
Location::manufactured(),
operator.to_string(),
exprs,
)
} else {
exprs.pop().unwrap()
}
} else {
exprs.truncate(arg_count);
Expression::Primitive(
Location::manufactured(),
operator.to_string(),
exprs,
)
// 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(
|((oper, count), left, right)| {
let mut args = vec![left, right];
while args.len() > count {
args.pop();
}
Expression::Primitive(Location::manufactured(), oper.to_string(), args)
},
)
})
@@ -181,7 +161,7 @@ impl Arbitrary for Value {
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(target_type: Self::Parameters) -> Self::Strategy {
let base_strategy = Union::new([
let printed_base_strategy = Union::new([
Just(None::<u8>),
Just(Some(2)),
Just(Some(8)),
@@ -189,14 +169,13 @@ impl Arbitrary for Value {
Just(Some(16)),
]);
let type_strategy = if target_type.is_some() {
Just(target_type).boxed()
} else {
proptest::option::of(ConstantType::arbitrary()).boxed()
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();
(base_strategy, type_strategy, value_strategy)
(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),