λ Support functions! #5

Open
acw wants to merge 59 commits from awick/functions into develop
7 changed files with 11409 additions and 73 deletions
Showing only changes of commit b72b2eddb4 - Show all commits

File diff suppressed because it is too large Load Diff

View File

@@ -180,9 +180,9 @@ pub struct ProgramTree {
}
impl ProgramTree {
fn new(mut rng: TestRng) -> Self {
fn new(_rng: TestRng) -> Self {
// let mut items = vec![];
let program_length = PROGRAM_LENGTH_DISTRIBUTION.sample(&mut rng);
//let program_length = PROGRAM_LENGTH_DISTRIBUTION.sample(&mut rng);
// let mut env = ScopedMap::new();
// for _ in 0..program_length {
@@ -216,10 +216,10 @@ impl ProgramTree {
let current = Program {
functions: HashMap::new(),
type_definitions: HashMap::new(),
body: unimplemented!(),
body: Expression::Block(Location::manufactured(), Type::void(), vec![]),
};
ProgramTree { _rng: rng, current }
ProgramTree { _rng, current }
}
}
@@ -459,8 +459,8 @@ fn generate_random_name(rng: &mut TestRng) -> Name {
Name::gensym(start)
}
fn generate_random_argument_type(rng: &mut TestRng) -> Type {
ARGUMENT_TYPE_FREQUENCIES[ARGUMENT_TYPE_DISTRIBUTION.sample(rng)]
.0
.clone()
}
//fn generate_random_argument_type(rng: &mut TestRng) -> Type {
// ARGUMENT_TYPE_FREQUENCIES[ARGUMENT_TYPE_DISTRIBUTION.sample(rng)]
// .0
// .clone()
//}

View File

@@ -322,17 +322,38 @@ fn order_of_operations() {
}
proptest::proptest! {
#[test]
fn random_syntaxes_validate(program in self::arbitrary::ProgramGenerator::default()) {
let result = Program::validate(program);
proptest::prop_assert!(result.is_ok());
}
#[test]
fn syntax_asts_roundtrip(program in self::arbitrary::ProgramGenerator::default()) {
use crate::util::pretty::Allocator;
#[test]
fn generated_run_or_overflow(program in self::arbitrary::ProgramGenerator::default()) {
use crate::eval::{EvalError, PrimOpError};
let validated = Program::validate(program);
let actual_program = validated.into_result().expect("got a valid result");
proptest::prop_assert!(matches!(actual_program.eval(), Ok(_) | Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))));
}
let allocator = Allocator::new();
let mut outbytes = Vec::new();
for top_level in program.iter() {
let docbuilder = top_level.pretty(&allocator);
docbuilder.render(78, &mut outbytes).expect("can write program text");
}
let outstr = std::str::from_utf8(&outbytes).expect("generated utf8");
println!("---------------- GENERATED TEXT -------------------");
println!("{}", outstr);
println!("---------------------------------------------------");
let syntax = crate::syntax::parse_string(0, &outstr).expect("generated text parses");
assert_eq!(program, syntax);
}
#[test]
fn random_syntaxes_validate(program in self::arbitrary::ProgramGenerator::default()) {
let result = Program::validate(program);
proptest::prop_assert!(result.is_ok());
}
#[test]
fn generated_run_or_overflow(program in self::arbitrary::ProgramGenerator::default()) {
use crate::eval::{EvalError, PrimOpError};
let validated = Program::validate(program);
let actual_program = validated.into_result().expect("got a valid result");
proptest::prop_assert!(matches!(actual_program.eval(), Ok(_) | Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))));
}
}

View File

@@ -18,21 +18,22 @@ const MAX_COMPLEX_DEPTH: usize = 4;
lazy_static::lazy_static! {
static ref BASE_PROGRAM_LENGTH: WeightedIndex<usize> = WeightedIndex::new([
0, // weight for 0
1, // weight for 1
1, // weight for 2
1, // weight for 3
3, // triple weight for 4
3, // triple weight for 5
3, // triple weight for 6
3, // triple weight for 7
3, // triple weight for 8
3, // triple weight for 9
3, // triple weight for 10
3, // double weight for 11
3, // double weight for 12
3, // double weight for 13
1, // weight for 14
1, // weight for 15
9,
// 1, // weight for 1
// 1, // weight for 2
// 1, // weight for 3
// 3, // triple weight for 4
// 3, // triple weight for 5
// 3, // triple weight for 6
// 3, // triple weight for 7
// 3, // triple weight for 8
// 3, // triple weight for 9
// 3, // triple weight for 10
// 3, // double weight for 11
// 3, // double weight for 12
// 3, // double weight for 13
// 1, // weight for 14
// 1, // weight for 15
]).unwrap();
static ref KEEP_FIELD_TYPE_ANNOTATION: WeightedMap<bool> = WeightedMap::new(&[
@@ -151,7 +152,7 @@ impl Strategy for ProgramGenerator {
pub struct ProgramTree {
_rng: TestRng,
current: VecDeque<TopLevel>,
current: Vec<TopLevel>,
}
pub enum Requirement {
@@ -167,14 +168,15 @@ impl ProgramTree {
let mut rng = runner.new_rng();
let base_program_length = BASE_PROGRAM_LENGTH.sample(&mut rng);
let mut env = ScopedMap::new();
let mut current = VecDeque::new();
let mut current = Vec::new();
let mut established_structs = HashMap::new();
while current.len() < base_program_length {
let (expression, mut requirements) =
generate_expr(0, &mut rng, &mut established_structs, &mut env, None);
let mut new_entries = VecDeque::new();
current.push_front(TopLevel::Expression(expression));
new_entries.push_back(TopLevel::Expression(expression));
while let Some(requirement) = requirements.pop() {
match requirement {
Requirement::Function(name, args, result) => {
@@ -187,7 +189,7 @@ impl ProgramTree {
args,
result,
);
current.push_front(TopLevel::Expression(expression));
new_entries.push_front(TopLevel::Expression(expression));
requirements.extend(newreqs.into_iter());
}
@@ -202,7 +204,7 @@ impl ProgramTree {
}
})
.collect();
current.push_front(TopLevel::Structure(
new_entries.push_front(TopLevel::Structure(
Location::manufactured(),
name,
fields,
@@ -219,17 +221,16 @@ impl ProgramTree {
);
let binding =
Expression::Binding(Location::manufactured(), name, Box::new(newexpr));
current.push_front(TopLevel::Expression(binding));
new_entries.push_front(TopLevel::Expression(binding));
requirements.extend(newreqs.into_iter());
}
}
}
current.extend(new_entries.into_iter());
}
ProgramTree {
_rng: rng,
current: current.into_iter().collect(),
}
ProgramTree { _rng: rng, current }
}
}
@@ -287,7 +288,7 @@ fn generate_expr(
let mut valid_types =
find_structs_with_field_type(established_struct_types, &target_type);
if valid_types.is_empty() {
let name = generate_name(rng);
let struct_name = generate_type_name(rng);
let mut fields = generate_structure_fields(rng, established_struct_types);
valid_types = fields
.iter()
@@ -298,10 +299,11 @@ fn generate_expr(
if valid_types.is_empty() {
let field_name = generate_name(rng);
fields.insert(field_name.clone(), target_type.clone());
valid_types = vec![(Type::Named(name.clone()), field_name)];
valid_types = vec![(Type::Named(struct_name.clone()), field_name)];
}
requirements.push(Requirement::Structure(name.clone(), fields));
established_struct_types.insert(struct_name.clone(), fields.clone());
requirements.push(Requirement::Structure(struct_name.clone(), fields));
}
let new_target_type_idx = rng.gen_range(0..valid_types.len());
@@ -366,7 +368,7 @@ fn generate_expr(
Type::Struct(fields) => match find_struct_for_fields(&fields, established_struct_types)
{
None => {
let name = generate_name(rng);
let struct_name = generate_type_name(rng);
let mut new_fields = HashMap::new();
for (field_name, maybe_type) in fields.into_iter() {
@@ -375,20 +377,20 @@ fn generate_expr(
new_fields.insert(field_name, field_type);
}
established_struct_types.insert(name.clone(), new_fields.clone());
established_struct_types.insert(struct_name.clone(), new_fields.clone());
let (subexpr, mut reqs) = generate_expr(
depth + 1,
rng,
established_struct_types,
env,
Some(Type::Named(name.clone())),
Some(Type::Named(struct_name.clone())),
);
let result = Expression::Cast(
Location::manufactured(),
name.current_name().to_string(),
struct_name.current_name().to_string(),
Box::new(subexpr),
);
reqs.push(Requirement::Structure(name, new_fields));
reqs.push(Requirement::Structure(struct_name, new_fields));
(result, reqs)
}
@@ -466,6 +468,7 @@ fn generate_expr(
let mut block = VecDeque::new();
let mut prereqs = vec![];
env.new_scope();
while block.len() < target_block_size_minus_one {
let inner_type = if INNER_BLOCK_TYPE_SHOULD_BE_VOID.sample(rng) {
Some(Type::Named(Name::new("void", Location::manufactured())))
@@ -501,6 +504,7 @@ fn generate_expr(
}
}
}
env.release_scope();
let retval = Expression::Block(Location::manufactured(), block.into_iter().collect());
(retval, prereqs)
@@ -705,19 +709,34 @@ fn find_struct_for_fields(
None
}
fn generate_name(rng: &mut TestRng) -> Name {
fn internal_generate_name(rng: &mut TestRng, is_type_name: bool) -> Name {
static COUNTER: AtomicU64 = AtomicU64::new(0);
let idx = rng.gen_range(0..names::NOUNS.len());
let mut noun = names::NOUNS[idx].to_string();
if is_type_name {
let first_char = noun.get_mut(0..1).expect("has first character");
first_char.make_ascii_uppercase();
}
let name = format!(
"{}{}",
names::NOUNS[idx],
noun,
COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst)
);
Name::new(name, Location::manufactured())
}
fn generate_name(rng: &mut TestRng) -> Name {
internal_generate_name(rng, false)
}
fn generate_type_name(rng: &mut TestRng) -> Name {
internal_generate_name(rng, true)
}
fn generate_type(rng: &mut TestRng, established_struct_types: &mut EstablishedStructMap) -> Type {
let possible_result = TYPE_FREQUENCIES.sample(rng);

View File

@@ -218,6 +218,8 @@ UnaryExpression: Expression = {
Expression::primitive(Location::new(file_idx, l..le), "negate", vec![e]),
<l: @L> "<" <v:"<var>"> ">" <e:UnaryExpression> <le: @L> =>
Expression::Cast(Location::new(file_idx, l..le), v.to_string(), Box::new(e)),
<l: @L> "<" <v:"<type>"> ">" <e:UnaryExpression> <le: @L> =>
Expression::Cast(Location::new(file_idx, l..le), v.to_string(), Box::new(e)),
CallExpression,
}

View File

@@ -28,14 +28,14 @@ impl Program {
interior = interior.indent(9);
let start = allocator.text("struct")
let start = allocator
.text("struct")
.append(allocator.space())
.append(allocator.text(definition.name.original_name().to_string()))
.append(allocator.space())
.append(allocator.text("{"));
let conclusion = allocator.text("}")
.append(allocator.hardline());
let conclusion = allocator.text("}").append(allocator.hardline());
result = result.append(start.append(interior).append(conclusion));
}
@@ -113,7 +113,8 @@ impl TopLevel {
.append(type_bit)
.append(allocator.text(";"))
.append(allocator.hardline())
})).indent(2)
}))
.indent(2),
)
.append(allocator.text("}"))
.append(allocator.hardline()),
@@ -141,7 +142,7 @@ impl Expression {
.append(allocator.text(";"))
.append(allocator.hardline())
}))
.indent(2)
.indent(2),
)
.append(allocator.text("}")),
Expression::Reference(var) => allocator.text(var.to_string()),
@@ -158,7 +159,10 @@ impl Expression {
let mut args = args.iter().map(|x| x.pretty(allocator)).collect::<Vec<_>>();
match fun.as_ref() {
Expression::Primitive(_, name) if ["/", "*", "+", "-"].contains(&name.current_name()) && args.len() == 2 => {
Expression::Primitive(_, name)
if ["/", "*", "+", "-"].contains(&name.current_name())
&& args.len() == 2 =>
{
let second = args.pop().unwrap();
args.pop()
.unwrap()
@@ -168,14 +172,21 @@ impl Expression {
.append(second)
.parens()
}
Expression::Primitive(_, name) if ["negate"].contains(&name.current_name()) && args.len() == 1 =>
allocator.text("-").append(args.pop().unwrap()),
Expression::Primitive(_, name) if ["print"].contains(&&name.current_name()) && args.len() == 1 =>
allocator.text("print")
.append(allocator.space())
.append(args.pop().unwrap()),
Expression::Primitive(_, name)
if ["negate"].contains(&name.current_name()) && args.len() == 1 =>
{
allocator.text("-").append(args.pop().unwrap())
}
Expression::Primitive(_, name)
if ["print"].contains(&&name.current_name()) && args.len() == 1 =>
{
allocator
.text("print")
.append(allocator.space())
.append(args.pop().unwrap())
}
_ => {
let comma_sepped_args = allocator.intersperse(args, allocator.text(","));
@@ -273,7 +284,7 @@ impl Value {
allocator.text(value_str)
}
Value::Void => allocator.text("<void>"),
Value::Void => allocator.text("void()"),
}
}
}
@@ -281,7 +292,7 @@ impl Value {
fn type_suffix(x: &Option<ConstantType>) -> &'static str {
match x {
None => "",
Some(ConstantType::Void) => "<void>",
Some(ConstantType::Void) => panic!("Should never get a void type suffix."),
Some(ConstantType::I8) => "i8",
Some(ConstantType::I16) => "i16",
Some(ConstantType::I32) => "i32",
@@ -314,8 +325,7 @@ impl Type {
.append(allocator.text(";"))
}),
allocator.hardline(),
))
.braces(),
).braces())
}
}
}