17db96d56Sopenharmony_ci# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
27db96d56Sopenharmony_ci# Licensed to PSF under a Contributor Agreement.
37db96d56Sopenharmony_ci
47db96d56Sopenharmony_ci# Pgen imports
57db96d56Sopenharmony_cifrom . import grammar, token, tokenize
67db96d56Sopenharmony_ci
77db96d56Sopenharmony_ciclass PgenGrammar(grammar.Grammar):
87db96d56Sopenharmony_ci    pass
97db96d56Sopenharmony_ci
107db96d56Sopenharmony_ciclass ParserGenerator(object):
117db96d56Sopenharmony_ci
127db96d56Sopenharmony_ci    def __init__(self, filename, stream=None):
137db96d56Sopenharmony_ci        close_stream = None
147db96d56Sopenharmony_ci        if stream is None:
157db96d56Sopenharmony_ci            stream = open(filename, encoding="utf-8")
167db96d56Sopenharmony_ci            close_stream = stream.close
177db96d56Sopenharmony_ci        self.filename = filename
187db96d56Sopenharmony_ci        self.stream = stream
197db96d56Sopenharmony_ci        self.generator = tokenize.generate_tokens(stream.readline)
207db96d56Sopenharmony_ci        self.gettoken() # Initialize lookahead
217db96d56Sopenharmony_ci        self.dfas, self.startsymbol = self.parse()
227db96d56Sopenharmony_ci        if close_stream is not None:
237db96d56Sopenharmony_ci            close_stream()
247db96d56Sopenharmony_ci        self.first = {} # map from symbol name to set of tokens
257db96d56Sopenharmony_ci        self.addfirstsets()
267db96d56Sopenharmony_ci
277db96d56Sopenharmony_ci    def make_grammar(self):
287db96d56Sopenharmony_ci        c = PgenGrammar()
297db96d56Sopenharmony_ci        names = list(self.dfas.keys())
307db96d56Sopenharmony_ci        names.sort()
317db96d56Sopenharmony_ci        names.remove(self.startsymbol)
327db96d56Sopenharmony_ci        names.insert(0, self.startsymbol)
337db96d56Sopenharmony_ci        for name in names:
347db96d56Sopenharmony_ci            i = 256 + len(c.symbol2number)
357db96d56Sopenharmony_ci            c.symbol2number[name] = i
367db96d56Sopenharmony_ci            c.number2symbol[i] = name
377db96d56Sopenharmony_ci        for name in names:
387db96d56Sopenharmony_ci            dfa = self.dfas[name]
397db96d56Sopenharmony_ci            states = []
407db96d56Sopenharmony_ci            for state in dfa:
417db96d56Sopenharmony_ci                arcs = []
427db96d56Sopenharmony_ci                for label, next in sorted(state.arcs.items()):
437db96d56Sopenharmony_ci                    arcs.append((self.make_label(c, label), dfa.index(next)))
447db96d56Sopenharmony_ci                if state.isfinal:
457db96d56Sopenharmony_ci                    arcs.append((0, dfa.index(state)))
467db96d56Sopenharmony_ci                states.append(arcs)
477db96d56Sopenharmony_ci            c.states.append(states)
487db96d56Sopenharmony_ci            c.dfas[c.symbol2number[name]] = (states, self.make_first(c, name))
497db96d56Sopenharmony_ci        c.start = c.symbol2number[self.startsymbol]
507db96d56Sopenharmony_ci        return c
517db96d56Sopenharmony_ci
527db96d56Sopenharmony_ci    def make_first(self, c, name):
537db96d56Sopenharmony_ci        rawfirst = self.first[name]
547db96d56Sopenharmony_ci        first = {}
557db96d56Sopenharmony_ci        for label in sorted(rawfirst):
567db96d56Sopenharmony_ci            ilabel = self.make_label(c, label)
577db96d56Sopenharmony_ci            ##assert ilabel not in first # XXX failed on <> ... !=
587db96d56Sopenharmony_ci            first[ilabel] = 1
597db96d56Sopenharmony_ci        return first
607db96d56Sopenharmony_ci
617db96d56Sopenharmony_ci    def make_label(self, c, label):
627db96d56Sopenharmony_ci        # XXX Maybe this should be a method on a subclass of converter?
637db96d56Sopenharmony_ci        ilabel = len(c.labels)
647db96d56Sopenharmony_ci        if label[0].isalpha():
657db96d56Sopenharmony_ci            # Either a symbol name or a named token
667db96d56Sopenharmony_ci            if label in c.symbol2number:
677db96d56Sopenharmony_ci                # A symbol name (a non-terminal)
687db96d56Sopenharmony_ci                if label in c.symbol2label:
697db96d56Sopenharmony_ci                    return c.symbol2label[label]
707db96d56Sopenharmony_ci                else:
717db96d56Sopenharmony_ci                    c.labels.append((c.symbol2number[label], None))
727db96d56Sopenharmony_ci                    c.symbol2label[label] = ilabel
737db96d56Sopenharmony_ci                    return ilabel
747db96d56Sopenharmony_ci            else:
757db96d56Sopenharmony_ci                # A named token (NAME, NUMBER, STRING)
767db96d56Sopenharmony_ci                itoken = getattr(token, label, None)
777db96d56Sopenharmony_ci                assert isinstance(itoken, int), label
787db96d56Sopenharmony_ci                assert itoken in token.tok_name, label
797db96d56Sopenharmony_ci                if itoken in c.tokens:
807db96d56Sopenharmony_ci                    return c.tokens[itoken]
817db96d56Sopenharmony_ci                else:
827db96d56Sopenharmony_ci                    c.labels.append((itoken, None))
837db96d56Sopenharmony_ci                    c.tokens[itoken] = ilabel
847db96d56Sopenharmony_ci                    return ilabel
857db96d56Sopenharmony_ci        else:
867db96d56Sopenharmony_ci            # Either a keyword or an operator
877db96d56Sopenharmony_ci            assert label[0] in ('"', "'"), label
887db96d56Sopenharmony_ci            value = eval(label)
897db96d56Sopenharmony_ci            if value[0].isalpha():
907db96d56Sopenharmony_ci                # A keyword
917db96d56Sopenharmony_ci                if value in c.keywords:
927db96d56Sopenharmony_ci                    return c.keywords[value]
937db96d56Sopenharmony_ci                else:
947db96d56Sopenharmony_ci                    c.labels.append((token.NAME, value))
957db96d56Sopenharmony_ci                    c.keywords[value] = ilabel
967db96d56Sopenharmony_ci                    return ilabel
977db96d56Sopenharmony_ci            else:
987db96d56Sopenharmony_ci                # An operator (any non-numeric token)
997db96d56Sopenharmony_ci                itoken = grammar.opmap[value] # Fails if unknown token
1007db96d56Sopenharmony_ci                if itoken in c.tokens:
1017db96d56Sopenharmony_ci                    return c.tokens[itoken]
1027db96d56Sopenharmony_ci                else:
1037db96d56Sopenharmony_ci                    c.labels.append((itoken, None))
1047db96d56Sopenharmony_ci                    c.tokens[itoken] = ilabel
1057db96d56Sopenharmony_ci                    return ilabel
1067db96d56Sopenharmony_ci
1077db96d56Sopenharmony_ci    def addfirstsets(self):
1087db96d56Sopenharmony_ci        names = list(self.dfas.keys())
1097db96d56Sopenharmony_ci        names.sort()
1107db96d56Sopenharmony_ci        for name in names:
1117db96d56Sopenharmony_ci            if name not in self.first:
1127db96d56Sopenharmony_ci                self.calcfirst(name)
1137db96d56Sopenharmony_ci            #print name, self.first[name].keys()
1147db96d56Sopenharmony_ci
1157db96d56Sopenharmony_ci    def calcfirst(self, name):
1167db96d56Sopenharmony_ci        dfa = self.dfas[name]
1177db96d56Sopenharmony_ci        self.first[name] = None # dummy to detect left recursion
1187db96d56Sopenharmony_ci        state = dfa[0]
1197db96d56Sopenharmony_ci        totalset = {}
1207db96d56Sopenharmony_ci        overlapcheck = {}
1217db96d56Sopenharmony_ci        for label, next in state.arcs.items():
1227db96d56Sopenharmony_ci            if label in self.dfas:
1237db96d56Sopenharmony_ci                if label in self.first:
1247db96d56Sopenharmony_ci                    fset = self.first[label]
1257db96d56Sopenharmony_ci                    if fset is None:
1267db96d56Sopenharmony_ci                        raise ValueError("recursion for rule %r" % name)
1277db96d56Sopenharmony_ci                else:
1287db96d56Sopenharmony_ci                    self.calcfirst(label)
1297db96d56Sopenharmony_ci                    fset = self.first[label]
1307db96d56Sopenharmony_ci                totalset.update(fset)
1317db96d56Sopenharmony_ci                overlapcheck[label] = fset
1327db96d56Sopenharmony_ci            else:
1337db96d56Sopenharmony_ci                totalset[label] = 1
1347db96d56Sopenharmony_ci                overlapcheck[label] = {label: 1}
1357db96d56Sopenharmony_ci        inverse = {}
1367db96d56Sopenharmony_ci        for label, itsfirst in overlapcheck.items():
1377db96d56Sopenharmony_ci            for symbol in itsfirst:
1387db96d56Sopenharmony_ci                if symbol in inverse:
1397db96d56Sopenharmony_ci                    raise ValueError("rule %s is ambiguous; %s is in the"
1407db96d56Sopenharmony_ci                                     " first sets of %s as well as %s" %
1417db96d56Sopenharmony_ci                                     (name, symbol, label, inverse[symbol]))
1427db96d56Sopenharmony_ci                inverse[symbol] = label
1437db96d56Sopenharmony_ci        self.first[name] = totalset
1447db96d56Sopenharmony_ci
1457db96d56Sopenharmony_ci    def parse(self):
1467db96d56Sopenharmony_ci        dfas = {}
1477db96d56Sopenharmony_ci        startsymbol = None
1487db96d56Sopenharmony_ci        # MSTART: (NEWLINE | RULE)* ENDMARKER
1497db96d56Sopenharmony_ci        while self.type != token.ENDMARKER:
1507db96d56Sopenharmony_ci            while self.type == token.NEWLINE:
1517db96d56Sopenharmony_ci                self.gettoken()
1527db96d56Sopenharmony_ci            # RULE: NAME ':' RHS NEWLINE
1537db96d56Sopenharmony_ci            name = self.expect(token.NAME)
1547db96d56Sopenharmony_ci            self.expect(token.OP, ":")
1557db96d56Sopenharmony_ci            a, z = self.parse_rhs()
1567db96d56Sopenharmony_ci            self.expect(token.NEWLINE)
1577db96d56Sopenharmony_ci            #self.dump_nfa(name, a, z)
1587db96d56Sopenharmony_ci            dfa = self.make_dfa(a, z)
1597db96d56Sopenharmony_ci            #self.dump_dfa(name, dfa)
1607db96d56Sopenharmony_ci            oldlen = len(dfa)
1617db96d56Sopenharmony_ci            self.simplify_dfa(dfa)
1627db96d56Sopenharmony_ci            newlen = len(dfa)
1637db96d56Sopenharmony_ci            dfas[name] = dfa
1647db96d56Sopenharmony_ci            #print name, oldlen, newlen
1657db96d56Sopenharmony_ci            if startsymbol is None:
1667db96d56Sopenharmony_ci                startsymbol = name
1677db96d56Sopenharmony_ci        return dfas, startsymbol
1687db96d56Sopenharmony_ci
1697db96d56Sopenharmony_ci    def make_dfa(self, start, finish):
1707db96d56Sopenharmony_ci        # To turn an NFA into a DFA, we define the states of the DFA
1717db96d56Sopenharmony_ci        # to correspond to *sets* of states of the NFA.  Then do some
1727db96d56Sopenharmony_ci        # state reduction.  Let's represent sets as dicts with 1 for
1737db96d56Sopenharmony_ci        # values.
1747db96d56Sopenharmony_ci        assert isinstance(start, NFAState)
1757db96d56Sopenharmony_ci        assert isinstance(finish, NFAState)
1767db96d56Sopenharmony_ci        def closure(state):
1777db96d56Sopenharmony_ci            base = {}
1787db96d56Sopenharmony_ci            addclosure(state, base)
1797db96d56Sopenharmony_ci            return base
1807db96d56Sopenharmony_ci        def addclosure(state, base):
1817db96d56Sopenharmony_ci            assert isinstance(state, NFAState)
1827db96d56Sopenharmony_ci            if state in base:
1837db96d56Sopenharmony_ci                return
1847db96d56Sopenharmony_ci            base[state] = 1
1857db96d56Sopenharmony_ci            for label, next in state.arcs:
1867db96d56Sopenharmony_ci                if label is None:
1877db96d56Sopenharmony_ci                    addclosure(next, base)
1887db96d56Sopenharmony_ci        states = [DFAState(closure(start), finish)]
1897db96d56Sopenharmony_ci        for state in states: # NB states grows while we're iterating
1907db96d56Sopenharmony_ci            arcs = {}
1917db96d56Sopenharmony_ci            for nfastate in state.nfaset:
1927db96d56Sopenharmony_ci                for label, next in nfastate.arcs:
1937db96d56Sopenharmony_ci                    if label is not None:
1947db96d56Sopenharmony_ci                        addclosure(next, arcs.setdefault(label, {}))
1957db96d56Sopenharmony_ci            for label, nfaset in sorted(arcs.items()):
1967db96d56Sopenharmony_ci                for st in states:
1977db96d56Sopenharmony_ci                    if st.nfaset == nfaset:
1987db96d56Sopenharmony_ci                        break
1997db96d56Sopenharmony_ci                else:
2007db96d56Sopenharmony_ci                    st = DFAState(nfaset, finish)
2017db96d56Sopenharmony_ci                    states.append(st)
2027db96d56Sopenharmony_ci                state.addarc(st, label)
2037db96d56Sopenharmony_ci        return states # List of DFAState instances; first one is start
2047db96d56Sopenharmony_ci
2057db96d56Sopenharmony_ci    def dump_nfa(self, name, start, finish):
2067db96d56Sopenharmony_ci        print("Dump of NFA for", name)
2077db96d56Sopenharmony_ci        todo = [start]
2087db96d56Sopenharmony_ci        for i, state in enumerate(todo):
2097db96d56Sopenharmony_ci            print("  State", i, state is finish and "(final)" or "")
2107db96d56Sopenharmony_ci            for label, next in state.arcs:
2117db96d56Sopenharmony_ci                if next in todo:
2127db96d56Sopenharmony_ci                    j = todo.index(next)
2137db96d56Sopenharmony_ci                else:
2147db96d56Sopenharmony_ci                    j = len(todo)
2157db96d56Sopenharmony_ci                    todo.append(next)
2167db96d56Sopenharmony_ci                if label is None:
2177db96d56Sopenharmony_ci                    print("    -> %d" % j)
2187db96d56Sopenharmony_ci                else:
2197db96d56Sopenharmony_ci                    print("    %s -> %d" % (label, j))
2207db96d56Sopenharmony_ci
2217db96d56Sopenharmony_ci    def dump_dfa(self, name, dfa):
2227db96d56Sopenharmony_ci        print("Dump of DFA for", name)
2237db96d56Sopenharmony_ci        for i, state in enumerate(dfa):
2247db96d56Sopenharmony_ci            print("  State", i, state.isfinal and "(final)" or "")
2257db96d56Sopenharmony_ci            for label, next in sorted(state.arcs.items()):
2267db96d56Sopenharmony_ci                print("    %s -> %d" % (label, dfa.index(next)))
2277db96d56Sopenharmony_ci
2287db96d56Sopenharmony_ci    def simplify_dfa(self, dfa):
2297db96d56Sopenharmony_ci        # This is not theoretically optimal, but works well enough.
2307db96d56Sopenharmony_ci        # Algorithm: repeatedly look for two states that have the same
2317db96d56Sopenharmony_ci        # set of arcs (same labels pointing to the same nodes) and
2327db96d56Sopenharmony_ci        # unify them, until things stop changing.
2337db96d56Sopenharmony_ci
2347db96d56Sopenharmony_ci        # dfa is a list of DFAState instances
2357db96d56Sopenharmony_ci        changes = True
2367db96d56Sopenharmony_ci        while changes:
2377db96d56Sopenharmony_ci            changes = False
2387db96d56Sopenharmony_ci            for i, state_i in enumerate(dfa):
2397db96d56Sopenharmony_ci                for j in range(i+1, len(dfa)):
2407db96d56Sopenharmony_ci                    state_j = dfa[j]
2417db96d56Sopenharmony_ci                    if state_i == state_j:
2427db96d56Sopenharmony_ci                        #print "  unify", i, j
2437db96d56Sopenharmony_ci                        del dfa[j]
2447db96d56Sopenharmony_ci                        for state in dfa:
2457db96d56Sopenharmony_ci                            state.unifystate(state_j, state_i)
2467db96d56Sopenharmony_ci                        changes = True
2477db96d56Sopenharmony_ci                        break
2487db96d56Sopenharmony_ci
2497db96d56Sopenharmony_ci    def parse_rhs(self):
2507db96d56Sopenharmony_ci        # RHS: ALT ('|' ALT)*
2517db96d56Sopenharmony_ci        a, z = self.parse_alt()
2527db96d56Sopenharmony_ci        if self.value != "|":
2537db96d56Sopenharmony_ci            return a, z
2547db96d56Sopenharmony_ci        else:
2557db96d56Sopenharmony_ci            aa = NFAState()
2567db96d56Sopenharmony_ci            zz = NFAState()
2577db96d56Sopenharmony_ci            aa.addarc(a)
2587db96d56Sopenharmony_ci            z.addarc(zz)
2597db96d56Sopenharmony_ci            while self.value == "|":
2607db96d56Sopenharmony_ci                self.gettoken()
2617db96d56Sopenharmony_ci                a, z = self.parse_alt()
2627db96d56Sopenharmony_ci                aa.addarc(a)
2637db96d56Sopenharmony_ci                z.addarc(zz)
2647db96d56Sopenharmony_ci            return aa, zz
2657db96d56Sopenharmony_ci
2667db96d56Sopenharmony_ci    def parse_alt(self):
2677db96d56Sopenharmony_ci        # ALT: ITEM+
2687db96d56Sopenharmony_ci        a, b = self.parse_item()
2697db96d56Sopenharmony_ci        while (self.value in ("(", "[") or
2707db96d56Sopenharmony_ci               self.type in (token.NAME, token.STRING)):
2717db96d56Sopenharmony_ci            c, d = self.parse_item()
2727db96d56Sopenharmony_ci            b.addarc(c)
2737db96d56Sopenharmony_ci            b = d
2747db96d56Sopenharmony_ci        return a, b
2757db96d56Sopenharmony_ci
2767db96d56Sopenharmony_ci    def parse_item(self):
2777db96d56Sopenharmony_ci        # ITEM: '[' RHS ']' | ATOM ['+' | '*']
2787db96d56Sopenharmony_ci        if self.value == "[":
2797db96d56Sopenharmony_ci            self.gettoken()
2807db96d56Sopenharmony_ci            a, z = self.parse_rhs()
2817db96d56Sopenharmony_ci            self.expect(token.OP, "]")
2827db96d56Sopenharmony_ci            a.addarc(z)
2837db96d56Sopenharmony_ci            return a, z
2847db96d56Sopenharmony_ci        else:
2857db96d56Sopenharmony_ci            a, z = self.parse_atom()
2867db96d56Sopenharmony_ci            value = self.value
2877db96d56Sopenharmony_ci            if value not in ("+", "*"):
2887db96d56Sopenharmony_ci                return a, z
2897db96d56Sopenharmony_ci            self.gettoken()
2907db96d56Sopenharmony_ci            z.addarc(a)
2917db96d56Sopenharmony_ci            if value == "+":
2927db96d56Sopenharmony_ci                return a, z
2937db96d56Sopenharmony_ci            else:
2947db96d56Sopenharmony_ci                return a, a
2957db96d56Sopenharmony_ci
2967db96d56Sopenharmony_ci    def parse_atom(self):
2977db96d56Sopenharmony_ci        # ATOM: '(' RHS ')' | NAME | STRING
2987db96d56Sopenharmony_ci        if self.value == "(":
2997db96d56Sopenharmony_ci            self.gettoken()
3007db96d56Sopenharmony_ci            a, z = self.parse_rhs()
3017db96d56Sopenharmony_ci            self.expect(token.OP, ")")
3027db96d56Sopenharmony_ci            return a, z
3037db96d56Sopenharmony_ci        elif self.type in (token.NAME, token.STRING):
3047db96d56Sopenharmony_ci            a = NFAState()
3057db96d56Sopenharmony_ci            z = NFAState()
3067db96d56Sopenharmony_ci            a.addarc(z, self.value)
3077db96d56Sopenharmony_ci            self.gettoken()
3087db96d56Sopenharmony_ci            return a, z
3097db96d56Sopenharmony_ci        else:
3107db96d56Sopenharmony_ci            self.raise_error("expected (...) or NAME or STRING, got %s/%s",
3117db96d56Sopenharmony_ci                             self.type, self.value)
3127db96d56Sopenharmony_ci
3137db96d56Sopenharmony_ci    def expect(self, type, value=None):
3147db96d56Sopenharmony_ci        if self.type != type or (value is not None and self.value != value):
3157db96d56Sopenharmony_ci            self.raise_error("expected %s/%s, got %s/%s",
3167db96d56Sopenharmony_ci                             type, value, self.type, self.value)
3177db96d56Sopenharmony_ci        value = self.value
3187db96d56Sopenharmony_ci        self.gettoken()
3197db96d56Sopenharmony_ci        return value
3207db96d56Sopenharmony_ci
3217db96d56Sopenharmony_ci    def gettoken(self):
3227db96d56Sopenharmony_ci        tup = next(self.generator)
3237db96d56Sopenharmony_ci        while tup[0] in (tokenize.COMMENT, tokenize.NL):
3247db96d56Sopenharmony_ci            tup = next(self.generator)
3257db96d56Sopenharmony_ci        self.type, self.value, self.begin, self.end, self.line = tup
3267db96d56Sopenharmony_ci        #print token.tok_name[self.type], repr(self.value)
3277db96d56Sopenharmony_ci
3287db96d56Sopenharmony_ci    def raise_error(self, msg, *args):
3297db96d56Sopenharmony_ci        if args:
3307db96d56Sopenharmony_ci            try:
3317db96d56Sopenharmony_ci                msg = msg % args
3327db96d56Sopenharmony_ci            except:
3337db96d56Sopenharmony_ci                msg = " ".join([msg] + list(map(str, args)))
3347db96d56Sopenharmony_ci        raise SyntaxError(msg, (self.filename, self.end[0],
3357db96d56Sopenharmony_ci                                self.end[1], self.line))
3367db96d56Sopenharmony_ci
3377db96d56Sopenharmony_ciclass NFAState(object):
3387db96d56Sopenharmony_ci
3397db96d56Sopenharmony_ci    def __init__(self):
3407db96d56Sopenharmony_ci        self.arcs = [] # list of (label, NFAState) pairs
3417db96d56Sopenharmony_ci
3427db96d56Sopenharmony_ci    def addarc(self, next, label=None):
3437db96d56Sopenharmony_ci        assert label is None or isinstance(label, str)
3447db96d56Sopenharmony_ci        assert isinstance(next, NFAState)
3457db96d56Sopenharmony_ci        self.arcs.append((label, next))
3467db96d56Sopenharmony_ci
3477db96d56Sopenharmony_ciclass DFAState(object):
3487db96d56Sopenharmony_ci
3497db96d56Sopenharmony_ci    def __init__(self, nfaset, final):
3507db96d56Sopenharmony_ci        assert isinstance(nfaset, dict)
3517db96d56Sopenharmony_ci        assert isinstance(next(iter(nfaset)), NFAState)
3527db96d56Sopenharmony_ci        assert isinstance(final, NFAState)
3537db96d56Sopenharmony_ci        self.nfaset = nfaset
3547db96d56Sopenharmony_ci        self.isfinal = final in nfaset
3557db96d56Sopenharmony_ci        self.arcs = {} # map from label to DFAState
3567db96d56Sopenharmony_ci
3577db96d56Sopenharmony_ci    def addarc(self, next, label):
3587db96d56Sopenharmony_ci        assert isinstance(label, str)
3597db96d56Sopenharmony_ci        assert label not in self.arcs
3607db96d56Sopenharmony_ci        assert isinstance(next, DFAState)
3617db96d56Sopenharmony_ci        self.arcs[label] = next
3627db96d56Sopenharmony_ci
3637db96d56Sopenharmony_ci    def unifystate(self, old, new):
3647db96d56Sopenharmony_ci        for label, next in self.arcs.items():
3657db96d56Sopenharmony_ci            if next is old:
3667db96d56Sopenharmony_ci                self.arcs[label] = new
3677db96d56Sopenharmony_ci
3687db96d56Sopenharmony_ci    def __eq__(self, other):
3697db96d56Sopenharmony_ci        # Equality test -- ignore the nfaset instance variable
3707db96d56Sopenharmony_ci        assert isinstance(other, DFAState)
3717db96d56Sopenharmony_ci        if self.isfinal != other.isfinal:
3727db96d56Sopenharmony_ci            return False
3737db96d56Sopenharmony_ci        # Can't just return self.arcs == other.arcs, because that
3747db96d56Sopenharmony_ci        # would invoke this method recursively, with cycles...
3757db96d56Sopenharmony_ci        if len(self.arcs) != len(other.arcs):
3767db96d56Sopenharmony_ci            return False
3777db96d56Sopenharmony_ci        for label, next in self.arcs.items():
3787db96d56Sopenharmony_ci            if next is not other.arcs.get(label):
3797db96d56Sopenharmony_ci                return False
3807db96d56Sopenharmony_ci        return True
3817db96d56Sopenharmony_ci
3827db96d56Sopenharmony_ci    __hash__ = None # For Py3 compatibility.
3837db96d56Sopenharmony_ci
3847db96d56Sopenharmony_cidef generate_grammar(filename="Grammar.txt"):
3857db96d56Sopenharmony_ci    p = ParserGenerator(filename)
3867db96d56Sopenharmony_ci    return p.make_grammar()
387