The algorithm as you have written it is quadratic  (ret.addAll() is the culprit.)  Instead of returning a Set<NonTerminal>, pass that as parameter and have each call just add to it.  You should do the same for "visited" so it is not being "globally" modified.