1

I have created a sentence parser in prolog. It successfully parses sentences that are inputted with... ?- sentence([input,sentence,here],Parse).

This is the code I'm using to parse the sentence:

np([X|T],np(det(X),NP2),Rem):- /* Det NP2 */
    det(X),
    np2(T,NP2,Rem).
np(Sentence,Parse,Rem):- np2(Sentence,Parse,Rem). /* NP2 */
np(Sentence,np(NP,PP),Rem):- /* NP PP */
    np(Sentence,NP,Rem1),
    pp(Rem1,PP,Rem).

np2([H|T],np2(noun(H)),T):- noun(H). /* Noun */
np2([H|T],np2(adj(H),Rest),Rem):- adj(H),np2(T,Rest,Rem).

pp([H|T],pp(prep(H),Parse),Rem):- /* PP NP */
    prep(H),
    np(T,Parse,Rem).

vp([H| []], vp(verb(H))):- /* Verb */
    verb(H).

vp([H|T], vp(verb(H), Rem)):- /* VP PP */
    vp(H, Rem),
    pp(T, Rem, _).

vp([H|T], vp(verb(H), Rem)):- /* Verb NP */
    verb(H),
    np(T, Rem, _).

I should mention that the output would be: sentence(np(det(a), np2(adj(very), np2(adj(young), np2(noun(boy))))), vp(verb(loves), np(det(a), np2(adj(manual), np2(noun(problem)))))).

Using the predefined vocabulary: det(a), adj(very), adj(young), noun(boy), verb(loves), det(a), adj(manual), noun(problem).

What I want to do is pass the parsed output to predicates that would separate the words into three different categories, which are "subject, verb, and object".

(1) The subject will hold the first two adjectives and then a noun.

(2) The verb will hold the verb from the "verb phrase".

(3) And the object will hold the adjectives and nouns in the "verb phrase".

All determiners should be ignored.

For example, I would want a predicate that would look for adjectives in the output.

I have tried many things to try and get this working but none work. Any help will be much appreciated.

Joseph
  • 120
  • 9

2 Answers2

2

I am making a second attempt, then.

the output would be: sentence(np(det(a), np2(adj(very), np2(adj(young), np2(noun(boy))))), vp(verb(loves), np(det(a), np2(adj(manual), np2(noun(problem)))))). [...] What I want to do is pass the parsed output to predicates that would separate the words into three different categories, which are "subject, verb, and object".

You can write procedures like these, that map from your structures to lists of words.

handle_sent(sentence(NP1,vp(V,NP2)),Subj,Verb,Obj) :-
  handle_np(NP1,Subj), handle_verb(V,Verb), handle_np(NP2,Obj).

handle_verb(verb(V),[V]).

handle_np(np(_,np2(adj(A),np2(noun(N)))),[A,N]).
handle_np(np(_,np2(adj(A1),np2(adj(A2),np2(noun(N))))),[A1,A2,N]).

This produces:

?- handle_sent(...,Subj,Verb,Obj).
Subj = [very,young,boy]
Verb = [loves]
Obj = [manual,problem]
Tomas By
  • 1,396
  • 1
  • 11
  • 23
1

The DCG below produces this behaviour:

?- s(Sem,[a,young,boy,loves,a,manual,problem],[]).
Sem = [noun(boy),verb(loves),noun(problem)]

There are some problems in your grammar. Your third np clause calls itself directly (not consuming input in between), which means infinite loop. The grammar you posted does not seem to be able to produce your output (very young). Anyway, here is the DCG:

s(Sem) -->
  np(Sem1), vp(Sem2), { append(Sem1,Sem2,Sem) }.

np(Sem) -->
  [W], { det(W) },
  np2(Sem).
np(Sem) -->
  np2(Sem).
np(Sem) -->
  np2(Sem1),
  pp(Sem2), { append(Sem1,Sem2,Sem) }.

np2([noun(W)]) -->
  [W], { noun(W) }.
np2(Sem) -->
  [W], { adj(W) },
  np2(Sem).

pp(Sem) -->
  [W], { prep(W) },
  np(Sem).

vp([verb(W)]) -->
  [W], { verb(W) }.
vp(Sem) -->
  [W], { verb(W) },
  np(Sem0), { Sem = [verb(W)|Sem0] }.
vp(Sem) -->
  [W], { verb(W) },
  pp(Sem0), { Sem = [verb(W)|Sem0] }.

Addition: if you want to handle modification (e.g. adjectives), then there are simple obvious solutions that quickly become impractical, and then there are more general techniques, like adding a logical variable to the np.

np(X,Sem) -->
  [W], { det(W) },
  np2(X,Sem).
np(X,Sem) -->
  np2(X,Sem).
np(X,Sem) -->
  np2(X,Sem1),
  pp(Sem2), { append(Sem1,Sem2,Sem) }.

np2(X,[noun(X,W)]) -->
  [W], { noun(W) }.
np2(X,[adj(X,W)|Sem]) -->
  [W], { adj(W) },
  np2(X,Sem).

This variable (X) is never instantiated, it only serves to link the parts of the noun phrase meaning together.

?- s(Sem,[a,young,boy,loves,a,manual,problem],[]).
Sem = [adj(_A,young),noun(_A,boy),verb(loves),adj(_B,manual),noun(_B,problem)]

There are various additional possibilities. Good books are Gazdar & Mellish, NLP in Prolog, and Norvig, Paradigms of AI programming (if you speak Lisp), as well as Pereira & Shieber, Prolog and natural-language analysis.

Addition #2: after reading your question again, and this other question, I realised that you actually want three separate lists. No problem.

s(L1,L2,L3) -->
  np(_,L1), vp(L2,L3).

np(X,L) -->
  [W], { det(W) },
  np2(X,L).
np(X,L) -->
  np2(X,L).
np(X,L) -->
  np2(X,L),
  pp(_).

np2(X,[noun(X,W)]) -->
  [W], { noun(W) }.
np2(X,[adj(X,W)|L]) -->
  [W], { adj(W) },
  np2(X,L).

pp(L) -->
  [W], { prep(W) },
  np(_,L).

vp([verb(W)],[]) -->
  [W], { verb(W) }.
vp([verb(W)],L) -->
  [W], { verb(W) },
  np(_,L).
vp([verb(W)],L) -->
  [W], { verb(W) },
  pp(L).

Output:

| ?- s(L1,L2,L3,[a,young,boy,loves,a,manual,problem],[]).
L1 = [adj(_A,young),noun(_A,boy)],
L2 = [verb(loves)],
L3 = [adj(_B,manual),noun(_B,problem)] ? 

Now maybe you don't need the logical variables, but one the other hand, you could have more complicated modifiers, like "a young boy loves a manual problem involving red bolts and white cubes". Then the variables would keep track of which adjective modifies which noun.

Tomas By
  • 1,396
  • 1
  • 11
  • 23
  • Would I be able to do this by using separate predicates like 'extractnounphrase(np(det(A),_)):-' by passing the parsed value to them? Instead of doing it as we go along. – Joseph Dec 09 '17 at 21:44
  • Well, it would be like having two parsers, since the output you are currently producing is isomorphic to the rules of your grammar. DCG is much better. – Tomas By Dec 09 '17 at 21:58
  • I understand that DSG is better (because people keep telling me), but I'm still to learn this. I was wondering if you could show me how to do it in the way I've been doing it. – Joseph Dec 09 '17 at 22:13