checkpoint in reconstruction

This commit is contained in:
2023-12-26 21:08:01 -08:00
parent 2c2268925a
commit e5db6640f2
21 changed files with 759 additions and 153 deletions

View File

@@ -1,21 +1,433 @@
use crate::ir::{Program, TopLevel, Expression, ValueOrRef, Value, Type};
use proptest::{
prelude::Arbitrary,
strategy::{BoxedStrategy, Strategy},
};
use crate::eval::PrimitiveType;
use crate::ir::{Expression, Primitive, Program, TopLevel, Type, Value, ValueOrRef, Variable};
use crate::syntax::Location;
use crate::util::scoped_map::ScopedMap;
use proptest::strategy::{NewTree, Strategy, ValueTree};
use proptest::test_runner::{TestRng, TestRunner};
use rand::distributions::{Distribution, WeightedIndex};
use rand::seq::SliceRandom;
use rand::Rng;
use std::str::FromStr;
impl<Type: core::fmt::Debug> Arbitrary for Program<Type> {
type Parameters = crate::syntax::arbitrary::GenerationEnvironment;
type Strategy = BoxedStrategy<Self>;
lazy_static::lazy_static! {
static ref PROGRAM_LENGTH_DISTRIBUTION: WeightedIndex<usize> = WeightedIndex::new([
0, // % chance of 0
10, // % chance of 1
10, // % chance of 2
15, // % chance of 3
10, // % chance of 4
10, // % chance of 5
10, // % chance of 6
5, // % chance of 7
5, // % chance of 8
5, // % chance of 9
5, // % chance of 10
5, // % chance of 11
3, // % chance of 12
3, // % chance of 13
3, // % chance of 14
1, // % chance of 15
]).unwrap();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
unimplemented!()
//crate::syntax::Program::arbitrary_with(args)
// .prop_map(|x| {
// x.type_infer()
// .expect("arbitrary_with should generate type-correct programs")
// })
// .boxed()
static ref BLOCK_LENGTH_DISTRIBUTION: WeightedIndex<usize> = WeightedIndex::new([
1, // % chance of 0
10, // % chance of 1
20, // % chance of 2
15, // % chance of 3
10, // % chance of 4
8, // % chance of 5
8, // % chance of 6
5, // % chance of 7
5, // % chance of 8
4, // % chance of 9
3, // % chance of 10
3, // % chance of 11
3, // % chance of 12
2, // % chance of 13
2, // % chance of 14
1, // % chance of 15
]).unwrap();
static ref FUNCTION_ARGUMENTS_DISTRIBUTION: WeightedIndex<usize> = WeightedIndex::new([
5, // % chance of 0
20, // % chance of 1
20, // % chance of 2
20, // % chance of 3
15, // % chance of 4
10, // % chance of 5
5, // % chance of 6
2, // % chance of 7
1, // % chance of 8
1, // % chance of 9
1, // % chance of 10
]).unwrap();
static ref STATEMENT_TYPE_DISTRIBUTION: WeightedIndex<usize> = WeightedIndex::new(
STATEMENT_TYPE_FREQUENCIES.iter().map(|x| x.1)
).unwrap();
static ref EXPRESSION_TYPE_DISTRIBUTION: WeightedIndex<usize> = WeightedIndex::new(
EXPRESSION_TYPE_FREQUENCIES.iter().map(|x| x.1)
).unwrap();
static ref ARGUMENT_TYPE_DISTRIBUTION: WeightedIndex<usize> = WeightedIndex::new(
ARGUMENT_TYPE_FREQUENCIES.iter().map(|x| x.1)
).unwrap();
static ref VALUE_TYPE_DISTRIBUTION: WeightedIndex<usize> = WeightedIndex::new(
VALUE_TYPE_FREQUENCIES.iter().map(|x| x.1)
).unwrap();
}
static STATEMENT_TYPE_FREQUENCIES: &[(StatementType, usize)] = &[
(StatementType::Binding, 3),
(StatementType::Function, 1),
(StatementType::Expression, 2),
];
static EXPRESSION_TYPE_FREQUENCIES: &[(ExpressionType, usize)] = &[
(ExpressionType::Atomic, 50),
(ExpressionType::Cast, 5),
(ExpressionType::Primitive, 5),
(ExpressionType::Block, 10),
(ExpressionType::Print, 10),
(ExpressionType::Bind, 20),
];
static ARGUMENT_TYPE_FREQUENCIES: &[(Type, usize)] = &[
(Type::Primitive(PrimitiveType::U8), 1),
(Type::Primitive(PrimitiveType::U16), 1),
(Type::Primitive(PrimitiveType::U32), 1),
(Type::Primitive(PrimitiveType::U64), 1),
(Type::Primitive(PrimitiveType::I8), 1),
(Type::Primitive(PrimitiveType::I16), 1),
(Type::Primitive(PrimitiveType::I32), 1),
(Type::Primitive(PrimitiveType::I64), 1),
];
enum StatementType {
Binding,
Function,
Expression,
}
enum ExpressionType {
Atomic,
Cast,
Primitive,
Block,
Print,
Bind,
}
// this knowingly excludes void
static VALUE_TYPE_FREQUENCIES: &[(ValueType, usize)] = &[
(ValueType::I8, 1),
(ValueType::I16, 1),
(ValueType::I32, 1),
(ValueType::I64, 1),
(ValueType::U8, 1),
(ValueType::U16, 1),
(ValueType::U32, 1),
(ValueType::U64, 1),
];
#[derive(Copy, Clone)]
enum ValueType {
I8,
I16,
I32,
I64,
U8,
U16,
U32,
U64,
Void,
}
impl From<PrimitiveType> for ValueType {
fn from(value: PrimitiveType) -> Self {
match value {
PrimitiveType::U8 => ValueType::U8,
PrimitiveType::U16 => ValueType::U16,
PrimitiveType::U32 => ValueType::U32,
PrimitiveType::U64 => ValueType::U64,
PrimitiveType::I8 => ValueType::I8,
PrimitiveType::I16 => ValueType::I16,
PrimitiveType::I32 => ValueType::I32,
PrimitiveType::I64 => ValueType::I64,
PrimitiveType::Void => ValueType::Void,
}
}
}
#[derive(Debug, Default)]
pub struct ProgramGenerator {}
impl Strategy for ProgramGenerator {
type Tree = ProgramTree;
type Value = Program<Type>;
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
NewTree::<ProgramGenerator>::Ok(ProgramTree::new(runner.new_rng()))
}
}
pub struct ProgramTree {
_rng: TestRng,
current: Program<Type>,
}
impl ProgramTree {
fn new(mut rng: TestRng) -> Self {
let mut items = vec![];
let program_length = PROGRAM_LENGTH_DISTRIBUTION.sample(&mut rng);
let mut env = ScopedMap::new();
for _ in 0..program_length {
match STATEMENT_TYPE_FREQUENCIES[STATEMENT_TYPE_DISTRIBUTION.sample(&mut rng)].0 {
StatementType::Binding => {
let binding = generate_random_binding(&mut rng, &mut env);
items.push(TopLevel::Statement(binding));
}
StatementType::Expression => {
let expr = generate_random_expression(&mut rng, &mut env);
items.push(TopLevel::Statement(expr));
}
StatementType::Function => {
env.new_scope();
let name = generate_random_name(&mut rng);
let mut args = vec![];
let arg_count = FUNCTION_ARGUMENTS_DISTRIBUTION.sample(&mut rng);
for _ in 0..arg_count {
let name = generate_random_name(&mut rng);
let ty = generate_random_argument_type(&mut rng);
args.push((name, ty));
}
let body = generate_random_expression(&mut rng, &mut env);
let rettype = body.type_of();
env.release_scope();
items.push(TopLevel::Function(name, args, rettype, body))
}
}
}
let current = Program { items };
ProgramTree { _rng: rng, current }
}
}
impl ValueTree for ProgramTree {
type Value = Program<Type>;
fn current(&self) -> Self::Value {
self.current.clone()
}
fn simplify(&mut self) -> bool {
unimplemented!()
}
fn complicate(&mut self) -> bool {
unimplemented!()
}
}
#[derive(Debug)]
struct ExpressionGenerator {}
impl Strategy for ExpressionGenerator {
type Tree = ExpressionTree;
type Value = Expression<Type>;
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
NewTree::<ExpressionGenerator>::Ok(ExpressionTree::new(runner.new_rng()))
}
}
struct ExpressionTree {
_rng: TestRng,
current: Expression<Type>,
}
impl ValueTree for ExpressionTree {
type Value = Expression<Type>;
fn current(&self) -> Self::Value {
self.current.clone()
}
fn simplify(&mut self) -> bool {
unimplemented!()
}
fn complicate(&mut self) -> bool {
unimplemented!()
}
}
impl ExpressionTree {
fn new(mut rng: TestRng) -> Self {
let mut env = ScopedMap::new();
let current = generate_random_expression(&mut rng, &mut env);
ExpressionTree { _rng: rng, current }
}
}
fn generate_random_expression(
rng: &mut TestRng,
env: &mut ScopedMap<Variable, Type>,
) -> Expression<Type> {
match EXPRESSION_TYPE_FREQUENCIES[EXPRESSION_TYPE_DISTRIBUTION.sample(rng)].0 {
ExpressionType::Atomic => Expression::Atomic(generate_random_valueref(rng, env, None)),
ExpressionType::Bind => generate_random_binding(rng, env),
ExpressionType::Block => {
let num_stmts = BLOCK_LENGTH_DISTRIBUTION.sample(rng);
let mut stmts = Vec::new();
let mut last_type = Type::Primitive(PrimitiveType::Void);
env.new_scope();
for _ in 0..num_stmts {
let next = generate_random_expression(rng, env);
last_type = next.type_of();
stmts.push(next);
}
env.release_scope();
Expression::Block(Location::manufactured(), last_type, stmts)
}
ExpressionType::Cast => {
let inner = generate_random_valueref(rng, env, None);
match inner.type_of() {
// nevermind
Type::Function(_, _) => Expression::Atomic(inner),
Type::Primitive(primty) => {
let to_type = primty
.allowed_casts()
.choose(rng)
.expect("actually chose type");
Expression::Cast(Location::manufactured(), Type::Primitive(*to_type), inner)
}
}
}
ExpressionType::Primitive => {
let base_expr = generate_random_valueref(rng, env, None);
let out_type = base_expr.type_of();
match out_type {
Type::Function(_, _) => Expression::Atomic(base_expr),
Type::Primitive(primty) => match primty.valid_operators().choose(rng) {
None => Expression::Atomic(base_expr),
Some((operator, arg_count)) => {
let primop = Primitive::from_str(operator).expect("chose valid primitive");
let mut args = vec![base_expr];
while args.len() < *arg_count {
args.push(generate_random_valueref(rng, env, Some(primty)));
}
Expression::Primitive(Location::manufactured(), out_type, primop, args)
}
},
}
}
ExpressionType::Print => {
let possible_variables = env
.bindings()
.iter()
.filter_map(|(variable, ty)| {
if ty.is_printable() {
Some(variable.clone())
} else {
None
}
})
.collect::<Vec<Variable>>();
if possible_variables.is_empty() {
generate_random_binding(rng, env)
} else {
Expression::Print(
Location::manufactured(),
possible_variables.choose(rng).unwrap().clone(),
)
}
}
}
}
fn generate_random_binding(
rng: &mut TestRng,
env: &mut ScopedMap<Variable, Type>,
) -> Expression<Type> {
let name = generate_random_name(rng);
let expr = generate_random_expression(rng, env);
let ty = expr.type_of();
env.insert(name.clone(), ty.clone());
Expression::Bind(Location::manufactured(), name, ty, Box::new(expr))
}
fn generate_random_valueref(
rng: &mut TestRng,
env: &mut ScopedMap<Variable, Type>,
target_type: Option<PrimitiveType>,
) -> ValueOrRef<Type> {
let mut bindings = env.bindings();
bindings.retain(|_, value| {
target_type
.map(|x| value == &Type::Primitive(x))
.unwrap_or(true)
});
if rng.gen() || bindings.is_empty() {
let value_type = if let Some(target_type) = target_type {
ValueType::from(target_type)
} else {
VALUE_TYPE_FREQUENCIES[VALUE_TYPE_DISTRIBUTION.sample(rng)].0
};
// generate a constant
let val = match value_type {
ValueType::I8 => Value::I8(None, rng.gen()),
ValueType::I16 => Value::I16(None, rng.gen()),
ValueType::I32 => Value::I32(None, rng.gen()),
ValueType::I64 => Value::I64(None, rng.gen()),
ValueType::U8 => Value::U8(None, rng.gen()),
ValueType::U16 => Value::U16(None, rng.gen()),
ValueType::U32 => Value::U32(None, rng.gen()),
ValueType::U64 => Value::U64(None, rng.gen()),
ValueType::Void => Value::Void,
};
ValueOrRef::Value(Location::manufactured(), val.type_of(), val)
} else {
// generate a reference
let weighted_keys = bindings.keys().map(|x| (1, x)).collect::<Vec<_>>();
let distribution = WeightedIndex::new(weighted_keys.iter().map(|x| x.0)).unwrap();
let var = weighted_keys[distribution.sample(rng)].1.clone();
let ty = bindings
.remove(&var)
.expect("chose unbound variable somehow?");
ValueOrRef::Ref(Location::manufactured(), ty, var)
}
}
fn generate_random_name(rng: &mut TestRng) -> Variable {
let start = rng.gen_range('a'..='z');
crate::ir::gensym(&format!("{}", start))
}
fn generate_random_argument_type(rng: &mut TestRng) -> Type {
ARGUMENT_TYPE_FREQUENCIES[ARGUMENT_TYPE_DISTRIBUTION.sample(rng)]
.0
.clone()
}