λ Support functions! #5
11284
examples/basic/generated0005.ngr
Normal file
11284
examples/basic/generated0005.ngr
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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()
|
||||
//}
|
||||
|
||||
@@ -322,6 +322,27 @@ fn order_of_operations() {
|
||||
}
|
||||
|
||||
proptest::proptest! {
|
||||
#[test]
|
||||
fn syntax_asts_roundtrip(program in self::arbitrary::ProgramGenerator::default()) {
|
||||
use crate::util::pretty::Allocator;
|
||||
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -169,13 +173,20 @@ impl Expression {
|
||||
.parens()
|
||||
}
|
||||
|
||||
Expression::Primitive(_, name) if ["negate"].contains(&name.current_name()) && args.len() == 1 =>
|
||||
allocator.text("-").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")
|
||||
Expression::Primitive(_, name)
|
||||
if ["print"].contains(&&name.current_name()) && args.len() == 1 =>
|
||||
{
|
||||
allocator
|
||||
.text("print")
|
||||
.append(allocator.space())
|
||||
.append(args.pop().unwrap()),
|
||||
.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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user