faster: Iterative gen_sets

This commit is contained in:
John Doty 2024-04-13 13:32:27 -07:00
parent c5552ab36f
commit 066ad1e982

View file

@ -206,24 +206,25 @@ class GenerateLR0(object):
return tuple(next) return tuple(next)
def gen_sets_recursive(self, config_set, F):
"""Recursively generate all configuration sets starting from the
provided set, and merge them with the provided set 'F'.
"""
if config_set in F:
return F
else:
new_F = F + (config_set,)
for successor in self.gen_all_successors(config_set):
new_F = self.gen_sets_recursive(successor, new_F)
return new_F
def gen_sets(self, config_set): def gen_sets(self, config_set):
"""Recursively generate all configuration sets starting from the """Recursively generate all configuration sets starting from the
provided set, and merge them with the provided set 'F'. provided set, and merge them with the provided set 'F'.
""" """
return self.gen_sets_recursive(config_set, ()) # NOTE: Not a set because we need to maintain insertion order!
# The first element in the dictionary needs to be the intial
# set.
F = {}
pending = [config_set]
while len(pending) > 0:
config_set = pending.pop()
if config_set in F:
continue
F[config_set] = len(F)
for successor in self.gen_all_successors(config_set):
pending.append(successor)
return tuple(F.keys())
def gen_all_sets(self): def gen_all_sets(self):
@ -653,7 +654,7 @@ class GenerateLALR(GenerateLR1):
b_no_la = tuple(s.replace(lookahead=()) for s in b) b_no_la = tuple(s.replace(lookahead=()) for s in b)
return a_no_la == b_no_la return a_no_la == b_no_la
def gen_sets_recursive(self, config_set, F): def gen_sets(self, config_set):
"""Recursively generate all configuration sets starting from the """Recursively generate all configuration sets starting from the
provided set, and merge them with the provided set 'F'. provided set, and merge them with the provided set 'F'.
@ -663,19 +664,23 @@ class GenerateLALR(GenerateLR1):
then instead of returning F unchanged, we merge the two equal sets then instead of returning F unchanged, we merge the two equal sets
and replace the set in F, returning the modified set. and replace the set in F, returning the modified set.
""" """
config_set_no_la = tuple(s.replace(lookahead=()) for s in config_set) F = {}
for index, existing in enumerate(F): pending = [config_set]
existing_no_la = tuple(s.replace(lookahead=()) for s in existing) while len(pending) > 0:
if config_set_no_la == existing_no_la: config_set = pending.pop()
merged_set = self.merge_sets(config_set, existing) config_set_no_la = tuple(s.replace(lookahead=()) for s in config_set)
return F[:index] + (merged_set,) + F[index+1:]
# No merge candidate found, proceed. existing = F.get(config_set_no_la)
new_F = F + (config_set,) if existing is not None:
for successor in self.gen_all_successors(config_set): F[config_set_no_la] = self.merge_sets(config_set, existing)
new_F = self.gen_sets_recursive(successor, new_F) else:
F[config_set_no_la] = config_set
for successor in self.gen_all_successors(config_set):
pending.append(successor)
return new_F # NOTE: We count on insertion order here! The first element must be the
# starting state!
return tuple(F.values())
def find_set_index(self, sets, set): def find_set_index(self, sets, set):
"""Find the specified set in the set of sets, and return the """Find the specified set in the set of sets, and return the