%%% TALK Program, adapted from Pereira & Shieber (1987).
:- op(500,xfy,'&').
:- op(510,xfy,'=>').
:- op(100,fx,'`').
run :-
write('>> '), % prompt the user
read_sent(Words), % read a sentence
talk(Words, Reply), % process it with TALK
print_reply(Reply), % generate a reply
run. % pocess more sentences
%%% talk(Sentence, Reply)
%%%
%%% Sentence ==> sentence to form a reply to
%%% Reply <== appropriate reply to the sentence
talk(Sentence, Reply) :- % parse the sentence
parse(Sentence, LF, Type),
% convert the FOL logical form into a Horn
% clause, if possible
clausify(LF, Clause, FreeVars), !,
% concoct a reply, based on the clause and
% whether sentence was a query or assertion
reply(Type, FreeVars, Clause, Reply).
% No parse was found; sentence is too difficult for the grammar
% or the lexicon.
talk(_Sentence, error('too difficult')).
%%% parse(sentence, LF, Type)
%%%
%%% Sentence ==> sentence to parse
%%% LF <== logical form (in FOL) of sentence
%%% Type <== type of Sentence
%%% (query or assertion)
% Parsing an assertion: a finite sentence without gaps.
parse(Sentence, LF, assertion) :- s(LF, nogap, Sentence, []).
% Parsing a query: a question.
parse(Sentence, LF, query) :- q(LF, Sentence, []).
/* Nonterminal names:
q Question
sinv INVerted Sentence
s noninverted Sentence
np Noun Phrase
vp Verb Phrase
iv Intransitive Verb
tv Transitive Verb
aux AUXiliary verb
rov subject_Object Raising Verb
optrel OPTional RELative clause
relpron RELative PRONoun
whpron WH PRONoun
det DETerminer
n Noun
pn Proper Noun
Typical order of and values for arguments:
1. verb form:
(main verbs) finite, nonfinite, etc.
(auxiliaries and raising verbs) Form1-Form2
where Forml is form of embedded VP
Form2 is form of verb itself
2. FOL logical form
3. gap information:
nogap or gap(Nonterm, Var)
where Nonterm is nonterminal for gap
Var is the LF variable that
the filler will bind
*/
%%% Declarative Sentences
s(S, GapInfo) --> np(VP^S, nogap), vp(finite, VP, GapInfo).
%%% Inverted Sentences
sinv(S, GapInfo) --> aux(finite/Form, VP1^VP2),
np(VP2^S, nogap), vp(Form, VP1, GapInfo).
%%% Questions
q(S => `answer(X)) --> whpron, vp(finite, X^S, nogap).
q(S => `answer(X)) --> whpron, sinv(S, gap(np, X)).
q(S => `answer(yes)) --> sinv(S, nogap).
q(S => `answer(yes)) --> [is], np((X^S0)^S, nogap),
np((X^true)^exists(X,S0&true), nogap).
%%% Noun Phrases
np(NP, nogap) --> det(N2^NP), n(N1), optrel(N1^N2).
np(NP, nogap) --> pn(NP).
np((X^S)^S, gap(np, X)) --> [].
%%% Verb Phrases
vp(Form, X^S, GapInfo) --> tv(Form, X^VP), np(VP^S, GapInfo).
vp(Form, VP, nogap) --> iv(Form, VP).
vp(Form1, VP2, GapInfo) --> aux(Form1/Form2, VP1^VP2),
vp(Form2, VP1, GapInfo).
vp(Form1, VP2, GapInfo) --> rov(Form1/Form2, NP^VP1^VP2),
np(NP, GapInfo), vp(Form2, VP1, nogap).
vp(Form2, VP2, GapInfo) --> rov(Form1/Form2, NP^VP1^VP2),
np(NP, nogap), vp(Form1, VP1, GapInfo).
vp(finite, X^S, GapInfo) --> [is], np((X^P)^exists(X,S&P), GapInfo).
%%% Relative Clauses
optrel((X^S1)^(X^(S1&S2))) --> relpron, vp(finite, X^S2, nogap).
optrel((X^S1)^(X^(S1&S2))) --> relpron, s(S2, gap(np, X)).
optrel(N^N) --> [].
% Dictionary
% Verb entry arguments:
% 1. nonfinite form of the verb
% 2. third person singular present tense form of the verb
% 3. past tense form of the verb
% 4. past participle form of the verb
% 5. pres participle form of the verb
% 6. logical form of the verb
iv(nonfinite, LF) --> [IV], {iv(IV, _, _, _, _, LF)}.
iv(finite, LF) --> [IV], {iv(_, IV, _, _, _, LF)}.
iv(finite, LF) --> [IV], {iv(_, _, IV, _, _, LF)}.
iv(past_participle, LF) --> [IV], {iv(_, _, _, IV, _, LF)}.
iv(pres_participle, LF) --> [IV], {iv(_, _, _, _, IV, LF)}.
iv(halt, halts, halted, halted, halting, X^ `halts(X)).
iv(run, runs, ran, run, running, X^ `runs(X)).
tv(nonfinite, LF) --> [TV], {tv(TV, _, _, _, _, LF)}.
tv(finite, LF) --> [TV], {tv(_, TV, _, _, _, LF)}.
tv(finite, LF) --> [TV], {tv(_, _, TV, _, _, LF)}.
tv(past_participle, LF) --> [TV], {tv(_, _, _, TV, _, LF)}.
tv(pres_participle, LF) --> [TV], {tv(_, _, _, _, TV, LF)}.
tv(write, writes, wrote, written, writing, X^Y^ `writes(X,Y)).
tv(read, reads, read, read, reading, X^Y^ `reads(X,Y)).
tv(speak, speaks, spoke, spoken, speaking, X^Y^ `speaks(X,Y)).
tv(meet, meets, met, met, meeting, X^Y^ `meets(X,Y)).
tv(concern, concerns, concerned, concerned, concerning, X^Y^ `concerns(X,Y)).
tv(run, runs, ran, run, running, X^Y^ `runs(X,Y)).
rov(nonfinite /Requires, LF) --> [ROV], {rov(ROV, _, _, _, _, LF, Requires)}.
rov(finite /Requires, LF) --> [ROV], {rov(_, ROV, _, _, _, LF, Requires)}.
rov(finite /Requires, LF) --> [ROV], {rov(_, _, ROV, _, _, LF, Requires)}.
rov(past_participle/Requires, LF) --> [ROV], {rov(_, _, _, ROV, _, LF, Requires)}.
rov(pres_participle/Requires, LF) --> [ROV], {rov(_, _, _, _, ROV, LF, Requires)}.
rov(want, wants, wanted, wanted, wanting,
% semantics is partial execution of
% NP ^ VP ^ Y ^ NP( X^want(Y,X,VP(X))
((X^ `want(Y, X, Comp))^S) ^ (X^Comp) ^ Y ^ S,
% form of VP required:
infinitival).
aux(Form, LF) --> [Aux], {aux(Aux, Form, LF)}.
aux(to, infinitival/nonfinite, VP^ VP).
aux(does, finite/nonfinite, VP^ VP).
aux(did, finite/nonfinite, VP^ VP).
aux(could, finite/nonfinite, VP^ VP).
aux(have, nonfinite/past_participle, VP^ VP).
aux(has, finite/past_participle, VP^ VP).
aux(been, past_participle/present_participle, VP^ VP).
aux(be, nonfinite/present_participle, VP^ VP).
relpron --> [RP], {relpron(RP)}.
relpron(that).
relpron(who).
relpron(whom).
whpron --> [WH], {whpron(WH)}.
whpron(who).
whpron(whom).
whpron(what).
det(LF) --> [D], {det(D, LF)}.
det(every, (X^S1) ^ (X^S2) ^ all(X, S1=>S2)).
det(a, (X^S1) ^ (X^S2) ^ exists(X, S1&S2)).
det(some, (X^S1) ^ (X^S2) ^ exists(X, S1&S2)).
n(LF) --> [N], {n(N, LF)}.
n(author, X^ `author(X)).
n(book, X^ `book(X)).
n(professor, X^ `professor(X)).
n(program, X^ `program(X)).
n(programmer, X^ `programmer(X)).
n(student, X^ `student(X)).
n(person, X^ `person(X)).
n(language, X^ `language(X)).
pn((E^S)^S) --> [PN], {pn(PN, E)}.
pn(allen, allen).
pn(bruce, bruce).
pn(bertrand, bertrand).
pn(terry, terry).
pn(bill, bill).
pn(david, david).
pn(kathy, kathy).
pn(behshad, behshad).
pn(shane, shane).
pn(principia, principia).
pn(shrdlu, shrdlu).
pn(prolog, prolog).
pn(english, english).
pn(chinese, chinese).
pn(korean, korean).
pn(swahili, swahili).
%%% Clausifier
%%% clausify(FOL, Clause, FreeVars)
%%%
%%% FOL ==> FOL expression to be converted to clause form
%%% Clause <== clause form of FOL expression
%%% FreeVars <== free variables in clause
% Universals: variable is left implicitly scoped.
clausify(all(X,F0),F,[X|V]) :- clausify(F0,F,V).
% Implications: consequent must be a literal,
% antecedent is clausified specially.
clausify(A0=>C0,(C:-A),V) :- clausify_literal(C0,C),
clausify_antecedent(A0,A,V).
% Literals: left unchanged (except literal marker is removed).
clausify(C0,C,[]) :- clausify_literal(C0,C).
% Note that conjunctions and existentials are
% disallowed, since they can't form Horn clauses.
%%% clausify_antecedent(FOL, Clause, FreeVars)
%%%
%%% FOL ==> FOL expression to be converted to clause form
%%% Clause <== clause form of FOL expression
%%% FreeVars ==> list of free variables in clause
% Literals: left unchanged (except literal marker is removed).
clausify_antecedent(L0,L,[]) :- clausify_literal(L0,L).
% Conjunctions: each conjunct is clausified separately.
clausify_antecedent(E0&F0, (E,F), V) :-
clausify_antecedent(E0,E,V0),
clausify_antecedent(F0,F,V1),
conc(V0,V1,V).
% Existentials: variable is left implicitly scoped.
clausify_antecedent(exists(X,F0), F, [X|V]) :-
clausify_antecedent(F0,F,V).
%%% clausify_literal(Literal, Clause)
%%%
%%% Literal ==> FOL literal to be converted
%%% to clause form
%%% Clause <== clause form of FOL expression
% Literal is left unchanged (except literal marker is removed).
clausify_literal(`L, L).
% Auxiliary Predicates
conc([], List, List).
conc([Element|Rest], List, [Element|LongRest]) :-
conc(Rest, List, LongRest).
%%% read_sent(Words)
%%% input ==> series of words terminated by a new line character
%%% Words <== list of the words
read_sent(Words) :- get0(Char), read_sent(Char, Words).
read_sent(C, []) :- newline(C), !.
read_sent(C, Words) :- space(C), !, get0(Char),
read_sent(Char, Words).
read_sent(C, [Word|Words]) :- read_word(C, Chars, Next),
name(Word, Chars),
read_sent(Next, Words).
read_word(C, [], C) :- space(C), !.
read_word(C, [], C) :- newline(C), !.
read_word(C, [C|Chars], Last) :- get0(Next),
read_word(Next, Chars, Last).
newline(10).
space(32).
%%% reply(Type, FreeVars, Clause, Reply)
%%%
%%% Type ==> the constant "query" or "assertion"
%%% depending on whether clause should
%%% be interpreted as a query or assertion
%%% FreeVars ==> the free variables (to be
%%% interpreted existentially) in the clause
%%% Clause ==> the clause being replied to
%%% Reply <== the reply
%%%
%%% If the clause is interpreted as an assertion,
%%% the predicate has a side effect of asserting
%%% the clause to the database.
%Replying to a query.
reply(query, FreeVars,
(answer(Answer):-Condition), Reply) :-
% find all the answers that satisfy the query,
% replying with that set if it exists, or "no"
% or "none" if it doesn't.
(setof(Answer, FreeVars^Condition, Answers)
-> Reply = answer(Answers)
; (Answer = yes
-> Reply = answer([no])
; Reply = answer([none]))), !.
% Replying to an assertion.
% assert the assertion and tell user what we asserted
reply(assertion, _FreeVars, Assertion, asserted(Assertion)) :-
assert(Assertion), !.
% Replying to some other type of sentence.
reply(_Type, _FreeVars, _Clause, error('unknown type')).
%%% print_reply(Reply)
%%%
%%% Reply ==> reply generated by reply predicate
%%% that is to be printed to the standard output.
print_reply(error(ErrorType)) :-
write('Error: "'), write(ErrorType), write('."'), nl.
print_reply(asserted(Assertion)) :-
write('Asserted "'), write(Assertion), write('."'), nl.
print_reply(answer(Answers)) :- print_answers(Answers).
%%% print_answer(Answers)
%%%
%%% Answers ==> nonempty list of answers to be printed
%%% to the standard output separated by commas.
print_answers([Answer]) :- !, write(Answer), write('.'), nl.
print_answers([Answer|Rest]) :- write(Answer), write(', '),
print_reply(answer(Rest)).