-
Notifications
You must be signed in to change notification settings - Fork 3
[RFC?] Weighted Choices #4
Description
I propose a "Weighted Choices" helper function. Sometimes, when one has a highly-recursive grammar, a naïve approach to generating strings would turn into Stack Overflow panics instead of the desired behavior. That's because when we write a grammar, we're implicitly favoring shorter strings in our mind. One could think about it this way: "certainly, a function comprised of 20 nested scopes and 1000 lines of code should never exist".
And that statement is true, but implicit in our reasoning. I propose a helper function for giving different priorities to different choices in a disyuntive (choice!) production. It would be used like this:
let term = choice_weighted_multi(
recursive_rules.clone(),
vec![
("paren_expr".to_string(), 1),
("ids".to_string(), 100),
("ints".to_string(), 100),
]
);The name of the generators are on the left hand side of the tuples, and their "weights" on the right side.
This is how I've implemented the function, if you know of a better way please let me know:
type NameAndWeight = (String, u8);
fn choice_weighted_multi(rule_index: Arc<RwLock<Rules>>, names_and_weights: Vec<NameAndWeight> ) -> impl Generator
{
let mut rules_in_generator: Vec<Box<Generator>> = Vec::new();
for (name, weight) in names_and_weights.iter() {
for _i in 0..*weight {
rules_in_generator.push(
Box::new(rule(name.clone(), rule_index.clone()))
);
}
}
choice(rules_in_generator)
}Thanks to this approach, I'm now fuzzing my parser of Tiny-C. Before, it would run into stackoverflow 8 times out of 10. Now, I can run 5 million different generation steps without a problem :D