% Prolog representation of a grammar to ask a query of a database
%  This is not meant to be polished or lingustically reasonable, but purely to show what can be done

% This is slightly expanded code of Figure 13.11 in Section 13.6.6 of
% Poole and Mackworth, Artificial Intelligence: foundations of
% computational agents, Cambridge, 2017

% Copyright (c) David Poole and Alan Mackworth 2017. This program
% is released under GPL, version 3 or later; see http://www.gnu.org/licenses/gpl.html

% noun_phrase(L0,L4,Entity) is true if
%  L0 and L4 are list of words, such that
%        L4 is an ending of L0
%        the words in L0 before L4 (written L0-L4) form a noun phrase
%  Entity is an individual that the noun phrase is referring to

% A noun phrase is a determiner followed by adjectives followed
% by a noun followed by an optional modifying phrase:
noun_phrase(L0,L4,Entity) :-
    det(L0,L1,Entity),
    adjectives(L1,L2,Entity),
    noun(L2,L3,Entity),
    mp(L3,L4,Entity).

% Try:
%?- noun_phrase([a,spanish,speaking,country],L1,I1).
%?- noun_phrase([a,country,that,borders,chile],L2,I2).
%?- noun_phrase([a,spanish,speaking,country,that,borders,chile],L3,I3).

% Determiners (articles) are ignored in this oversimplified example.
% They do not provide any extra constraints.
det([the | L],L,_).
det([a | L],L,_).
det(L,L,_).


% adjectives(L0,L1,Entity) is true if 
% L0-L1 is a sequence of adjectives that true of Entity
adjectives(L0,L2,Entity) :-
    adj(L0,L1,Entity),
    adjectives(L1,L2,Entity).
adjectives(L,L,_).

% An optional modifying phrase / relative clause is either
% a relation (verb or preposition) followed by a noun_phrase or
% 'that' followed by a relation then a noun_phrase or
% nothing 
mp(L0,L2,Subject) :-
    reln(L0,L1,Subject,Object),
    noun_phrase(L1,L2,Object).
mp([that|L0],L2,Subject) :-
    reln(L0,L1,Subject,Object),
    noun_phrase(L1,L2,Object).
mp(L,L,_).

% DICTIONARY
% adj(L0,L1,Entity) is true if L0-L1 
% is an adjective that is true of Entity
adj([large | L],L,Entity) :- large(Entity).
adj([Lang,speaking | L],L,Entity) :- speaks(Entity,Lang).
adj([Lang,-,speaking | L],L,Entity) :- speaks(Entity,Lang).

noun([country | L],L,Entity) :- country(Entity).
noun([city | L],L,Entity) :- city(Entity).
noun([X | L],L,X) :- country(X).
noun([X | L],L,X) :- langauge(X).

reln([borders | L],L,O1,O2) :- borders(O1,O2).
reln([the,capital,of | L],L,O1,O2) :- capital(O2,O1).
reln([next,to | L],L,O1,O2) :- borders(O1,O2).

% question(Question,QR,Entity) is true if Query provides an answer about Entity to Question
question(['Is' | L0],L2,Entity) :-
    noun_phrase(L0,L1,Entity),
    mp(L1,L2,Entity).
question(['What',is | L0], L1, Entity) :-
    mp(L0,L1,Entity).
question(['What',is | L0],L1,Entity) :-
    noun_phrase(L0,L1,Entity).
question(['What' | L0],L2,Entity) :-
    noun_phrase(L0,L1,Entity),
    mp(L1,L2,Entity).

% ask(Q,A) gives answer A to question Q
ask(Q,A) :-
    question(Q,[],A).


%  The Database of Facts to be Queried


% country(C) is true if C is a country
country(argentina).
country(brazil).
country(chile).
country(paraguay).
country(peru).

% large(C) is true if the area of C is greater than 2m km^2
large(brazil).
large(argentina).

% language(L) is true if L is a language
language(spanish).
langauge(portugese).

% speaks(Country,Lang) is true of Lang is an official language of Country
speaks(argentina,spanish).
speaks(brazil,portugese).
speaks(chile,spanish).
speaks(paraguay,spanish).
speaks(peru,spanish).

capital(argentina,'Buenos Aires').
capital(chile,'Santiago').
capital(peru,'Lima').
capital(brazil,'Brasilia').
capital(paraguay,'Asunción').

% borders(C1,C2) is true if country C1 borders country C2
borders(peru,chile).
borders(chile,peru).
borders(argentina,chile).
borders(chile,argentina).
borders(brazil,peru).
borders(peru,brazil).
borders(argentina,brazil).
borders(brazil,argentina).
borders(brazil,paraguay).
borders(paraguay,brazil).
borders(argentina,paraguay).
borders(paraguay,argentina).


/* Try the following queries:
?- ask(['What',is,a,country],A).
?- ask(['What',is,a,spanish,speaking,country],A).
?- ask(['What',is,the,capital,of, chile],A).
?- ask(['What',is,the,capital,of, a, country],A).
?- ask(['What',is, a, country, that, borders,chile],A).
?- ask(['What',is, a, country, that, borders,a, country,that,borders,chile],A).
?- ask(['What',is,the,capital,of, a, country, that, borders,chile],A).
?- ask(['What',country,borders,chile],A).
?- ask(['What',country,that,borders,chile,borders,paraguay],A).
*/


% To get the input from a line:

q(Ans) :-
    write("Ask me: "), flush_output(current_output),
    readln(Ln),
    question(Ln,End,Ans),
    member(End,[[],['?'],['.']]).

/*
?- q(Ans).
Ask me: What is a country that borders chile?
Ans = argentina ;
Ans = peru ;
false.

?- q(Ans).
Ask me: What is the capital of a spanish speaking country that borders argentina?
Ans = 'Santiago' ;
Ans = 'Asunción' ;
false.

Some more questions:
What is next to chile?
Is brazil next to peru?
What is a country that borders a country that borders chile.
What is borders chile?
What borders chile?
What country borders chile?
What country that borders chile is next to paraguay?
What country that borders chile next to paraguay?

*/
