Reduce probability of stack overflowing when generating random programs
dvberkel opened this issue · comments
As seen in random.rs, it is possible to generate a random program. The strategy for generating a random program is to take the Program
enum and choose, with equal probability, one of the options, and generating a random random element for that option.
Because the Program
can generate abstract syntax trees of unbounded depth, it is possible, and in fact very likely, that the call to generate a random program overflows the stack.
We should change the probabilities of the options so that it becomes less likely that we will generate very deep abstract syntax trees
The current strategy is to randomly return a enum option is by the following code
impl rand::Rand for Command {
fn rand<R: rand::Rng>(rng: &mut R) -> Self {
match rng.gen_range(0, 4) {
i @ 0...1 if i < 1 => Command::Skip,
i @ 1...2 if i < 2 => Command::Left,
i @ 2...3 if i < 3 => Command::Right,
i @ 3...4 if i < 4 => Command::Up,
_ => panic!(),
}
}
}
It is easy to change the probability of the different options by changing the numbers of the match. I am working on a (series of) macros that can create the rust code above.
Ideally it would look like
pickOneOf![10, Command::Skip, 5, Command::Left, 5, Command::Right, 8, Command::Up]
Where the numbers are the weights with which the options are chosen. E.g. the above code should expand to
match rng.gen_range(0, 28) {
i @ 0...10 if i < 10 => Command::Skip,
i @ 10...15 if i < 15 => Command::Left,
i @ 15...20 if i < 20 => Command::Right,
i @ 20...28 if i < 28 => Command::Up,
_ => panic!(),
}
I am partway to the example. I have a macro that when given the correct total and correct ranges expands to the last example. I am now in the process of creating a macro that calculates the total and the ranges.
I am hesitant to do this with one bigger macro, but I will see what the future will bring.
Couldn't pickOneOf
take a list of tuples? Then it wouldn't need to be a macro.
Mmm, good suggestion. It is always wise to check if you are solving a problem with the wrong tool.
The reason I am still convinced a macro is the way to go is user experience. (That would be us :-)
) It is far easier to just write the macro then to create the list with tuples. But maybe I have been sitting in my ivory nursery for too long.
Agreed, totally depends on what the syntax would be. I don't know enough Rust yet to predict :)
I managed to create a macro that adheres to the wanted contract. It landed in commit 8234401.
Although I really like the match
operator, I could not make it happen due. The most problematic part was updating the bounds for each iteration round the provided expressions. I ended up expanding into a if ... else if ... else
structure.
We now only have to use it.