private static ISet<Nonterminal> GetNullableNonterminals(ISet<Production> originalProductions) {
var productions = CloneProductions(originalProductions);
ISet<Production> newProductions = new HashSet<Production>();
var nullableNonterminals = new HashSet<Nonterminal>();
var changed = true;
while (changed) {
changed = false;
foreach (var production in productions) {
if (production.IsEmpty) {
nullableNonterminals.Add(production.Lhs);
changed = true;
continue;
}
if (production.Rhs.OnlyTerminals()) {
continue;
}
for (int i = production.Rhs.Count - 1; i >= 0; i--) {
var word = production.Rhs[i];
if (nullableNonterminals.Contains(word)) {
production.Rhs.RemoveAt(i);
changed = true;
}
}
newProductions.Add(production);
}
var oldProductions = productions;
productions = newProductions;
newProductions = oldProductions;
newProductions.Clear();
}
return nullableNonterminals;
}