Search code examples
prologdcg

Converting context-free grammar to pushdown automata in Prolog


I need to get the delta function of a non-deterministic pushdown automaton from a context-free grammar in DCG form, and the algorithm for doing this is very simple:


For every rule A --> B in the grammar, add a transition [q1, lambda, A] --> [B] to the delta function.

For every rule E --> c in the grammar, add a transition [q1, c, c] --> [lambda] to the delta function.

Add the transitions [q0, lambda, z0] --> [q1, S*z0] and [q1, lambda, z0] --> [q2, z0] to the delta function.

Every uppercase letter is a non-terminal symbol, and every lowercase letter is a terminal symbol. lambda is the empty string, S is the initial symbol of the grammar, * is the concatenation operator and z0 is the symbol on the top of the stack.


This means the grammar

S --> A*b | B | y
A --> w*x | x
B --> A*b

generates a pushdown automaton with the delta function

[q0, lambda, z0] --> [q1, S*z0]
[q1, lambda, S] --> [q1, A*b]
[q1, lambda, S] --> [q1, B]
[q1, lambda, S] --> [q1, y]
[q1, lambda, A] --> [q1, w*x]
[q1, lambda, A] --> [q1, x]
[q1, lambda, B] --> [q1, A*b]
[q1, b, b] --> [q1, lambda]
[q1, w, w] --> [q1, lambda]
[q1, x, x] --> [q1, lambda]
[q1, y, y] --> [q1, lambda]
[q1, lambda, z0] --> [q2, z0]

I have to implement this algorithm in Prolog (getting a grammar from user and returning the delta function) and I'm confused because this is my first time using a logic programming language.

I thought I could maybe translate the grammar into its predicate form and then "iterating" over all the predicates, and somehow adding the already "traversed" predicates to a list, so I can process this list and return the delta function. But I think this is some sort of really complicated imperative way of doing things and using Prolog for this would be pointless.

Maybe there's a more elegant solution for this problem, so I'd like to know if such solution exists.


Solution

  • Here a possible coding

    g2nfa(Grammar, Delta) :-
        maplist(transitions, Grammar, DeltaT),
        append(DeltaT, DeltaF),
        Initial = (q0, lambda, z0 --> q1, 'S'*z0),
        Final = (q1, lambda, z0 --> q2, z0),
        append([[Initial], DeltaF, [Final]], Delta).
    
    transitions(Sym --> Alternatives, Transitions) :-
        phrase(transition(Sym, Alternatives), Transitions).
    
    transition(Sym, A|As) --> state(Sym, A), !, transition(Sym, As).
    transition(Sym, A) --> state(Sym, A).
    state(Sym, A) --> [(q1, lambda, Sym --> q1, A)].
    
    test :-
        g2nfa([( 'S' --> 'A'*b | 'B' | y ),
               ( 'A' --> w*x | x ),
               ( 'B' --> 'A'*b )
              ], T), maplist(writeln, T).
    

    that seems to satisfy your requirements

    ?- test.
    q0,lambda,z0-->q1,S*z0
    q1,lambda,S-->q1,A*b
    q1,lambda,S-->q1,B
    q1,lambda,S-->q1,y
    q1,lambda,A-->q1,w*x
    q1,lambda,A-->q1,x
    q1,lambda,B-->q1,A*b
    q1,lambda,z0-->q2,z0
    true.
    

    so you can work out the syntactic details. Note that the second rewrite rule (For every rule E --> c in the grammar, add a transition [q1, c, c] --> [lambda] to the delta function.) has not been applied in your sample.