Example #1
0
    def build_grammar(grammar_txt: str):
        """
        Transform a string in this format:

        S --> A B
        A --> a A | epsilon
        B --> b B | epsilon

        to a Grammar object
        """
        try:
            nonTerminals, terminals, P = [], [], []

            lines = [
                l.strip() for l in grammar_txt.splitlines() if l.strip() != ''
            ]

            head = None

            for l in lines:
                if '->' not in l:
                    l = f'{head} -> {l}'

                head, bodies = l.split('->')
                head = head.split()[0]
                nonTerminals.append(head)

                for body in bodies.split('|'):
                    if body.strip() != '':
                        P.append({'Head': head, 'Body': list(body.split())})
                        terminals.extend(P[-1]['Body'])

            set_terminals, set_nonTerminals = set(terminals).difference(
                nonTerminals + ['epsilon']), set(nonTerminals)

            N, T = [], []

            for nt in nonTerminals:
                if nt in set_nonTerminals and set_nonTerminals.discard(
                        nt) == None:
                    N.append(nt)

            for t in terminals:
                if t in set_terminals and set_terminals.discard(t) == None:
                    T.append(t)

            data = json.dumps({
                'Terminals': T,
                'NonTerminals': N,
                'Productions': P
            })
            G = Grammar.from_json(data)
            # print(data)
            G.startSymbol = G.nonTerminals[0]

            return G
        except:
            return None
    def parseGrammar(self, text: str):
        terminals, nonTerminals, productions = [], [], []

        try:
            lines = text.split('\r\n')

            for line in lines:
                head, bodies = line.split('->')
                head, = head.split()

                if len(head[0]) > 1:
                    raise BadGrammarException()

                nonTerminals.append(head)

                for body in bodies.split('|'):
                    productions.append({
                        'Head': head,
                        'Body': list(body.split())
                    })
                    terminals.extend(productions[-1]['Body'])

        except:
            raise BadGrammarException()

        nonTerminals = set(nonTerminals)
        terminals = set(
            [t for t in terminals if t not in nonTerminals and t != 'epsilon'])

        data = json.dumps({
            'NonTerminals': [nt for nt in nonTerminals],
            'Terminals': [t for t in terminals],
            'Productions': productions
        })

        return Grammar.from_json(data)