Skip to content

Commit 07a5e3b

Browse files
committed
init optimizations
1 parent 6900c56 commit 07a5e3b

File tree

1 file changed

+54
-28
lines changed

1 file changed

+54
-28
lines changed

sly/yacc.py

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ def __init__(self, number, name, prod, precedence=('right', 0), func=None, file=
202202
self.file = file
203203
self.line = line
204204
self.prec = precedence
205+
self.lr0_added = 0
205206

206207
# Internal settings used during table construction
207208
self.len = len(self.prod) # Length of the production
@@ -319,7 +320,7 @@ def __init__(self, p, n):
319320
self.prod = list(p.prod)
320321
self.number = p.number
321322
self.lr_index = n
322-
self.lookaheads = {}
323+
self.lookaheads = defaultdict(set)
323324
self.prod.insert(n, '.')
324325
self.prod = tuple(self.prod)
325326
self.len = len(self.prod)
@@ -915,9 +916,7 @@ def traverse(x, N, stack, F, X, R, FP):
915916
if N[y] == 0:
916917
traverse(y, N, stack, F, X, R, FP)
917918
N[x] = min(N[x], N[y])
918-
for a in F.get(y, []):
919-
if a not in F[x]:
920-
F[x].append(a)
919+
F[x].update(F[y])
921920
if N[x] == d:
922921
N[stack[-1]] = MAXINT
923922
F[stack[-1]] = F[x]
@@ -945,7 +944,10 @@ def __init__(self, grammar):
945944
self.lr_action = {} # Action table
946945
self.lr_goto = {} # Goto table
947946
self.lr_productions = grammar.Productions # Copy of grammar Production array
948-
self.lr_goto_cache = {} # Cache of computed gotos
947+
948+
# Cache of computed gotos
949+
self.lr_goto_cache = defaultdict(dict)
950+
self.lr_goto_cache2 = {}
949951
self.lr0_cidhash = {} # Cache of closures
950952
self._add_count = 0 # Internal counter used to detect cycles
951953

@@ -977,6 +979,11 @@ def __init__(self, grammar):
977979
rules = list(actions.values())
978980
if len(rules) == 1 and rules[0] < 0:
979981
self.defaulted_states[state] = rules[0]
982+
# clear cache
983+
self.lr_goto_cache = None
984+
self.lr_goto_cache2 = None
985+
self.lr0_cidhash = None
986+
self.grammar = None
980987

981988
# Compute the LR(0) closure operation on I, where I is a set of LR(0) items.
982989
def lr0_closure(self, I):
@@ -989,7 +996,7 @@ def lr0_closure(self, I):
989996
didadd = False
990997
for j in J:
991998
for x in j.lr_after:
992-
if getattr(x, 'lr0_added', 0) == self._add_count:
999+
if x.lr0_added == self._add_count:
9931000
continue
9941001
# Add B --> .G to J
9951002
J.append(x.lr_next)
@@ -1005,40 +1012,60 @@ def lr0_closure(self, I):
10051012
# objects). With uniqueness, we can later do fast set comparisons using
10061013
# id(obj) instead of element-wise comparison.
10071014

1008-
def lr0_goto(self, I, x):
1015+
def lr0_goto(self, I, x, I_d=None):
10091016
# First we look for a previously cached entry
1010-
g = self.lr_goto_cache.get((id(I), x))
1017+
g = self.lr_goto_cache[id(I)].get(x)
10111018
if g:
10121019
return g
10131020

10141021
# Now we generate the goto set in a way that guarantees uniqueness
10151022
# of the result
10161023

1017-
s = self.lr_goto_cache.get(x)
1024+
s = self.lr_goto_cache2.get(x)
10181025
if not s:
10191026
s = {}
1020-
self.lr_goto_cache[x] = s
1027+
self.lr_goto_cache2[x] = s
10211028

10221029
gs = []
1023-
for p in I:
1024-
n = p.lr_next
1025-
if n and n.lr_before == x:
1030+
1031+
if I_d is None:
1032+
for p in I:
1033+
n = p.lr_next
1034+
if n and n.lr_before == x:
1035+
s1 = s.get(id(n))
1036+
if not s1:
1037+
s1 = {}
1038+
s[id(n)] = s1
1039+
gs.append(n)
1040+
s = s1
1041+
else:
1042+
for n in I_d[x]:
10261043
s1 = s.get(id(n))
10271044
if not s1:
10281045
s1 = {}
10291046
s[id(n)] = s1
10301047
gs.append(n)
10311048
s = s1
1049+
10321050
g = s.get('$end')
10331051
if not g:
10341052
if gs:
10351053
g = self.lr0_closure(gs)
10361054
s['$end'] = g
10371055
else:
10381056
s['$end'] = gs
1039-
self.lr_goto_cache[(id(I), x)] = g
1057+
self.lr_goto_cache[id(I)][x] = g
10401058
return g
10411059

1060+
def get_i_map(self, I):
1061+
# creates structure for faster search
1062+
I_d = defaultdict(list)
1063+
for p in I:
1064+
n = p.lr_next
1065+
if n:
1066+
I_d[n.lr_before].append(n)
1067+
return I_d
1068+
10421069
# Compute the LR(0) sets of item function
10431070
def lr0_items(self):
10441071
C = [self.lr0_closure([self.grammar.Productions[0].lr_next])]
@@ -1053,14 +1080,16 @@ def lr0_items(self):
10531080
I = C[i]
10541081
i += 1
10551082

1083+
I_d = self.get_i_map(I)
1084+
10561085
# Collect all of the symbols that could possibly be in the goto(I,X) sets
10571086
asyms = {}
10581087
for ii in I:
10591088
for s in ii.usyms:
10601089
asyms[s] = None
10611090

10621091
for x in asyms:
1063-
g = self.lr0_goto(I, x)
1092+
g = self.lr0_goto(I, x, I_d)
10641093
if not g or id(g) in self.lr0_cidhash:
10651094
continue
10661095
self.lr0_cidhash[id(g)] = len(C)
@@ -1146,21 +1175,19 @@ def find_nonterminal_transitions(self, C):
11461175
# -----------------------------------------------------------------------------
11471176

11481177
def dr_relation(self, C, trans, nullable):
1149-
dr_set = {}
11501178
state, N = trans
1151-
terms = []
1179+
terms = set()
11521180

11531181
g = self.lr0_goto(C[state], N)
11541182
for p in g:
11551183
if p.lr_index < p.len - 1:
11561184
a = p.prod[p.lr_index+1]
11571185
if a in self.grammar.Terminals:
1158-
if a not in terms:
1159-
terms.append(a)
1186+
terms.add(a)
11601187

11611188
# This extra bit is to handle the start state
11621189
if state == 0 and N == self.grammar.Productions[0].prod[0]:
1163-
terms.append('$end')
1190+
terms.add('$end')
11641191

11651192
return terms
11661193

@@ -1335,14 +1362,11 @@ def compute_follow_sets(self, ntrans, readsets, inclsets):
13351362

13361363
def add_lookaheads(self, lookbacks, followset):
13371364
for trans, lb in lookbacks.items():
1365+
f = followset[trans]
1366+
13381367
# Loop over productions in lookback
13391368
for state, p in lb:
1340-
if state not in p.lookaheads:
1341-
p.lookaheads[state] = set()
1342-
1343-
f = followset.get(trans, [])
1344-
for a in f:
1345-
p.lookaheads[state].add(a)
1369+
p.lookaheads[state].update(f)
13461370

13471371
# -----------------------------------------------------------------------------
13481372
# add_lalr_lookaheads()
@@ -1391,6 +1415,8 @@ def lr_parse_table(self):
13911415

13921416
# Build the parser table, state by state
13931417
for st, I in enumerate(C):
1418+
I_d = self.get_i_map(I)
1419+
13941420
descrip = []
13951421
# Loop over each production in I
13961422
actlist = [] # List of actions
@@ -1468,7 +1494,7 @@ def lr_parse_table(self):
14681494
i = p.lr_index
14691495
a = p.prod[i+1] # Get symbol right after the "."
14701496
if a in self.grammar.Terminals:
1471-
g = self.lr0_goto(I, a)
1497+
g = self.lr0_goto(I, a, I_d)
14721498
j = self.lr0_cidhash.get(id(g), -1)
14731499
if j >= 0:
14741500
# We are in a shift state
@@ -1524,7 +1550,7 @@ def lr_parse_table(self):
15241550
if s in self.grammar.Nonterminals:
15251551
nkeys[s] = None
15261552
for n in nkeys:
1527-
g = self.lr0_goto(I, n)
1553+
g = self.lr0_goto(I, n, I_d)
15281554
j = self.lr0_cidhash.get(id(g), -1)
15291555
if j >= 0:
15301556
st_goto[n] = j

0 commit comments

Comments
 (0)