| #  Copyright (c) 1998-2002 John Aycock | 
 | # | 
 | #  Permission is hereby granted, free of charge, to any person obtaining | 
 | #  a copy of this software and associated documentation files (the | 
 | #  "Software"), to deal in the Software without restriction, including | 
 | #  without limitation the rights to use, copy, modify, merge, publish, | 
 | #  distribute, sublicense, and/or sell copies of the Software, and to | 
 | #  permit persons to whom the Software is furnished to do so, subject to | 
 | #  the following conditions: | 
 | # | 
 | #  The above copyright notice and this permission notice shall be | 
 | #  included in all copies or substantial portions of the Software. | 
 | # | 
 | #  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
 | #  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
 | #  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | 
 | #  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | 
 | #  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | 
 | #  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | 
 | #  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 
 |  | 
 | __version__ = 'SPARK-0.7 (pre-alpha-5)' | 
 |  | 
 | import re | 
 | import string | 
 |  | 
 | def _namelist(instance): | 
 |     namelist, namedict, classlist = [], {}, [instance.__class__] | 
 |     for c in classlist: | 
 |         for b in c.__bases__: | 
 |             classlist.append(b) | 
 |         for name in c.__dict__.keys(): | 
 |             if not namedict.has_key(name): | 
 |                 namelist.append(name) | 
 |                 namedict[name] = 1 | 
 |     return namelist | 
 |  | 
 | class GenericScanner: | 
 |     def __init__(self, flags=0): | 
 |         pattern = self.reflect() | 
 |         self.re = re.compile(pattern, re.VERBOSE|flags) | 
 |  | 
 |         self.index2func = {} | 
 |         for name, number in self.re.groupindex.items(): | 
 |             self.index2func[number-1] = getattr(self, 't_' + name) | 
 |  | 
 |     def makeRE(self, name): | 
 |         doc = getattr(self, name).__doc__ | 
 |         rv = '(?P<%s>%s)' % (name[2:], doc) | 
 |         return rv | 
 |  | 
 |     def reflect(self): | 
 |         rv = [] | 
 |         for name in _namelist(self): | 
 |             if name[:2] == 't_' and name != 't_default': | 
 |                 rv.append(self.makeRE(name)) | 
 |  | 
 |         rv.append(self.makeRE('t_default')) | 
 |         return string.join(rv, '|') | 
 |  | 
 |     def error(self, s, pos): | 
 |         print "Lexical error at position %s" % pos | 
 |         raise SystemExit | 
 |  | 
 |     def tokenize(self, s): | 
 |         pos = 0 | 
 |         n = len(s) | 
 |         while pos < n: | 
 |             m = self.re.match(s, pos) | 
 |             if m is None: | 
 |                 self.error(s, pos) | 
 |  | 
 |             groups = m.groups() | 
 |             for i in range(len(groups)): | 
 |                 if groups[i] and self.index2func.has_key(i): | 
 |                     self.index2func[i](groups[i]) | 
 |             pos = m.end() | 
 |  | 
 |     def t_default(self, s): | 
 |         r'( . | \n )+' | 
 |         print "Specification error: unmatched input" | 
 |         raise SystemExit | 
 |  | 
 | # | 
 | #  Extracted from GenericParser and made global so that [un]picking works. | 
 | # | 
 | class _State: | 
 |     def __init__(self, stateno, items): | 
 |         self.T, self.complete, self.items = [], [], items | 
 |         self.stateno = stateno | 
 |  | 
 | class GenericParser: | 
 |     # | 
 |     #  An Earley parser, as per J. Earley, "An Efficient Context-Free | 
 |     #  Parsing Algorithm", CACM 13(2), pp. 94-102.  Also J. C. Earley, | 
 |     #  "An Efficient Context-Free Parsing Algorithm", Ph.D. thesis, | 
 |     #  Carnegie-Mellon University, August 1968.  New formulation of | 
 |     #  the parser according to J. Aycock, "Practical Earley Parsing | 
 |     #  and the SPARK Toolkit", Ph.D. thesis, University of Victoria, | 
 |     #  2001, and J. Aycock and R. N. Horspool, "Practical Earley | 
 |     #  Parsing", unpublished paper, 2001. | 
 |     # | 
 |  | 
 |     def __init__(self, start): | 
 |         self.rules = {} | 
 |         self.rule2func = {} | 
 |         self.rule2name = {} | 
 |         self.collectRules() | 
 |         self.augment(start) | 
 |         self.ruleschanged = 1 | 
 |  | 
 |     _NULLABLE = '\e_' | 
 |     _START = 'START' | 
 |     _BOF = '|-' | 
 |  | 
 |     # | 
 |     #  When pickling, take the time to generate the full state machine; | 
 |     #  some information is then extraneous, too.  Unfortunately we | 
 |     #  can't save the rule2func map. | 
 |     # | 
 |     def __getstate__(self): | 
 |         if self.ruleschanged: | 
 |             # | 
 |             #  XXX - duplicated from parse() | 
 |             # | 
 |             self.computeNull() | 
 |             self.newrules = {} | 
 |             self.new2old = {} | 
 |             self.makeNewRules() | 
 |             self.ruleschanged = 0 | 
 |             self.edges, self.cores = {}, {} | 
 |             self.states = { 0: self.makeState0() } | 
 |             self.makeState(0, self._BOF) | 
 |         # | 
 |         #  XXX - should find a better way to do this.. | 
 |         # | 
 |         changes = 1 | 
 |         while changes: | 
 |             changes = 0 | 
 |             for k, v in self.edges.items(): | 
 |                 if v is None: | 
 |                     state, sym = k | 
 |                     if self.states.has_key(state): | 
 |                         self.goto(state, sym) | 
 |                         changes = 1 | 
 |         rv = self.__dict__.copy() | 
 |         for s in self.states.values(): | 
 |             del s.items | 
 |         del rv['rule2func'] | 
 |         del rv['nullable'] | 
 |         del rv['cores'] | 
 |         return rv | 
 |  | 
 |     def __setstate__(self, D): | 
 |         self.rules = {} | 
 |         self.rule2func = {} | 
 |         self.rule2name = {} | 
 |         self.collectRules() | 
 |         start = D['rules'][self._START][0][1][1]        # Blech. | 
 |         self.augment(start) | 
 |         D['rule2func'] = self.rule2func | 
 |         D['makeSet'] = self.makeSet_fast | 
 |         self.__dict__ = D | 
 |  | 
 |     # | 
 |     #  A hook for GenericASTBuilder and GenericASTMatcher.  Mess | 
 |     #  thee not with this; nor shall thee toucheth the _preprocess | 
 |     #  argument to addRule. | 
 |     # | 
 |     def preprocess(self, rule, func):       return rule, func | 
 |  | 
 |     def addRule(self, doc, func, _preprocess=1): | 
 |         fn = func | 
 |         rules = string.split(doc) | 
 |  | 
 |         index = [] | 
 |         for i in range(len(rules)): | 
 |             if rules[i] == '::=': | 
 |                 index.append(i-1) | 
 |         index.append(len(rules)) | 
 |  | 
 |         for i in range(len(index)-1): | 
 |             lhs = rules[index[i]] | 
 |             rhs = rules[index[i]+2:index[i+1]] | 
 |             rule = (lhs, tuple(rhs)) | 
 |  | 
 |             if _preprocess: | 
 |                 rule, fn = self.preprocess(rule, func) | 
 |  | 
 |             if self.rules.has_key(lhs): | 
 |                 self.rules[lhs].append(rule) | 
 |             else: | 
 |                 self.rules[lhs] = [ rule ] | 
 |             self.rule2func[rule] = fn | 
 |             self.rule2name[rule] = func.__name__[2:] | 
 |         self.ruleschanged = 1 | 
 |  | 
 |     def collectRules(self): | 
 |         for name in _namelist(self): | 
 |             if name[:2] == 'p_': | 
 |                 func = getattr(self, name) | 
 |                 doc = func.__doc__ | 
 |                 self.addRule(doc, func) | 
 |  | 
 |     def augment(self, start): | 
 |         rule = '%s ::= %s %s' % (self._START, self._BOF, start) | 
 |         self.addRule(rule, lambda args: args[1], 0) | 
 |  | 
 |     def computeNull(self): | 
 |         self.nullable = {} | 
 |         tbd = [] | 
 |  | 
 |         for rulelist in self.rules.values(): | 
 |             lhs = rulelist[0][0] | 
 |             self.nullable[lhs] = 0 | 
 |             for rule in rulelist: | 
 |                 rhs = rule[1] | 
 |                 if len(rhs) == 0: | 
 |                     self.nullable[lhs] = 1 | 
 |                     continue | 
 |                 # | 
 |                 #  We only need to consider rules which | 
 |                 #  consist entirely of nonterminal symbols. | 
 |                 #  This should be a savings on typical | 
 |                 #  grammars. | 
 |                 # | 
 |                 for sym in rhs: | 
 |                     if not self.rules.has_key(sym): | 
 |                         break | 
 |                 else: | 
 |                     tbd.append(rule) | 
 |         changes = 1 | 
 |         while changes: | 
 |             changes = 0 | 
 |             for lhs, rhs in tbd: | 
 |                 if self.nullable[lhs]: | 
 |                     continue | 
 |                 for sym in rhs: | 
 |                     if not self.nullable[sym]: | 
 |                         break | 
 |                 else: | 
 |                     self.nullable[lhs] = 1 | 
 |                     changes = 1 | 
 |  | 
 |     def makeState0(self): | 
 |         s0 = _State(0, []) | 
 |         for rule in self.newrules[self._START]: | 
 |             s0.items.append((rule, 0)) | 
 |         return s0 | 
 |  | 
 |     def finalState(self, tokens): | 
 |         # | 
 |         #  Yuck. | 
 |         # | 
 |         if len(self.newrules[self._START]) == 2 and len(tokens) == 0: | 
 |             return 1 | 
 |         start = self.rules[self._START][0][1][1] | 
 |         return self.goto(1, start) | 
 |  | 
 |     def makeNewRules(self): | 
 |         worklist = [] | 
 |         for rulelist in self.rules.values(): | 
 |             for rule in rulelist: | 
 |                 worklist.append((rule, 0, 1, rule)) | 
 |  | 
 |         for rule, i, candidate, oldrule in worklist: | 
 |             lhs, rhs = rule | 
 |             n = len(rhs) | 
 |             while i < n: | 
 |                 sym = rhs[i] | 
 |                 if not self.rules.has_key(sym) or \ | 
 |                    not self.nullable[sym]: | 
 |                     candidate = 0 | 
 |                     i = i + 1 | 
 |                     continue | 
 |  | 
 |                 newrhs = list(rhs) | 
 |                 newrhs[i] = self._NULLABLE+sym | 
 |                 newrule = (lhs, tuple(newrhs)) | 
 |                 worklist.append((newrule, i+1, | 
 |                                  candidate, oldrule)) | 
 |                 candidate = 0 | 
 |                 i = i + 1 | 
 |             else: | 
 |                 if candidate: | 
 |                     lhs = self._NULLABLE+lhs | 
 |                     rule = (lhs, rhs) | 
 |                 if self.newrules.has_key(lhs): | 
 |                     self.newrules[lhs].append(rule) | 
 |                 else: | 
 |                     self.newrules[lhs] = [ rule ] | 
 |                 self.new2old[rule] = oldrule | 
 |  | 
 |     def typestring(self, token): | 
 |         return None | 
 |  | 
 |     def error(self, token): | 
 |         print "Syntax error at or near `%s' token" % token | 
 |         raise SystemExit | 
 |  | 
 |     def parse(self, tokens): | 
 |         sets = [ [(1,0), (2,0)] ] | 
 |         self.links = {} | 
 |  | 
 |         if self.ruleschanged: | 
 |             self.computeNull() | 
 |             self.newrules = {} | 
 |             self.new2old = {} | 
 |             self.makeNewRules() | 
 |             self.ruleschanged = 0 | 
 |             self.edges, self.cores = {}, {} | 
 |             self.states = { 0: self.makeState0() } | 
 |             self.makeState(0, self._BOF) | 
 |  | 
 |         for i in xrange(len(tokens)): | 
 |             sets.append([]) | 
 |  | 
 |             if sets[i] == []: | 
 |                 break | 
 |             self.makeSet(tokens[i], sets, i) | 
 |         else: | 
 |             sets.append([]) | 
 |             self.makeSet(None, sets, len(tokens)) | 
 |  | 
 |         #_dump(tokens, sets, self.states) | 
 |  | 
 |         finalitem = (self.finalState(tokens), 0) | 
 |         if finalitem not in sets[-2]: | 
 |             if len(tokens) > 0: | 
 |                 self.error(tokens[i-1]) | 
 |             else: | 
 |                 self.error(None) | 
 |  | 
 |         return self.buildTree(self._START, finalitem, | 
 |                               tokens, len(sets)-2) | 
 |  | 
 |     def isnullable(self, sym): | 
 |         # | 
 |         #  For symbols in G_e only.  If we weren't supporting 1.5, | 
 |         #  could just use sym.startswith(). | 
 |         # | 
 |         return self._NULLABLE == sym[0:len(self._NULLABLE)] | 
 |  | 
 |     def skip(self, (lhs, rhs), pos=0): | 
 |         n = len(rhs) | 
 |         while pos < n: | 
 |             if not self.isnullable(rhs[pos]): | 
 |                 break | 
 |             pos = pos + 1 | 
 |         return pos | 
 |  | 
 |     def makeState(self, state, sym): | 
 |         assert sym is not None | 
 |         # | 
 |         #  Compute \epsilon-kernel state's core and see if | 
 |         #  it exists already. | 
 |         # | 
 |         kitems = [] | 
 |         for rule, pos in self.states[state].items: | 
 |             lhs, rhs = rule | 
 |             if rhs[pos:pos+1] == (sym,): | 
 |                 kitems.append((rule, self.skip(rule, pos+1))) | 
 |         core = kitems | 
 |  | 
 |         core.sort() | 
 |         tcore = tuple(core) | 
 |         if self.cores.has_key(tcore): | 
 |             return self.cores[tcore] | 
 |         # | 
 |         #  Nope, doesn't exist.  Compute it and the associated | 
 |         #  \epsilon-nonkernel state together; we'll need it right away. | 
 |         # | 
 |         k = self.cores[tcore] = len(self.states) | 
 |         K, NK = _State(k, kitems), _State(k+1, []) | 
 |         self.states[k] = K | 
 |         predicted = {} | 
 |  | 
 |         edges = self.edges | 
 |         rules = self.newrules | 
 |         for X in K, NK: | 
 |             worklist = X.items | 
 |             for item in worklist: | 
 |                 rule, pos = item | 
 |                 lhs, rhs = rule | 
 |                 if pos == len(rhs): | 
 |                     X.complete.append(rule) | 
 |                     continue | 
 |  | 
 |                 nextSym = rhs[pos] | 
 |                 key = (X.stateno, nextSym) | 
 |                 if not rules.has_key(nextSym): | 
 |                     if not edges.has_key(key): | 
 |                         edges[key] = None | 
 |                         X.T.append(nextSym) | 
 |                 else: | 
 |                     edges[key] = None | 
 |                     if not predicted.has_key(nextSym): | 
 |                         predicted[nextSym] = 1 | 
 |                         for prule in rules[nextSym]: | 
 |                             ppos = self.skip(prule) | 
 |                             new = (prule, ppos) | 
 |                             NK.items.append(new) | 
 |             # | 
 |             #  Problem: we know K needs generating, but we | 
 |             #  don't yet know about NK.  Can't commit anything | 
 |             #  regarding NK to self.edges until we're sure.  Should | 
 |             #  we delay committing on both K and NK to avoid this | 
 |             #  hacky code?  This creates other problems.. | 
 |             # | 
 |             if X is K: | 
 |                 edges = {} | 
 |  | 
 |         if NK.items == []: | 
 |             return k | 
 |  | 
 |         # | 
 |         #  Check for \epsilon-nonkernel's core.  Unfortunately we | 
 |         #  need to know the entire set of predicted nonterminals | 
 |         #  to do this without accidentally duplicating states. | 
 |         # | 
 |         core = predicted.keys() | 
 |         core.sort() | 
 |         tcore = tuple(core) | 
 |         if self.cores.has_key(tcore): | 
 |             self.edges[(k, None)] = self.cores[tcore] | 
 |             return k | 
 |  | 
 |         nk = self.cores[tcore] = self.edges[(k, None)] = NK.stateno | 
 |         self.edges.update(edges) | 
 |         self.states[nk] = NK | 
 |         return k | 
 |  | 
 |     def goto(self, state, sym): | 
 |         key = (state, sym) | 
 |         if not self.edges.has_key(key): | 
 |             # | 
 |             #  No transitions from state on sym. | 
 |             # | 
 |             return None | 
 |  | 
 |         rv = self.edges[key] | 
 |         if rv is None: | 
 |             # | 
 |             #  Target state isn't generated yet.  Remedy this. | 
 |             # | 
 |             rv = self.makeState(state, sym) | 
 |             self.edges[key] = rv | 
 |         return rv | 
 |  | 
 |     def gotoT(self, state, t): | 
 |         return [self.goto(state, t)] | 
 |  | 
 |     def gotoST(self, state, st): | 
 |         rv = [] | 
 |         for t in self.states[state].T: | 
 |             if st == t: | 
 |                 rv.append(self.goto(state, t)) | 
 |         return rv | 
 |  | 
 |     def add(self, set, item, i=None, predecessor=None, causal=None): | 
 |         if predecessor is None: | 
 |             if item not in set: | 
 |                 set.append(item) | 
 |         else: | 
 |             key = (item, i) | 
 |             if item not in set: | 
 |                 self.links[key] = [] | 
 |                 set.append(item) | 
 |             self.links[key].append((predecessor, causal)) | 
 |  | 
 |     def makeSet(self, token, sets, i): | 
 |         cur, next = sets[i], sets[i+1] | 
 |  | 
 |         ttype = token is not None and self.typestring(token) or None | 
 |         if ttype is not None: | 
 |             fn, arg = self.gotoT, ttype | 
 |         else: | 
 |             fn, arg = self.gotoST, token | 
 |  | 
 |         for item in cur: | 
 |             ptr = (item, i) | 
 |             state, parent = item | 
 |             add = fn(state, arg) | 
 |             for k in add: | 
 |                 if k is not None: | 
 |                     self.add(next, (k, parent), i+1, ptr) | 
 |                     nk = self.goto(k, None) | 
 |                     if nk is not None: | 
 |                         self.add(next, (nk, i+1)) | 
 |  | 
 |             if parent == i: | 
 |                 continue | 
 |  | 
 |             for rule in self.states[state].complete: | 
 |                 lhs, rhs = rule | 
 |                 for pitem in sets[parent]: | 
 |                     pstate, pparent = pitem | 
 |                     k = self.goto(pstate, lhs) | 
 |                     if k is not None: | 
 |                         why = (item, i, rule) | 
 |                         pptr = (pitem, parent) | 
 |                         self.add(cur, (k, pparent), | 
 |                                  i, pptr, why) | 
 |                         nk = self.goto(k, None) | 
 |                         if nk is not None: | 
 |                             self.add(cur, (nk, i)) | 
 |  | 
 |     def makeSet_fast(self, token, sets, i): | 
 |         # | 
 |         #  Call *only* when the entire state machine has been built! | 
 |         #  It relies on self.edges being filled in completely, and | 
 |         #  then duplicates and inlines code to boost speed at the | 
 |         #  cost of extreme ugliness. | 
 |         # | 
 |         cur, next = sets[i], sets[i+1] | 
 |         ttype = token is not None and self.typestring(token) or None | 
 |  | 
 |         for item in cur: | 
 |             ptr = (item, i) | 
 |             state, parent = item | 
 |             if ttype is not None: | 
 |                 k = self.edges.get((state, ttype), None) | 
 |                 if k is not None: | 
 |                     #self.add(next, (k, parent), i+1, ptr) | 
 |                     #INLINED --v | 
 |                     new = (k, parent) | 
 |                     key = (new, i+1) | 
 |                     if new not in next: | 
 |                         self.links[key] = [] | 
 |                         next.append(new) | 
 |                     self.links[key].append((ptr, None)) | 
 |                     #INLINED --^ | 
 |                     #nk = self.goto(k, None) | 
 |                     nk = self.edges.get((k, None), None) | 
 |                     if nk is not None: | 
 |                         #self.add(next, (nk, i+1)) | 
 |                         #INLINED --v | 
 |                         new = (nk, i+1) | 
 |                         if new not in next: | 
 |                             next.append(new) | 
 |                         #INLINED --^ | 
 |             else: | 
 |                 add = self.gotoST(state, token) | 
 |                 for k in add: | 
 |                     if k is not None: | 
 |                         self.add(next, (k, parent), i+1, ptr) | 
 |                         #nk = self.goto(k, None) | 
 |                         nk = self.edges.get((k, None), None) | 
 |                         if nk is not None: | 
 |                             self.add(next, (nk, i+1)) | 
 |  | 
 |             if parent == i: | 
 |                 continue | 
 |  | 
 |             for rule in self.states[state].complete: | 
 |                 lhs, rhs = rule | 
 |                 for pitem in sets[parent]: | 
 |                     pstate, pparent = pitem | 
 |                     #k = self.goto(pstate, lhs) | 
 |                     k = self.edges.get((pstate, lhs), None) | 
 |                     if k is not None: | 
 |                         why = (item, i, rule) | 
 |                         pptr = (pitem, parent) | 
 |                         #self.add(cur, (k, pparent), | 
 |                         #        i, pptr, why) | 
 |                         #INLINED --v | 
 |                         new = (k, pparent) | 
 |                         key = (new, i) | 
 |                         if new not in cur: | 
 |                             self.links[key] = [] | 
 |                             cur.append(new) | 
 |                         self.links[key].append((pptr, why)) | 
 |                         #INLINED --^ | 
 |                         #nk = self.goto(k, None) | 
 |                         nk = self.edges.get((k, None), None) | 
 |                         if nk is not None: | 
 |                             #self.add(cur, (nk, i)) | 
 |                             #INLINED --v | 
 |                             new = (nk, i) | 
 |                             if new not in cur: | 
 |                                 cur.append(new) | 
 |                             #INLINED --^ | 
 |  | 
 |     def predecessor(self, key, causal): | 
 |         for p, c in self.links[key]: | 
 |             if c == causal: | 
 |                 return p | 
 |         assert 0 | 
 |  | 
 |     def causal(self, key): | 
 |         links = self.links[key] | 
 |         if len(links) == 1: | 
 |             return links[0][1] | 
 |         choices = [] | 
 |         rule2cause = {} | 
 |         for p, c in links: | 
 |             rule = c[2] | 
 |             choices.append(rule) | 
 |             rule2cause[rule] = c | 
 |         return rule2cause[self.ambiguity(choices)] | 
 |  | 
 |     def deriveEpsilon(self, nt): | 
 |         if len(self.newrules[nt]) > 1: | 
 |             rule = self.ambiguity(self.newrules[nt]) | 
 |         else: | 
 |             rule = self.newrules[nt][0] | 
 |         #print rule | 
 |  | 
 |         rhs = rule[1] | 
 |         attr = [None] * len(rhs) | 
 |  | 
 |         for i in range(len(rhs)-1, -1, -1): | 
 |             attr[i] = self.deriveEpsilon(rhs[i]) | 
 |         return self.rule2func[self.new2old[rule]](attr) | 
 |  | 
 |     def buildTree(self, nt, item, tokens, k): | 
 |         state, parent = item | 
 |  | 
 |         choices = [] | 
 |         for rule in self.states[state].complete: | 
 |             if rule[0] == nt: | 
 |                 choices.append(rule) | 
 |         rule = choices[0] | 
 |         if len(choices) > 1: | 
 |             rule = self.ambiguity(choices) | 
 |         #print rule | 
 |  | 
 |         rhs = rule[1] | 
 |         attr = [None] * len(rhs) | 
 |  | 
 |         for i in range(len(rhs)-1, -1, -1): | 
 |             sym = rhs[i] | 
 |             if not self.newrules.has_key(sym): | 
 |                 if sym != self._BOF: | 
 |                     attr[i] = tokens[k-1] | 
 |                     key = (item, k) | 
 |                     item, k = self.predecessor(key, None) | 
 |             #elif self.isnullable(sym): | 
 |             elif self._NULLABLE == sym[0:len(self._NULLABLE)]: | 
 |                 attr[i] = self.deriveEpsilon(sym) | 
 |             else: | 
 |                 key = (item, k) | 
 |                 why = self.causal(key) | 
 |                 attr[i] = self.buildTree(sym, why[0], | 
 |                                          tokens, why[1]) | 
 |                 item, k = self.predecessor(key, why) | 
 |         return self.rule2func[self.new2old[rule]](attr) | 
 |  | 
 |     def ambiguity(self, rules): | 
 |         # | 
 |         #  XXX - problem here and in collectRules() if the same rule | 
 |         #        appears in >1 method.  Also undefined results if rules | 
 |         #        causing the ambiguity appear in the same method. | 
 |         # | 
 |         sortlist = [] | 
 |         name2index = {} | 
 |         for i in range(len(rules)): | 
 |             lhs, rhs = rule = rules[i] | 
 |             name = self.rule2name[self.new2old[rule]] | 
 |             sortlist.append((len(rhs), name)) | 
 |             name2index[name] = i | 
 |         sortlist.sort() | 
 |         list = map(lambda (a,b): b, sortlist) | 
 |         return rules[name2index[self.resolve(list)]] | 
 |  | 
 |     def resolve(self, list): | 
 |         # | 
 |         #  Resolve ambiguity in favor of the shortest RHS. | 
 |         #  Since we walk the tree from the top down, this | 
 |         #  should effectively resolve in favor of a "shift". | 
 |         # | 
 |         return list[0] | 
 |  | 
 | # | 
 | #  GenericASTBuilder automagically constructs a concrete/abstract syntax tree | 
 | #  for a given input.  The extra argument is a class (not an instance!) | 
 | #  which supports the "__setslice__" and "__len__" methods. | 
 | # | 
 | #  XXX - silently overrides any user code in methods. | 
 | # | 
 |  | 
 | class GenericASTBuilder(GenericParser): | 
 |     def __init__(self, AST, start): | 
 |         GenericParser.__init__(self, start) | 
 |         self.AST = AST | 
 |  | 
 |     def preprocess(self, rule, func): | 
 |         rebind = lambda lhs, self=self: \ | 
 |                         lambda args, lhs=lhs, self=self: \ | 
 |                                 self.buildASTNode(args, lhs) | 
 |         lhs, rhs = rule | 
 |         return rule, rebind(lhs) | 
 |  | 
 |     def buildASTNode(self, args, lhs): | 
 |         children = [] | 
 |         for arg in args: | 
 |             if isinstance(arg, self.AST): | 
 |                 children.append(arg) | 
 |             else: | 
 |                 children.append(self.terminal(arg)) | 
 |         return self.nonterminal(lhs, children) | 
 |  | 
 |     def terminal(self, token):      return token | 
 |  | 
 |     def nonterminal(self, type, args): | 
 |         rv = self.AST(type) | 
 |         rv[:len(args)] = args | 
 |         return rv | 
 |  | 
 | # | 
 | #  GenericASTTraversal is a Visitor pattern according to Design Patterns.  For | 
 | #  each node it attempts to invoke the method n_<node type>, falling | 
 | #  back onto the default() method if the n_* can't be found.  The preorder | 
 | #  traversal also looks for an exit hook named n_<node type>_exit (no default | 
 | #  routine is called if it's not found).  To prematurely halt traversal | 
 | #  of a subtree, call the prune() method -- this only makes sense for a | 
 | #  preorder traversal.  Node type is determined via the typestring() method. | 
 | # | 
 |  | 
 | class GenericASTTraversalPruningException: | 
 |     pass | 
 |  | 
 | class GenericASTTraversal: | 
 |     def __init__(self, ast): | 
 |         self.ast = ast | 
 |  | 
 |     def typestring(self, node): | 
 |         return node.type | 
 |  | 
 |     def prune(self): | 
 |         raise GenericASTTraversalPruningException | 
 |  | 
 |     def preorder(self, node=None): | 
 |         if node is None: | 
 |             node = self.ast | 
 |  | 
 |         try: | 
 |             name = 'n_' + self.typestring(node) | 
 |             if hasattr(self, name): | 
 |                 func = getattr(self, name) | 
 |                 func(node) | 
 |             else: | 
 |                 self.default(node) | 
 |         except GenericASTTraversalPruningException: | 
 |             return | 
 |  | 
 |         for kid in node: | 
 |             self.preorder(kid) | 
 |  | 
 |         name = name + '_exit' | 
 |         if hasattr(self, name): | 
 |             func = getattr(self, name) | 
 |             func(node) | 
 |  | 
 |     def postorder(self, node=None): | 
 |         if node is None: | 
 |             node = self.ast | 
 |  | 
 |         for kid in node: | 
 |             self.postorder(kid) | 
 |  | 
 |         name = 'n_' + self.typestring(node) | 
 |         if hasattr(self, name): | 
 |             func = getattr(self, name) | 
 |             func(node) | 
 |         else: | 
 |             self.default(node) | 
 |  | 
 |  | 
 |     def default(self, node): | 
 |         pass | 
 |  | 
 | # | 
 | #  GenericASTMatcher.  AST nodes must have "__getitem__" and "__cmp__" | 
 | #  implemented. | 
 | # | 
 | #  XXX - makes assumptions about how GenericParser walks the parse tree. | 
 | # | 
 |  | 
 | class GenericASTMatcher(GenericParser): | 
 |     def __init__(self, start, ast): | 
 |         GenericParser.__init__(self, start) | 
 |         self.ast = ast | 
 |  | 
 |     def preprocess(self, rule, func): | 
 |         rebind = lambda func, self=self: \ | 
 |                         lambda args, func=func, self=self: \ | 
 |                                 self.foundMatch(args, func) | 
 |         lhs, rhs = rule | 
 |         rhslist = list(rhs) | 
 |         rhslist.reverse() | 
 |  | 
 |         return (lhs, tuple(rhslist)), rebind(func) | 
 |  | 
 |     def foundMatch(self, args, func): | 
 |         func(args[-1]) | 
 |         return args[-1] | 
 |  | 
 |     def match_r(self, node): | 
 |         self.input.insert(0, node) | 
 |         children = 0 | 
 |  | 
 |         for child in node: | 
 |             if children == 0: | 
 |                 self.input.insert(0, '(') | 
 |             children = children + 1 | 
 |             self.match_r(child) | 
 |  | 
 |         if children > 0: | 
 |             self.input.insert(0, ')') | 
 |  | 
 |     def match(self, ast=None): | 
 |         if ast is None: | 
 |             ast = self.ast | 
 |         self.input = [] | 
 |  | 
 |         self.match_r(ast) | 
 |         self.parse(self.input) | 
 |  | 
 |     def resolve(self, list): | 
 |         # | 
 |         #  Resolve ambiguity in favor of the longest RHS. | 
 |         # | 
 |         return list[-1] | 
 |  | 
 | def _dump(tokens, sets, states): | 
 |     for i in range(len(sets)): | 
 |         print 'set', i | 
 |         for item in sets[i]: | 
 |             print '\t', item | 
 |             for (lhs, rhs), pos in states[item[0]].items: | 
 |                 print '\t\t', lhs, '::=', | 
 |                 print string.join(rhs[:pos]), | 
 |                 print '.', | 
 |                 print string.join(rhs[pos:]) | 
 |         if i < len(tokens): | 
 |             print | 
 |             print 'token', str(tokens[i]) | 
 |             print |