/*---------------------------------------------------------------------* AN IMPLEMENTATION OF PLURALITY IN DISCOURSE REPRESENTATION THEORY P L U R A L - D R T By: Laurel K. Graham date: May 4th, 1994 This program takes natural langague sentences as input, represents these sentences as DRSs, displays the DRSs, and translates the semantic information in the DRSs into prolog structures. They can then be asserted into the databased, or used to query existing information in the database. This program must be loaded into GULP3 on Quintus Prolog 3.1.4. PluralDRT's code is based on code written by M.A. Covington. (See thesis for details.) Some of the code has remained unchanged, while other parts have undergone radical changes. To execute, type: ?- go. *------------------------------------------------------------------*/ /*------------------------------------------------------------------* FEATURE AND OPERATOR DECLARATIONS *------------------------------------------------------------------*/ :- no_style_check(discontiguous). :- dynamic unique_aux/1. :- dynamic fact/1. :- dynamic is_query/0. :- dynamic is_dist/1. g_features([in,out,syn,sem,index,scope,res,class,arg1,arg2,number, type,noun,amt,det,name,case]). :- op(1200, /* priority same as :- */ xfx, /* no associativity */ '::-'). /* This operator is used as the internal representation for conditionals. It is meant to look a lot like the ordinary Prolog ':-'. However, it has two super- powers: (1) GoalA ::- GoalB can be queried as well as asserted. (2) In GoalA ::- GoalB, GoalA as well as GoalB can be compound. */ /*------------------------------------------------------------------* COMPUTATIONAL UTILITIES *------------------------------------------------------------------*/ /* * reverse(List,Result) * Fast list reversal with stacks. * NOTE: If the tail of the list is uninstantiated, this procedure * will instantiate it to nil. So don't use this procedure on * open lists that need to remain open. */ reverse(List,Result) :- nonvar(List), reverse_aux(List,[],Result). reverse_aux([],Result,Result). reverse_aux([H|T],Stack,Result) :- reverse_aux(T,[H|Stack],Result). /* * Unique_integer(N) * unifies N with a different integer every time it is called. */ unique_integer(N) :- retract(unique_aux(N)), NN is N+1, assert(unique_aux(NN)), !. /* Cut needed by Quintus, not Arity */ unique_aux(0). /*------------------------------------------------------------------* I/O UTILITIES *------------------------------------------------------------------*/ /* * display_drs(X) * outputs a readable representation of a DRS */ display_drs(X) :- display_drs_indented(X,0). display_drs_indented(X,N) :- var(X), !, tab(N), write(X),nl. display_drs_indented(ifthen(X,Y),N) :- !, tab(N), write('IF:'), nl, NN is N+2, display_drs_indented(X,NN), tab(N), write('THEN:'), nl, display_drs_indented(Y,NN). display_drs_indented(gen(X,Y),N) :- !, tab(N), write('IF:'), nl, NN is N+2, display_drs_indented(X,NN), tab(N), write('THEN:'), nl, display_drs_indented(Y,NN). display_drs_indented(gen1(X,Y),N) :- !, tab(N), write('IF:'), nl, NN is N+2, display_drs_indented(X,NN), tab(N), write('THEN:'), nl, display_drs_indented(Y,NN). display_drs_indented(dist(X,Y),N) :- !, display_drs_indented(X,N), X = drs(_,Con), find_dist(Con,I), NN is N + 2, nl, tab(N), write('For each x in '), write(I), nl, display_drs_dist(Y,I,NN). display_drs_indented(abs(X,Y,Z),N) :- !, find_dist(X,I), NN is N + 2, nl, tab(N), write('For each x in '), write(I), nl, display_drs_dist(Y,I,NN), display_drs_dist(Z,I,NN). display_drs_indented(neg(X),N) :- !, tab(N), write('NOT:'), nl, NN is N+2, display_drs_indented(X,NN). display_drs_indented(query(X),N) :- !, tab(N), write('QUERY:'), nl, NN is N+2, display_drs_indented(X,NN). display_drs_indented(drs(X,Y),N) :- !, reverse(X,RX), display_drs_indented(RX,N), reverse(Y,RY), display_drs_indented(RY,N). display_drs_indented([H|T],N) :- !, display_drs_indented(H,N), display_drs_indented(T,N). display_drs_indented([],_) :- !. display_drs_indented(Cond,N) :- (Cond = det(_,_), display_det(Cond,N); tab(N), write(Cond), nl). display_det(det(X,Y),N) :- Y = type:card, Y = amt:Amt, X =.. [_,Var], tab(N), write('|'), write(Var),write('|'),write('='), write(Amt),nl. /* find_dist(+List,?Var) looks through List until it find the variable which is the referent for the distributed plural. */ find_dist([H|_],I) :- H = det(Head,_), Head =.. [_,I]. find_dist([H|T],I) :- \+ H = det(_,_), find_dist(T,I). /* display_dr_dist(+DRS,+Var,+N) displays the DRS for distributive DRSs */ display_drs_dist(drs(U,Con),Var,N) :- display_drs_indented(U,N), display_dist(Con,Var,N). display_dist([],_,_). display_dist([H|T],Var,N) :- H =.. ListH, mem(Var, ListH), display_pred(ListH,Var,NewListH), NLH =.. NewListH, tab(N), write(NLH), nl, display_dist(T,Var,N). display_dist([H|T],Var,N) :- H =.. ListH, \+ mem(Var,ListH), display_drs_indented(H,N), display_dist(T,Var,N). display_pred([H|T],Var,[x|T]) :- H == Var. display_pred([H],Var,[x]) :- H == Var. display_pred([H|T],Var,NewList) :- \+ H == Var, display_pred(T,Var, NL), append([H],NL,NewList). /* mem(+Element, +List) works like member except it does not instantiate Element if it is a variable. */ mem(Var, [H|_]) :- Var == H. mem(Var, [H|T]) :- \+ Var == H, mem(Var,T). /*------------------------------------------------------------------* DRS-BUILDER *------------------------------------------------------------------*/ /* Modeled on that of Johnson and Klein (CSLI Report 86-63) */ /* with many minor changes. */ /*************************************** * Lexicon and lexical insertion rules * ***************************************/ /* Nouns have three qualities: gender, number, and type of their own; They * also have the qualities of their determiners--if they have one. Type is * have the noun is considered in context of the determiner and any floating * quantifiers--if there are any--i.e., whether the noun is considered * part of a collective group or individually. * Proper nouns * Type will always be individual (ind) for proper nouns. */ n(N) --> [Form], { proper_noun(Form,sg,ind,Case, lambda(I,Semantics)), append(Semantics,Con,NewCon), N = syn: (index:I :: class:proper :: case:Case :: number:sg) :: sem: (in: [drs(U,Con)|Super] :: out: [drs([I|U],NewCon)|Super]) }. proper_noun(pedro,sg,ind,Case, lambda(X, [named(X,'Pedro'), gender(X,m),number(X,sg),case(X,Case)])). proper_noun(chiquita,sg,ind,Case, lambda(X, [named(X,'Chiquita'), gender(X,f),number(X,sg),case(X,Case)])). proper_noun(maria,sg,ind,Case, lambda(X, [named(X,'Maria'), gender(X,f),number(X,sg),case(X,Case)])). proper_noun(blackie,sg,ind,Case, lambda(X, [named(X,'Blackie'), gender(X,n),number(X,sg),case(X,Case)])). proper_noun(chester,sg,ind,Case, lambda(X, [named(X,'Chester'), gender(X,n),number(X,sg),case(X,Case)])). proper_noun(kitty,sg,ind,Case, lambda(X, [named(X,'Kitty'), gender(X,n),number(X,sg),case(X,Case)])). proper_noun(bill,sg,ind,Case, lambda(X, [named(X,'Bill'), gender(X,m),number(X,sg),case(X,Case)])). proper_noun(john,sg,ind,Case, lambda(X, [named(X,'Bob'), gender(X,m),number(X,sg),case(X,Case)])). proper_noun(mark,sg,ind,Case, lambda(X, [named(X,'Dave'), gender(X,m),number(X,sg),case(X,Case)])). proper_noun(heather,sg,ind,Case, lambda(X, [named(X,'Heather'), gender(X,f),number(X,sg),case(X,Case)])). proper_noun(sage,sg,ind,Case, lambda(X, [named(X,'Sage'), gender(X,f),number(X,sg),case(X,Case)])). proper_noun(mary,sg,ind,Case, lambda(X, [named(X,'Dana'), gender(X,f),number(X,sg),case(X,Case)])). proper_noun(emma,sg,ind,Case, lambda(X, [named(X,'Emma'), gender(X,f),number(X,sg),case(X,Case)])). proper_noun(fox,sg,ind,Case, lambda(X, [named(X,'Fox'), gender(X,m),number(X,sg),case(X,Case)])). /* * Common nouns */ n(N) --> [Form], { common_noun(Form,Num,Type,Case,lambda(I,Semantics)), append(Semantics,Con,NewC), N = syn:det:Det, (nonvar(Det), Semantics = [Head|_], Det = syn:number:Num, Det = syn:type:Type, Det = syn:name:Form, NDet = [det, Head, Det], NewDet =..NDet, append([NewDet],NewC,NewCon); NewC = NewCon), N = syn: (index:I :: case:Case :: class:common :: number:Num :: type:Type) :: sem: (in: [drs(U,Con)|Super] :: out: [drs([I|U],NewCon)|Super]) }. common_noun(bandersnatch,sg,ind,Case, lambda(X,[bandersnatch(X), case(X,Case),gender(X,n),number(X,sg)] )). common_noun(bandersnatches,pl,col,Case, lambda(X,[bandersnatches(X), case(X,Case),gender(X,n),number(X,pl)] )). common_noun(bandersnatches,pl,ind,Case, lambda(X,[bandersnatch(X), case(X,Case),gender(X,n),number(X,pl)] )). common_noun(boojum,sg,ind,Case, lambda(X,[boojum(X), case(X,Case),gender(X,n),number(X,sg)] )). common_noun(boojums,pl,col,Case, lambda(X,[boojums(X), case(X,Case),gender(X,n),number(X,pl)] )). common_noun(boojums,pl,ind, Case, lambda(X,[boojum(X), case(X,Case),gender(X,n),number(X,pl)] )). common_noun(cat,sg,ind,Case, lambda(X,[cat(X), case(X,Case),gender(X,n),number(X,sg)] )). common_noun(cats,pl,col,Case, lambda(X,[cats(X), case(X,Case),gender(X,n),number(X,pl)] )). common_noun(cats,pl,ind,Case, lambda(X,[cat(X), case(X,Case),gender(X,n),number(X,pl)] )). common_noun(dog,sg,ind,Case, lambda(X,[dog(X), case(X,Case),gender(X,n),number(X,sg)] )). common_noun(dogs,pl,col,Case, lambda(X,[dogs(X), case(X,Case),gender(X,n),number(X,pl)] )). common_noun(dogs,pl,ind,Case, lambda(X,[dog(X), case(X,Case),gender(X,n),number(X,pl)] )). common_noun(man,sg,ind,Case, lambda(X,[man(X), case(X,Case),gender(X,m),number(X,sg)] )). common_noun(men,pl,col,Case, lambda(X,[men(X), case(X,Case),gender(X,m),number(X,pl)] )). common_noun(men,pl,ind,Case, lambda(X,[man(X), case(X,Case),gender(X,m),number(X,pl)] )). common_noun(woman,sg,ind,Case, lambda(X,[woman(X), case(X,Case),gender(X,f),number(X,sg)] )). common_noun(women,pl,col,Case, lambda(X,[women(X), case(X,Case),gender(X,f),number(X,pl)] )). common_noun(women,pl,ind,Case, lambda(X,[woman(X), case(X,Case),gender(X,f),number(X,pl)] )). common_noun(donkey,sg,ind,Case, lambda(X,[donkey(X), case(X,Case),gender(X,n),number(X,sg)] )). common_noun(donkeys,pl,col,Case, lambda(X,[donkeys(X), case(X,Case),gender(X,n),number(X,pl)] )). common_noun(donkeys,pl,ind,Case, lambda(X,[donkey(X), case(X,Case),gender(X,n),number(X,pl)] )). common_noun(farmer,sg,ind,Case, lambda(X,[farmer(X), case(X,Case),gender(X,m),number(X,sg)] )). common_noun(farmers,pl,col,Case, lambda(X,[farmers(X), case(X,Case),gender(X,m),number(X,pl)] )). common_noun(farmers,pl,ind,Case, lambda(X,[farmer(X), case(X,Case),gender(X,m),number(X,pl)] )). common_noun(knight,sg,ind,Case, lambda(X,[knight(X), case(X,Case),gender(X,m),number(X,sg)] )). common_noun(knights,pl,col,Case, lambda(X,[knights(X), case(X,Case),gender(X,m),number(X,pl)] )). common_noun(knights,pl,ind,Case, lambda(X,[knight(X), case(X,Case),gender(X,m),number(X,pl)] )). common_noun(lady,sg,ind,Case, lambda(X,[lady(X), case(X,Case),gender(X,f),number(X,sg)] )). common_noun(ladies,pl,col,Case, lambda(X,[ladies(X), case(X,Case),gender(X,f),number(X,pl)] )). common_noun(ladies,pl,ind,Case, lambda(X,[lady(X), case(X,Case),gender(X,f),number(X,pl)] )). common_noun(knave,sg,ind,Case, lambda(X,[knave(X), case(X,Case),gender(X,m),number(X,sg)] )). common_noun(knaves,pl,col,Case, lambda(X,[knaves(X), case(X,Case),gender(X,m),number(X,pl)] )). common_noun(knaves,pl,ind,Case, lambda(X,[knave(X), case(X,Case),gender(X,m),number(X,pl)] )). common_noun(pizza,sg,ind,Case, lambda(X,[pizza(X), case(X,Case),gender(X,n),number(X,sg)] )). common_noun(pizzas,pl,col,Case, lambda(X,[pizzas(X), case(X,Case),gender(X,n),number(X,pl)] )). common_noun(pizzas,pl,ind,Case, lambda(X,[pizza(X), case(X,Case),gender(X,n),number(X,pl)] )). /* * Adjectives */ adj(Adj) --> [Form], { adjective(Form,lambda(I,Semantics)), append(Semantics,Con,NewCon), Adj = syn: (index:I) :: sem: (in: [drs(U,Con)|Super] :: out: [drs(U,NewCon)|Super]) }. adjective(big, lambda(X,[big(X)])). adjective(green, lambda(X,[green(X)])). adjective(rich, lambda(X,[rich(X)])). adjective(old, lambda(X,[old(X)])). adjective(happy, lambda(X,[happy(X)])). /* * Transitive verbs */ v(V) --> [Form], { transitive_verb(Form,Num,lambda(A1,A2,Semantics)), append(Semantics,Con,NewCon), V = syn: (class:transitive :: number:Num :: arg1:A1 :: arg2:A2) :: sem: (in: [drs(U,Con)|Super] :: out: [drs(U,NewCon)|Super]) }. transitive_verb(see,pl, lambda(X,Y,[sees(X,Y)])). transitive_verb(sees,sg, lambda(X,Y,[sees(X,Y)])). transitive_verb(love,pl, lambda(X,Y,[loves(X,Y)])). transitive_verb(loves,sg, lambda(X,Y,[loves(X,Y)])). transitive_verb(own,pl, lambda(X,Y,[owns(X,Y)])). transitive_verb(owns,sg, lambda(X,Y,[owns(X,Y)])). transitive_verb(have,pl, lambda(X,Y,[has(X,Y)])). transitive_verb(has,sg, lambda(X,Y,[has(X,Y)])). transitive_verb(beat,pl, lambda(X,Y,[beats(X,Y)])). transitive_verb(beats,sg, lambda(X,Y,[beats(X,Y)])). transitive_verb(feed,pl, lambda(X,Y,[feeds(X,Y)])). transitive_verb(feeds,sg, lambda(X,Y,[feeds(X,Y)])). transitive_verb(admire,pl, lambda(X,Y,[admires(X,Y)])). transitive_verb(admires,sg,lambda(X,Y,[admires(X,Y)])). transitive_verb(fight,pl, lambda(X,Y,[fights(X,Y)])). transitive_verb(fights,sg, lambda(X,Y,[fights(X,Y)])). transitive_verb(insult,pl, lambda(X,Y,[insults(X,Y)])). transitive_verb(insults,sg,lambda(X,Y,[insults(X,Y)])). transitive_verb(chase,pl, lambda(X,Y,[chases(X,Y)])). transitive_verb(chases,sg, lambda(X,Y,[chases(X,Y)])). transitive_verb(buy,pl, lambda(X,Y,[buys(X,Y)])). transitive_verb(buys,sg, lambda(X,Y,[buys(X,Y)])). transitive_verb(eat,pl, lambda(X,Y,[eats(X,Y)])). transitive_verb(eats,sg, lambda(X,Y,[eats(X,Y)])). v(V) --> [Form], { intransitive_verb(Form,Num,lambda(Arg,Semantics)), append(Semantics,Con,NewCon), V = syn : (class:intransitive :: number:Num :: arg1:Arg) :: sem : (in: [drs(U,Con)|Super] :: out: [drs(U,NewCon)|Super]) }. intransitive_verb(bark,pl, lambda(X,[barks(X)])). intransitive_verb(barks,sg, lambda(X,[barks(X)])). intransitive_verb(eat,pl, lambda(X,[eats(X)])). intransitive_verb(eats,sg, lambda(X,[eats(X)])). intransitive_verb(bray,pl, lambda(X,[brays(X)])). intransitive_verb(brays,sg, lambda(X,[brays(X)])). /* * Determiners, each with its own semantics */ det(Det) --> ([a] ; [an]), { Det = sem:in:A, Det = sem:res:in:A, /* Pass sem:in to res */ Det = sem:res:out:B, Det = sem:scope:in:B, /* Pass res:out to scope:in */ Det = sem:scope:out:C, Det = sem:out:C }. /* Whatever comes out of scope is the result. */ det(Det) --> [one], { Det = sem:in:A, Det = sem:res:in:A, /* Pass sem:in to res */ Det = sem:res:out:B, Det = sem:scope:in:B, /* Pass res:out to scope:in */ Det = sem:scope:out:C, Det = sem:out:C }. /* Whatever comes out of scope is the result. */ det(Det) --> [two], { Det = syn: (number:pl :: type:col :: det: (type:card :: amt:2 )), Det = sem:in:A, Det = sem:res:in:A, /* Pass sem:in to res */ Det = sem:res:out:B, Det = sem:scope:in:B, /* Pass res:out to scope:in */ Det = sem:scope:out:C, Det = sem:out:C }. /* Whatever comes out of scope is the result. */ det(Det) --> [two], { Det = syn: (number:pl :: type:ind :: det: (type:card :: amt:2)), Det = sem:in:A, Det = sem:res:in:[drs([],[])|A], Det = sem:res:out:B, Det = sem:scope:in:[drs([],[])|B], Det = sem:scope:out:[Scope,Res,drs(U,Con)|Super], Det = sem:out:[drs(U,[dist(Res,Scope)|Con])|Super] }. det(Det) --> [three], { Det = syn: (number:pl :: type:col :: det: (type:card :: amt:3 )), Det = sem:in:A, Det = sem:res:in:A, /* Pass sem:in to res */ Det = sem:res:out:B, Det = sem:scope:in:B, /* Pass res:out to scope:in */ Det = sem:scope:out:C, Det = sem:out:C }. /* Whatever comes out of scope is the result. */ det(Det) --> [three], { Det = syn: (number:pl :: type:ind :: det: (type:card :: amt:3)), Det = sem:in:A, Det = sem:res:in:[drs([],[])|A], Det = sem:res:out:B, Det = sem:scope:in:[drs([],[])|B], Det = sem:scope:out:[Scope,Res,drs(U,Con)|Super], Det = sem:out:[drs(U,[dist(Res,Scope)|Con])|Super] }. det(Det) --> [four], { Det = syn: (number:pl :: type:col :: det: (type:card :: amt:4 )), Det = sem:in:A, Det = sem:res:in:A, /* Pass sem:in to res */ Det = sem:res:out:B, Det = sem:scope:in:B, /* Pass res:out to scope:in */ Det = sem:scope:out:C, Det = sem:out:C }. /* Whatever comes out of scope is the result. */ det(Det) --> [four], { Det = syn: (number:pl :: type:ind :: det: (type:card :: amt:4)), Det = sem:in:A, Det = sem:res:in:[drs([],[])|A], Det = sem:res:out:B, Det = sem:scope:in:[drs([],[])|B], Det = sem:scope:out:[Scope,Res,drs(U,Con)|Super], Det = sem:out:[drs(U,[dist(Res,Scope)|Con])|Super] }. det(Det) --> [five], { Det = syn: (number:pl :: type:col :: det: (type:card :: amt:5 )), Det = sem:in:A, Det = sem:res:in:A, /* Pass sem:in to res */ Det = sem:res:out:B, Det = sem:scope:in:B, /* Pass res:out to scope:in */ Det = sem:scope:out:C, Det = sem:out:C }. /* Whatever comes out of scope is the result. */ det(Det) --> [five], { Det = syn: (number:pl :: type:ind :: det: (type:card :: amt:5)), Det = sem:in:A, Det = sem:res:in:[drs([],[])|A], Det = sem:res:out:B, Det = sem:scope:in:[drs([],[])|B], Det = sem:scope:out:[Scope,Res,drs(U,Con)|Super], Det = sem:out:[drs(U,[dist(Res,Scope)|Con])|Super] }. det(Det) --> [six], { Det = syn: (number:pl :: type:col :: det: (type:card :: amt:6 )), Det = sem:in:A, Det = sem:res:in:A, /* Pass sem:in to res */ Det = sem:res:out:B, Det = sem:scope:in:B, /* Pass res:out to scope:in */ Det = sem:scope:out:C, Det = sem:out:C }. /* Whatever comes out of scope is the result. */ det(Det) --> [six], { Det = syn: (number:pl :: type:ind :: det: (type:card :: amt:6)), Det = sem:in:A, Det = sem:res:in:[drs([],[])|A], Det = sem:res:out:B, Det = sem:scope:in:[drs([],[])|B], Det = sem:scope:out:[Scope,Res,drs(U,Con)|Super], Det = sem:out:[drs(U,[dist(Res,Scope)|Con])|Super] }. det(Det) --> [seven], { Det = syn: (number:pl :: type:col :: det: (type:card :: amt:7 )), Det = sem:in:A, Det = sem:res:in:A, /* Pass sem:in to res */ Det = sem:res:out:B, Det = sem:scope:in:B, /* Pass res:out to scope:in */ Det = sem:scope:out:C, Det = sem:out:C }. /* Whatever comes out of scope is the result. */ det(Det) --> [seven], { Det = syn: (number:pl :: type:ind :: det: (type:card :: amt:7)), Det = sem:in:A, Det = sem:res:in:[drs([],[])|A], Det = sem:res:out:B, Det = sem:scope:in:[drs([],[])|B], Det = sem:scope:out:[Scope,Res,drs(U,Con)|Super], Det = sem:out:[drs(U,[dist(Res,Scope)|Con])|Super] }. det(Det) --> [eight], { Det = syn: (number:pl :: type:col :: det: (type:card :: amt:8 )), Det = sem:in:A, Det = sem:res:in:A, /* Pass sem:in to res */ Det = sem:res:out:B, Det = sem:scope:in:B, /* Pass res:out to scope:in */ Det = sem:scope:out:C, Det = sem:out:C }. /* Whatever comes out of scope is the result. */ det(Det) --> [eight], { Det = syn: (number:pl :: type:ind :: det: (type:card :: amt:8)), Det = sem:in:A, Det = sem:res:in:[drs([],[])|A], Det = sem:res:out:B, Det = sem:scope:in:[drs([],[])|B], Det = sem:scope:out:[Scope,Res,drs(U,Con)|Super], Det = sem:out:[drs(U,[dist(Res,Scope)|Con])|Super] }. det(Det) --> [nine], { Det = syn: (number:pl :: type:col :: det: (type:card :: amt:9 )), Det = sem:in:A, Det = sem:res:in:A, /* Pass sem:in to res */ Det = sem:res:out:B, Det = sem:scope:in:B, /* Pass res:out to scope:in */ Det = sem:scope:out:C, Det = sem:out:C }. /* Whatever comes out of scope is the result. */ det(Det) --> [nine], { Det = syn: (number:pl :: type:ind :: det: (type:card :: amt:9)), Det = sem:in:A, Det = sem:res:in:[drs([],[])|A], Det = sem:res:out:B, Det = sem:scope:in:[drs([],[])|B], Det = sem:scope:out:[Scope,Res,drs(U,Con)|Super], Det = sem:out:[drs(U,[dist(Res,Scope)|Con])|Super] }. det(Det) --> [every], { Det = sem:in:A, Det = sem:res:in:[drs([],[])|A], Det = sem:res:out:B, Det = sem:scope:in:[drs([],[])|B], Det = sem:scope:out:[Scope,Res,drs(U,Con)|Super], Det = sem:out:[drs(U,[ifthen(Res,Scope)|Con])|Super] }. det(Det) --> [no], { Det = sem:in:A, Det = sem:res:in:[drs([],[])|A], Det = sem:res:out:B, Det = sem:scope:in:B, Det = sem:scope:out:[DRS,drs(U,Con)|Super], Det = sem:out:[drs(U,[neg(DRS)|Con])|Super] }. det(Det) --> [not,every], { Det = sem:in:A, Det = sem:res:in:[drs([],[])|A], Det = sem:res:out:B, Det = sem:scope:in:[drs([],[])|B], Det = sem:scope:out:[Scope,Res,drs(U,Con)|Super], Det = sem:out:[drs(U,[neg(drs([],[ifthen(Res,Scope)]))|Con])|Super] }. /************************** * Phrase structure rules * **************************/ /* * n1: common noun preceded by zero or more adjectives */ n1(N1,H,H) --> n(N1). n1(N1A,H1,H2) --> { N1A = syn:A, Adj = syn:A, N1B = syn:A, /* Indexes are in syn, not sem */ N1A = sem:in:B, N1B = sem:in:B, N1B = sem:out:C, Adj = sem:in:C, Adj = sem:out:D, N1A = sem:out:D }, adj(Adj), n1(N1B,H1,H2). /* * n2: n1 optionally followed by relative clause */ n2(N2,H1,H2) --> n1(N2,H1,H2). n2(N2,H1,H3) --> { N2 = syn:Syn, N1 = syn:Syn, RC = syn:Syn, /* pass index to RC */ N2 = sem:in:S1, N1 = sem: (in:S1 :: out:S2), RC = sem: (in:S2 :: out:S3), N2 = sem:out:S3 }, n1(N1,H1,H2), relcl(RC,H2,H3). /* Noun phrase ending with relative clause */ /* * noun phrase */ np(NP,H,H) --> { N = syn:class:proper, N = syn:A, NP = syn:A, /* NP gets its syntax from N */ N = sem:B, NP = sem:res:B, /* NP gets its res from N */ NP = sem:in:C, NP = sem:res:in:C, /* Pass sem:in through res */ NP = sem:res:out:D, NP = sem:scope:in:D, /* then through scope */ NP = sem:scope:out:E, NP = sem:out:E }, n(N). /* Proper names do not take determiners */ np(NP,H1,H2) --> { N2 = syn:class:common, Det = syn:D, N2 = syn:D, NP = syn:D, /* NP gets its syntax from N */ N2 = syn:C, NP = syn:C, Det = sem:A, NP = sem:A, /* NP gets its semantics from Det */ N2 = sem:B, Det = sem:res:B }, /* Det gets its res from N */ det(Det), n2(N2,H1,H2). np(NP,H,H) --> { N = syn:class:common, N = syn:number:pl, N = syn:type:ind, N = syn:A, NP = syn:A, /* NP gets its syntax from N */ N = sem:B, NP = sem:res:B, /* NP gets its res from N */ NP = sem:in:C, NP = sem:res:in:C, /* Pass sem:in through res */ NP = sem:res:out:D, NP = sem:scope:in:D, /* then through scope */ NP = sem:scope:out:E, NP = sem:out:E }, n(N). /* common plural nouns do not necessairly take determiners. These nouns are assumed to be asserting some generic statement about the common noun. */ /* * trace (gap) from moved relative pronoun */ np(NP,[rel(Index)|Rest],Rest) --> [], /* Trace of moved rel pronoun */ { NP = sem:in:B, /* This kind of NP has no semantics and hence no res */ NP = sem:scope:in:B, NP = sem:scope:out:C, NP = sem:out:C, NP = syn:index:Index }. /* * anaphoric pronouns, with anaphora resolving routine */ np(NP,H,H) --> [the], [N], { common_noun(N,_,_,_,L), NP = sem:in:DrsList, L = lambda(_,[Noun,_,Gen,Num]), member(drs(U,Con),DrsList), member(Index,U), Gen =.. [H1,_,C], NGen =.. [H1,Index1,C], member(NGen,Con), Index == Index1, Num =.. [H2,_,C1], NNum =.. [H2,Index2,C1], member(NNum,Con), Index == Index2, Noun =..[H3,_], NNoun =..[H3,Index3], member(NNoun,Con), Index == Index3, NP = syn:index:Index, NP = sem:scope:in:DrsList, NP = sem:scope:out:DrsOut, NP = sem:out:DrsOut }. /* Treats the as an anaphor */ np(NP,H,H) --> ([he]), { NP = sem:in:DrsList, member(drs(U,Con),DrsList), member(Index,U), member(gender(Index2,m),Con), Index == Index2, member(case(Index3,nom),Con), Index == Index3, member(number(Index4,sg),Con), Index == Index4, NP = syn:index:Index, NP = sem:scope:in:DrsList, NP = sem:scope:out:DrsOut, NP = sem:out:DrsOut }. np(NP,H,H) --> ([him]), { NP = sem:in:DrsList, (member(drs(U,Con),DrsList), member(Index,U), member(gender(Index2,m),Con), Index == Index2, member(case(Index3,acc),Con), Index == Index3, member(number(Index4,sg),Con), Index == Index4; member(drs([],[dist(_,drs(U,Con))]),DrsList), member(Index,U), member(gender(Index2,m),Con), Index == Index2, member(case(Index3,acc),Con), Index == Index3, member(number(Index4,sg),Con), Index == Index4), NP = syn:index:Index, NP = sem:scope:in:DrsList, NP = sem:scope:out:DrsOut, NP = sem:out:DrsOut }. np(NP,H,H) --> ([she]), { NP = sem:in:DrsList, member(drs(U,Con),DrsList), member(Index,U), member(gender(Index2,f),Con), Index == Index2, member(case(Index3,nom),Con), Index == Index3, member(number(Index4,sg),Con), Index == Index4, NP = syn:index:Index, NP = sem:scope:in:DrsList, NP = sem:scope:out:DrsOut, NP = sem:out:DrsOut }. np(NP,H,H) --> ([her]), { NP = sem:in:DrsList, (member(drs(U,Con),DrsList), member(Index,U), member(gender(Index2,f),Con), Index == Index2, member(case(Index3,acc),Con), Index == Index3, member(number(Index4,sg),Con), Index == Index4; member(drs([],[dist(_,drs(U,Con))]),DrsList), member(Index,U), member(gender(Index2,f),Con), Index == Index2, member(case(Index3,acc),Con), Index == Index3, member(number(Index4,sg),Con), Index == Index4), NP = syn:index:Index, NP = sem:scope:in:DrsList, NP = sem:scope:out:DrsOut, NP = sem:out:DrsOut }. np(NP,H,H) --> ([they]), { NP = sem:in:DrsList, member(drs(U,Con),DrsList), member(Index,U), member(number(Index2,pl),Con), Index == Index2, member(case(Index3,nom),Con), Index == Index3, NP = syn:index:Index, NP = sem:scope:in:DrsList, NP = sem:scope:out:DrsOut, NP = sem:out:DrsOut }. np(NP,H,H) --> ([they]), { NP = sem:in:DrsList, member(drs([],[dist(drs(U,Con),Con2)]),DrsList), member(Index,U), member(Index,U), member(number(Index2,pl),Con), Index == Index2, member(case(Index3,nom),Con), Index == Index3, NP = syn:index:Index, NP = sem:in:A, NP = sem:scope:in:[drs([],[])|A], NP = sem:scope:out:[Scope,drs(NU,NCon)|Super], NP = sem:out:[drs(NU,[abs(Con,Con2,Scope)|NCon])|Super]}. np(NP,H,H) --> ([them]), { NP = sem:in:DrsList, member(drs(U,Con),DrsList), member(Index,U), member(number(Index2,pl),Con), Index == Index2, member(case(Index3,acc),Con), Index == Index3, NP = syn:index:Index, NP = sem:scope:in:DrsList, NP = sem:scope:out:DrsOut, NP = sem:out:DrsOut }. np(NP,H,H) --> ([them]), { NP = sem:in:DrsList, member(drs([],[dist(_,drs(U,Con))]),DrsList), member(Index,U), member(case(Index3,acc),Con), Index == Index3, NP = syn:index:Index, NP = sem:scope:in:DrsList, NP = sem:scope:out:DrsOut, NP = sem:out:DrsOut }. np(NP,H,H) --> [it], { NP = sem:in:DrsList, (member(drs(U,Con),DrsList); member(drs([],[dist(_,drs(U,Con))]),DrsList)), member(Index,U), member(gender(Index2,n),Con), Index == Index2, member(number(Index3,sg),Con), Index == Index3, NP = syn:index:Index, NP = sem:scope:in:DrsList, NP = sem:scope:out:DrsOut, NP = sem:out:DrsOut }. /* * verb phrase */ vp(VP,H1,H2) --> { V = syn:class:transitive, V = syn:D, VP = syn:D, /* VP gets its syntax from V */ NP = sem:A, VP = sem:A, /* VP gets its semantics from NP */ NP = syn:case:acc, NP = syn:index:C, VP = syn:type:Type, NP = syn:type:Type, VP = syn:arg2:C, V = sem:B, NP = sem:scope:B}, /* NP gets scope from V sem */ v(V), np(NP,H1,H2). vp(VP,H,H) --> v(VP), { VP = syn:class:intransitive }. vp(VP,H1,H2) --> /* Compound verb phrase */ { VP = sem:in:A, /* 'Both' is required */ VP1 = sem:in:A, /* to avoid left recursion */ VP1 = sem:out:B, /* with this simple parser */ VP2 = sem:in:B, VP2 = sem:out:C, VP = sem:out:C, VP1 = syn:arg1:D, VP2 = syn:arg1:D, VP = syn:arg1:D }, [both], vp(VP1,H1,H1A), [and], vp(VP2,H1A,H2). /* * relative clause */ relcl(RC,H1,H2) --> { RC = syn:index:Index, RC = sem:Sem, S = sem:Sem }, ([who];[whom];[which];[that]), s(S,[rel(Index)|H1],H2). /* * simple sentences */ s(S,H1,H2) --> { S = sem:in: A, N = syn:class:common, N = syn:number:pl, N = syn:index:I, N = syn:type:ind, N = syn:case:nom, N = syn:C, NP = syn:C, /* NP gets its syntax from N */ N = sem:B, NP = sem:res:B, /* NP gets its res from N */ NP = sem:in:A, NP = sem:res:in:[drs([],[])|A], /* Pass sem:in through res */ NP = sem:res:out:D, NP = sem:scope:in:[drs([],[])|D], /* then through scope */ NP = sem:scope:E, VP = sem:E, VP = syn:number:pl, VP = syn:type:ind, VP = syn:arg1:I, NP = sem:scope:out:[VPDRS,NPDRS,drs(U,Con)|Super], S = sem:out:[drs(U,[gen1(NPDRS,VPDRS)|Con])|Super] }, n(N), vp(VP,H1,H2). /* Generic sentence of the form: bare plural verb bare plural */ s(S,[],[]) --> { S = sem:in: A, N = syn:class:common, N = syn:number:pl, N = syn:type:ind, N = syn:case:nom, N = syn:C, NP = syn:C, /* NP gets its syntax from N */ N = sem:B, NP = sem:res:B, /* NP gets its res from N */ NP = sem:in:A, NP = sem:res:in:[drs([],[])|A], /* Pass sem:in through res */ NP = sem:res:out:D, NP = sem:scope:in:[drs([],[])|D], /* then through scope */ NP = sem:scope:E, Adj = sem:E, NP = sem:scope:out:[VPDRS,NPDRS,drs(U,Con)|Super], S = sem:out:[drs(U,[gen1(NPDRS,VPDRS)|Con])|Super] }, n(N), [are], adj(Adj). /* Generic sentence of the form: bare plural are adjective */ s(S,H1,H3) --> { NP = sem:A, S = sem:A, /* Pass sem from NP to S */ VP = sem:C, NP = sem:scope:C, /* Pass VP sem to NP sem scope */ NP = syn:index:D, NP = syn:case:nom, NP = syn:number:Num, VP = syn:number:Num, VP = syn:arg1:D }, /* Pass NP index to VP arg1 */ np(NP,H1,H2), vp(VP,H2,H3). s(S,H1,H3) --> /* * Note: "does not" is given sentential scope here. * That is, "Every man does not love a woman" is * taken to mean "It is not the case that every * man loves a woman." */ { S = sem:in:A, NP = sem:in:[drs([],[])|A], VP = sem:C, NP = sem:scope:C, NP = syn:index:D, NP = syn:case:nom, NP = syn:number:Num, VP = syn:number:Num, VP = syn:arg1:D, NP = sem:out:[DRS,drs(U,Con)|Super], S = sem:out:[drs(U,[neg(DRS)|Con])|Super] }, np(NP,H1,H2), [does,not], vp(VP,H2,H3). s(S,H1,H3) --> { NP = sem:A, S = sem:A, /* Pass sem from NP to S */ VP = sem:C, NP = sem:scope:C, /* Pass VP sem to NP sem scope */ NP = syn:index:D, NP = syn:type:ind, NP = syn:case:nom, NP = syn:number:Num, VP = syn:number:Num, VP = syn:arg1:D }, /* Pass NP index to VP arg1 */ np(NP,H1,H2), [each], vp(VP,H2,H3). /* Deals with Distributive Sentences. */ s(S,H1,H2) --> { NP = sem:A, S = sem:A, /* Pass sem from NP to S */ Adj = sem:C, NP = sem:scope:C, /* Pass VP sem to NP sem scope */ NP = syn:type:ind, NP = syn:case:nom}, np(NP,H1,H2), ([each], [are]; [are], [each]), adj(Adj). s(S,H1,H2) --> { S = sem:A, NP = sem:A, NP = sem:scope:B, Adj = sem:B, NP = syn:C, /* Pass along syntax */ NP = syn:number:sg, NP = syn:case:nom, Adj = syn:C }, np(NP,H1,H2), [is], adj(Adj). s(S,H1,H2) --> { S = sem:A, NP = sem:A, NP = sem:scope:B, Adj = sem:B, NP = syn:C, /* Pass along syntax */ NP = syn:number:pl, NP = syn:case:nom, Adj = syn:C }, np(NP,H1,H2), [are], adj(Adj). s(S,H1,H2) --> { S = sem:in:A, NP = sem:in:[drs([],[])|A], NP = sem:out:[DRS,drs(U,Con)|Super], S = sem:out:[drs(U,[neg(DRS)|Con])|Super], NP = sem:scope:B, Adj = sem:B, NP = syn:C, NP = syn:number:sg, NP = syn:case:nom, Adj = syn:C }, np(NP,H1,H2), [is,not], adj(Adj). s(S,H1,H2) --> { S = sem:in:A, NP = sem:in:[drs([],[])|A], NP = sem:out:[DRS,drs(U,Con)|Super], S = sem:out:[drs(U,[neg(DRS)|Con])|Super], NP = sem:scope:B, Adj = sem:B, NP = syn:C, NP = syn:number:pl, NP = syn:case:nom, Adj = syn:C }, np(NP,H1,H2), [are,not], adj(Adj). s(S,H,H) --> { S = sem:in: A, NP1 = sem:in: [drs([],[])|A], NP1 = syn:case:nom, NP1 = syn:number:sg, NP1 = syn:class:common, NP1 = sem:out:B, NP2 = syn:case:acc, NP2 = syn:number:sg, NP2 = syn:class:common, NP2 = sem:in: [drs([],[])|B], NP2 = sem:out:[N2DRS,N1DRS,drs(U,Con)|Super], S = sem:out:[drs(U,[gen(N1DRS,N2DRS)|Con])|Super] }, [a], n(NP1), [is], [a], n(NP2). s(S,H,H) --> { S = sem:in: A, NP1 = sem:in: [drs([],[])|A], NP1 = syn:case:nom, NP1 = syn:number:pl, NP1 = syn:class:common, NP1 = syn:type:ind, NP1 = sem:out:B, NP2 = syn:case:acc, NP2 = syn:number:pl, NP2 = syn:class:common, NP2 = syn:type:ind, NP2 = sem:in: [drs([],[])|B], NP2 = sem:out:[N2DRS,N1DRS,drs(U,Con)|Super], S = sem:out:[drs(U,[gen(N1DRS,N2DRS)|Con])|Super] }, n(NP1), [are], n(NP2). s(S,H1,H3) --> { S = sem:A, NP1 = sem:A, NP2 = sem:B, NP1 = sem:scope:B, NP1 = syn:case:nom, NP1 = syn:number:sg, NP2 = syn:number:sg, NP1 = syn:index:A1, NP2 = syn:case:acc, NP2 = syn:index:A2, NP2 = sem:scope: (in: [drs(U,Con)|Super] :: out: [drs(U,[(A1=A2)|Con])|Super]) }, np(NP1,H1,H2), [is], np(NP2,H2,H3). s(S,H1,H3) --> { S = sem:in:A, NP1 = sem:in:[drs([],[])|A], NP1 = sem:out:[DRS,drs(U1,Con1)|Super1], S = sem:out:[drs(U1,[neg(DRS)|Con1])|Super1], NP2 = sem:B, NP1 = sem:scope:B, NP1 = syn:case:nom, NP1 = syn:number:sg, NP2 = syn:number:sg, NP1 = syn:index:A1, NP2 = syn:case:acc, NP2 = syn:index:A2, NP2 = sem:scope: (in: [drs(U2,Con2)|Super2] :: out: [drs(U2,[(A1=A2)|Con2])|Super2]) }, np(NP1,H1,H2), [is,not], np(NP2,H2,H3). s(S,H1,H3) --> { S = sem:A, NP1 = sem:A, NP2 = sem:B, NP1 = sem:scope:B, NP1 = syn:case:nom, NP1 = syn:number:pl, NP2 = syn:number:pl, NP1 = syn:index:A1, NP2 = syn:case:acc, NP2 = syn:index:A2, NP2 = sem:scope: (in: [drs(U,Con)|Super] :: out: [drs(U,[(A1=A2)|Con])|Super]) }, np(NP1,H1,H2), [are], np(NP2,H2,H3). s(S,H1,H3) --> { S = sem:in:A, NP1 = sem:in:[drs([],[])|A], NP1 = sem:out:[DRS,drs(U1,Con1)|Super1], S = sem:out:[drs(U1,[neg(DRS)|Con1])|Super1], NP2 = sem:B, NP1 = sem:scope:B, NP1 = syn:case:nom, NP1 = syn:number:pl, NP2 = syn:number:pl, NP1 = syn:index:A1, NP2 = syn:case:acc, NP2 = syn:index:A2, NP2 = sem:scope: (in: [drs(U2,Con2)|Super2] :: out: [drs(U2,[(A1=A2)|Con2])|Super2]) }, np(NP1,H1,H2), [are,not], np(NP2,H2,H3). /* * complex sentence */ s(S,H,H) --> { S = sem:in: A, S1 = sem:in: [drs([],[])|A], S1 = sem:out:B, S2 = sem:in: [drs([],[])|B], S2 = sem:out:[S2DRS,S1DRS,drs(U,Con)|Super], S = sem:out:[drs(U,[ifthen(S1DRS,S2DRS)|Con])|Super] }, [if], s(S1,[],[]), [then], s(S2,[],[]). /* Empty hold lists enforce Coordinate Structure Constraint */ /* * statement, i.e., top-level, non-embedded sentence */ statement(S) --> s(S,[],[]). /* * question */ question(Q) --> { Q = sem:in: A, N = syn:class:common, N = syn:number:pl, N = syn:index:I, N = syn:type:ind, N = syn:case:nom, N = syn:C, NP = syn:C, /* NP gets its syntax from N */ N = sem:B, NP = sem:res:B, /* NP gets its res from N */ NP = sem:in:A, NP = sem:res:in:[drs([],[])|A], /* Pass sem:in through res */ NP = sem:res:out:D, NP = sem:scope:in:[drs([],[])|D], /* then through scope */ NP = sem:scope:C, VP = sem:C, VP = syn:arg1:I, VP = syn:type:ind, NP = sem:scope:out:[VPDRS,NPDRS,drs(U,Con)|Super], Q= sem:out:[drs(U,[query(drs([],[gen1(NPDRS,VPDRS)]))|Con])|Super]}, [do], n(N), vp(VP,[],[]). /* Question form for generics: bareplural verb bareplural. */ question(Q) --> { Q = sem:in: A, N = syn:class:common, N = syn:number:pl, N = syn:type:ind, N = syn:case:nom, N = syn:C, NP = syn:C, /* NP gets its syntax from N */ N = sem:B, NP = sem:res:B, /* NP gets its res from N */ NP = sem:in:A, NP = sem:res:in:[drs([],[])|A], /* Pass sem:in through res */ NP = sem:res:out:D, NP = sem:scope:in:[drs([],[])|D], /* then through scope */ NP = sem:scope:C, Adj = sem:C, NP = sem:scope:out:[AdjDRS,NPDRS,drs(U,Con)|Super], Q=sem:out:[drs(U,[query(drs([],[gen1(NPDRS,AdjDRS)]))|Con])|Super]}, [do], n(N), adj(Adj). /* Question form for generics: bareplural are adjective. */ question(Q) --> { Q = sem:in:A, NP = sem:in:[drs([],[])|A], VP = sem:C, NP = sem:scope:C, NP = syn:index:D, NP = syn:case:nom, VP = syn:arg1:D, NP = sem:out:[DRS,drs(U,Con)|Super], Q = sem:out:[drs(U,[query(DRS)|Con])|Super] }, [does], np(NP,[],H2), vp(VP,H2,[]). /* The plural collective and distributive forms of a question: */ question(Q) --> { Q = sem:in:A, NP = sem:in:[drs([],[])|A], VP = sem:C, NP = sem:scope:C, NP = syn:index:D, NP = syn:case:nom, VP = syn:arg1:D, NP = sem:out:[DRS,drs(U,Con)|Super], Q = sem:out:[drs(U,[query(DRS)|Con])|Super] }, [do], np(NP,[],H2), vp(VP,H2,[]). question(Q) --> { Q = sem:in:A, NP = sem:in:[drs([],[])|A], VP = sem:C, NP = sem:scope:C, NP = syn:index:D, NP = syn:case:nom, NP = syn:type:ind, VP = syn:arg1:D, NP = sem:out:[DRS,drs(U,Con)|Super], Q = sem:out:[drs(U,[query(DRS)|Con])|Super] }, [do], np(NP,[],H2), [each], vp(VP,H2,[]). /* Handles sentences with distributive readings. */ question(Q) --> { Q = sem:in:A, NP = sem:in:[drs([],[])|A], Adj = sem:C, NP = sem:scope:C, NP = syn:case:nom, NP = syn:type:ind, NP = sem:out:[DRS,drs(U,Con)|Super], Q = sem:out:[drs(U,[query(DRS)|Con])|Super] }, [do], np(NP,[],[]),([are], [each]; [each], [are]), adj(Adj). /* Handles sentences with distributive readings. */ question(Q) --> { Q = sem:in:A, NP = sem:in:[drs([],[])|A], NP = sem:out:[DRS,drs(U,Con)|Super], Q = sem:out:[drs(U,[query(DRS)|Con])|Super], NP = sem:scope:B, Adj = sem:B, NP = syn:C, NP = syn:case:nom, Adj = syn:C }, [is], np(NP,[],[]), adj(Adj). question(Q) --> { Q = sem:in:A, NP = sem:in:[drs([],[])|A], NP = sem:out:[DRS,drs(U,Con)|Super], Q = sem:out:[drs(U,[query(DRS)|Con])|Super], NP = sem:scope:B, Adj = sem:B, NP = syn:C, NP = syn:case:nom, Adj = syn:C }, [are], np(NP,[],[]), adj(Adj). question(Q) --> { Q = sem:in: A, NP1 = sem:in: [drs([],[])|A], NP1 = syn:case:nom, NP1 = syn:number:sg, NP1 = syn:class:common, NP1 = sem:out:B, NP2 = syn:case:acc, NP2 = syn:number:sg, NP2 = syn:class:common, NP2 = sem:in: [drs([],[])|B], NP2 = sem:out:[N2DRS,N1DRS,drs(U,Con)|Super], Q=sem:out:[drs(U,[query(drs([],[gen(N1DRS,N2DRS)]))|Con])|Super]}, [is], [a], n(NP1), [a], n(NP2). question(Q) --> { Q = sem:in: A, NP1 = sem:in: [drs([],[])|A], NP1 = syn:case:nom, NP1 = syn:number:pl, NP1 = syn:class:common, NP1 = syn:type:ind, NP1 = sem:out:B, NP2 = syn:case:acc, NP2 = syn:number:pl, NP2 = syn:class:common, NP2 = syn:type:ind, NP2 = sem:in: [drs([],[])|B], NP2 = sem:out:[N2DRS,N1DRS,drs(U,Con)|Super], Q = sem:out:[drs(U,[query(drs([],[gen(N1DRS,N2DRS)]))|Con])|Super]}, [are], n(NP1), n(NP2). question(Q) --> { Q = sem:in:A, NP1 = sem:in:[drs([],[])|A], NP1 = sem:out:[DRS,drs(U1,Con1)|Super1], Q = sem:out:[drs(U1,[query(DRS)|Con1])|Super1], NP2 = sem:B, NP1 = sem:scope:B, NP1 = syn:case:nom, NP1 = syn:index:A1, NP2 = syn:case:acc, NP2 = syn:index:A2, NP2 = sem:scope: (in: [drs(U2,Con2)|Super2] :: out: [drs(U2,[(A1=A2)|Con2])|Super2]) }, [is], np(NP1,[],[]), np(NP2,[],[]). question(Q) --> { Q = sem:in:A, NP1 = sem:in:[drs([],[])|A], NP1 = sem:out:[DRS,drs(U1,Con1)|Super1], Q = sem:out:[drs(U1,[query(DRS)|Con1])|Super1], NP2 = sem:B, NP1 = sem:scope:B, NP1 = syn:case:nom, NP1 = syn:index:A1, NP2 = syn:case:acc, NP2 = syn:index:A2, NP2 = sem:scope: (in: [drs(U2,Con2)|Super2] :: out: [drs(U2,[(A1=A2)|Con2])|Super2]) }, [are], np(NP1,[],[]), np(NP2,[],[]). /* * Discourse * [a,discourse,is,a,series,of,consecutive,sentences, * separated,by,endpuncts,like,this,'.',note,that,an, * endpunct,is,required,after,the,final,sentence,'!'] */ discourse(D1) --> { D1 = sem:in:A, S = sem:in:A, S = sem:out:B, S = syn:D, D1 = syn:D, D2 = sem:in:B, D2 = sem:out:C, D1 = sem:out:C }, ( statement(S) ; question(S) ), endpunct, {!}, discourse(D2). discourse(D) --> [], { D = sem:in:A, D = sem:out:A }. /* * endpunct (sentence terminator) */ endpunct --> ['.'] ; ['?'] ; ['!']. /*------------------------------------------------------------------* DRS-TO-PROLOG TRANSLATOR *------------------------------------------------------------------*/ /* * prologize_whole_drs(DRS) * processes the top-level DRS by translating into Prolog * and asserting or querying the appropriate facts or rules. */ prologize_whole_drs(DRS) :- clean_up(DRS,drs(U,Con)), write('--------------------------------------------------'),nl, write('Cleaned-up (simplified) DRS:'),nl, write('--------------------------------------------------'),nl, display_drs(drs(U,Con)), diff(Con,NewC1), reverse(NewC1,NewCon), skolemize(U,[]), write('--------------------------------------------------'),nl, prologize_recursively_list(NewCon,NCon), (NCon = [NC|[]]; NCon = NC), assert_or_process_list(NCon), abolish(is_dist/1). /* * prologize_recursively(Condition,Goal) * prologize_recursively_list(ConditionList,GoalList) * These predicates turn DRS conditions into Prolog goals. * The bulk of the work of the implementation is done here. */ prologize_recursively_list([],[]). prologize_recursively_list([H|T],NewHT) :- H = det(_,_), (T = [HT|[]]; T = HT), prologize_recursively_list(HT,NewHT). prologize_recursively_list([H|T],NewHT) :- H = abs(_,_), (T = [HT|[]]; T = HT), prologize_recursively_list(HT,NewHT). prologize_recursively_list([H|T],[NewH|NewT]) :- prologize_recursively(H,NewH), prologize_recursively_list(T,NewT). prologize_recursively(query(DRS),query(Goal)) :- !, assertz(is_query), clean_up(DRS,drs(_,Con)), diff(Con,NewCon), prologize_recursively_list(NewCon,NCon), reverse(NCon,NC), list_conj(NC,Goal), abolish(is_query/0). prologize_recursively(ifthen(DRSA,DRSC),(GoalC ::- GoalA)) :- !, clean_up(DRSA,drs(UA,ConA)), clean_up(DRSC,drs(UC,ConC)), diff(ConA,NewConA), diff(ConC,NewConC), skolemize(UC,UA), prologize_recursively_list(NewConA,NConA), prologize_recursively_list(NewConC,NConC), list_conj(NConA,GoalA), list_conj(NConC,GoalC). prologize_recursively(gen(DRSA,DRSC),(GoalC ::- GoalA)) :- !, clean_up(DRSA,drs(_,ConA)), clean_up(DRSC,drs(_,ConC)), prologize_recursively_list(ConA,NConA), prologize_recursively_list(ConC,NConC), list_conj(NConA,GoalA), list_conj(NConC,GoalC). prologize_recursively(gen1(DRSA,DRSC),(GoalC ::- GoalA)) :- !, clean_up(DRSA,drs(_,ConA)), clean_up(DRSC,drs(_,ConC)), diff(ConC,NewConC), prologize_recursively_list(ConA,NConA), prologize_recursively_list(NewConC,NConC), list_conj(NConA,GoalA), list_conj(NConC,GoalC). prologize_recursively(dist(DRSS,DRSO),Goal) :- !, clean_up(DRSS,drs(US,ConS)), clean_up(DRSO,drs(UO,ConO)), find_det(ConS,Det), find_det_arg(Det,OldVar,Amt,Arg,ArgVar), prologize_object(ConO,OldVar,ArgVar,NewConO), diff(ConS,NewConS),diff(NewConO,NConO), find_set_arg(NewConS,Y), remove_list(US,NewUS), ( \+ clause(is_query,_), skolemize(NewUS,[]), skolemize(UO,[ArgVar]); associate_var(UO,ArgVar)), prologize_recursively_list(NewConS,Goal1), prologize_recursively_list(NConO,Goal2), list_conj(Goal2,NewGoal2), NG2 = [(NewGoal2 ::-Arg,element(ArgVar,Y),card(Y,Amt))], append(Goal1,NG2,Goal). prologize_recursively(PlainGoal,PlainGoal). /* FURTHER PROBLEM: prologize_ifthen should really work recursively through ALL DRSes given to it. */ /* replace_variable(+List,+OldVariable,+NewVariable,?NewList) takes List looks for OldVariable and replaces it with NewVariable, returning NewList. */ replace_variable([],_,_,[]). replace_variable([H|T],OldVar,NewVar,[NewVar|NewT]) :- H == OldVar, replace_variable(T,OldVar,NewVar,NewT). replace_variable([H|T],OldVar,NewVar,[H|NewT]) :- \+ H == OldVar, replace_variable(T,OldVar,NewVar,NewT). /* find_det(+List,?Det) looks though List for determiner information, then returns Det--the determiner information. */ find_det([H|_],H) :- H = det(_,_). find_det([H|T],Det) :- \+ H = det(_,_), find_det(T,Det). /* find_det_arg(+Det,?Var,?Amt,?NewHead) looks through the determiner information, Det, for the noun headed by the determiner, then returns the variable of the noun, Var, the cardinality of the determiner, Amt, and the noun with a new variable. */ find_det_arg(Det,Oldvar,Amt,NewHead,I) :- Det = det(Head,D), Head =.. [H1,Oldvar], D = amt:Amt, NewHead =.. [H1,I]. remove_list([],[]). remove_list([U],U). /* prologize_object(+List,+OldVariable,+NewVariable,-NewList) looks through List for occurances of OldVariable and replaces it with NewVariable, returning the NewList. */ prologize_object([],_,[]). prologize_object([H|[]],OldVar,NewVar,[NewCon]) :- H =.. HList, (mem(OldVar,HList), replace_variable(HList,OldVar,NewVar,NewHList), NewCon =.. NewHList; NewCon =..HList). prologize_object([H|T],OldVar,NewVar,NewCon) :- H =.. HList, (mem(OldVar,HList), replace_variable(HList,OldVar,NewVar,NewHList), NewH =.. NewHList; NewH =..HList), prologize_object(T,OldVar,NewVar,NewT), append([NewH],NewT,NewCon). /* associate_var(?List,+Argument) looks through List for variable. If a variable is found, it associates Argument with that variable. That is, it instantiates that variable to be a list of another variable and Argument. */ associate_var([],_). associate_var([U|Rest],Arg) :- var(U), U = [_,Arg], associate_var(Rest,Arg). associate_var([U|Rest],Arg) :- \+ var(U), associate_var(U,Arg), associate_var(Rest,Arg). /* find_set_arg(Input,Output) find the representation for the set. */ find_set_arg([X|[]],Y) :- X = card(Y,_). find_set_arg([X|_],Y) :- X = card(Y,_). find_set_arg([_|X],Y) :- find_set_arg(X,Y). /* * skolemize(Term,Args) * Instantiates every free variable in Term to a unique structure * containing Args. In effect, this replaces each variable in Term * with a unique Skolem function of Args. */ skolemize(Variable,Args) :- var(Variable), already_member(Variable,Args), !. /* If a variable appears in Args, it is not really a new variable in its sub-DRS and therefore shouldn't be skolemized. */ skolemize(Variable,Arg) :- var(Variable), !, unique_integer(N), (var(Arg), NewArg = [Arg], append([N],NewArg,Variable); Arg == [], Variable = N; append([N],Arg,Variable)). skolemize(Atom,_) :- atomic(Atom), !. skolemize([Head|Tail],Args) :- !, skolemize(Head,Args), skolemize(Tail,Args). skolemize(card(_),_). skolemize(Structure,Args) :- Structure =.. List, skolemize(List,Args). /* already_member(Variable,+List) checks List to see if Variable is an element of it. It suceeds if Variable is. */ already_member(X,[Y|_]) :- X==Y. already_member(X,[Y|_]) :- \+ var(Y), already_member(X,Y). already_member(X,[_|Z]) :- already_member(X,Z). /* make_Set_var(-Set,+Amt) creates a set of variables which has Amt members. */ make_Set_var(Set,1) :- Set = [_]. make_Set_var(Set,Amt) :- Amt > 0, NewAmt is Amt - 1, make_Set_var(S,NewAmt), append([_],S,Set). /* diff(+Cons,?NewCons). Looks for determiner conditions within the dr structure, uses information to distinguish whether the noun attached to the determiner has a distributive or collective reading, then prepares the predicate representing the noun for skolemization. */ diff([],[]). diff([H|T],Con) :- \+ H = det(_,_), diff(T,NewT), append([H],NewT,Con). diff([D|[H|T]],Con) :- D = det(Head,Det), Head == H, Det = syn:number:Num, Det = syn:type:Type, Det = syn:name:Name, common_noun(Name,Num,Type,_,_), make_pred(Head,NewHead,Det,Type), diff(T,NewT), append(NewHead,NewT,C1), append([D],C1,Con). /* make_pred(+Head,?NewHead,+Det,+Type). Creates new predicates for noun according to their type for diff. */ make_pred(Head,NewHead,Det,col) :- Det = type:card, Det = amt:Amt, Head =.. [_,I], NewHead = [Head,card(I,Amt)]. make_pred(Head,NewHead,Det,ind) :- Det = type:card, Det = amt:Amt, Head =.. [H,I], make_Set_var(Set,Amt), append(Set,[Var],I), make_pred_list(Set,H,NHead), make_element_list(NHead,Var,Amt,NH), append(NHead,NH,NewHead). /* make_pred_list recursively makes a predicate for each variable in Set. */ make_pred_list([],_,[]). make_pred_list([H|T],Head,NewH) :- NH = [Head,H], NH1 =.. NH, make_pred_list(T,Head,NH2), append([NH1],NH2,NewH). /* make_element_list(+ElementsList,Var,+Amt,?Result) takes a list of elements which are in the same Set, and recursively creates referents that links these elements together along with the card of these list indicators. */ make_element_list([H|[]],X,Amt,Result) :- H =.. [_,Hvar], Result = [element(Hvar,X),card(X,Amt)]. make_element_list([H|T],X,Amt,Result) :- H =.. [_,Hvar], Ele = [element(Hvar,X),card(X,Amt)], make_element_list(T,X,Amt,R1), append(Ele,R1,Result). /* * clean_up(DRS1,DRS2) * performs preprocessing needed on all DRSes to clean up * extraneous information left in by the DRS-builder. */ clean_up(drs(U,OldCon),drs(U,NewCon)) :- discard_information(OldCon,Con1), unify_equated_referents(drs(U,Con1),drs(U,NewCon)). /* * unify_equated_referents(DRS1,DRS2) * unifies all discourse referents that are joined by '=' * in the DRS conditions. For instance, * drs([X,Y],[X=Y,donkey(Y)]) * is changed to * drs([X],[donkey(X)]). * This is a temporary measure to reduce the need for a table * of identity. It will be dropped when the table of identity * is fully supported. */ unify_equated_referents(drs(U,Con),drs(U,NewCon)) :- unify_equated(Con,NewCon). unify_equated([],[]). unify_equated([X=X|Tail],NewTail) :- !, unify_equated(Tail,NewTail). unify_equated([Head|Tail],[Head|NewTail]) :- unify_equated(Tail,NewTail). /* discard_information simply calls discard_gender_information, * discar_case_information, and discard_number_information. */ discard_information(DRS1,DRS2) :- discard_gender_information(DRS1,D1), discard_case_information(D1,D2), discard_number_information(D2,DRS2). /* * discard_gender_information(DRS1,DRS2) * simplifies DRS1 by removing gender conditions (which were * needed only to resolve anaphors), giving DRS2. * Fully recursive -- actually removes all terms of the form gender(_,_) * from any Prolog structure. */ discard_gender_information(X,X) :- (var(X) ; atomic(X)), !. discard_gender_information([Head|Rest],Result) :- nonvar(Head), Head = gender(_,_), !, discard_gender_information(Rest,Result). discard_gender_information([Head|Rest],[NewHead|NewRest]) :- !, discard_gender_information(Head,NewHead), discard_gender_information(Rest,NewRest). discard_gender_information(Term,NewTerm) :- Term =.. [Functor|List], discard_gender_information(List,NewList), NewTerm =.. [Functor|NewList]. /* * discard_case_information(DRS1,DRS2) * simplifies DRS1 by removing case conditions (which were * needed only to resolve anaphors), giving DRS2. * Fully recursive -- actually removes all terms of the form case(_,_) * from any Prolog structure. */ discard_case_information(X,X) :- (var(X) ; atomic(X)), !. discard_case_information([Head|Rest],Result) :- nonvar(Head), Head = case(_,_), !, discard_case_information(Rest,Result). discard_case_information([Head|Rest],[NewHead|NewRest]) :- !, discard_case_information(Head,NewHead), discard_case_information(Rest,NewRest). discard_case_information(Term,NewTerm) :- Term =.. [Functor|List], discard_case_information(List,NewList), NewTerm =.. [Functor|NewList]. /* * discard_number_information(DRS1,DRS2) * simplifies DRS1 by removing number conditions (which were * needed only to resolve plural anaphors), giving DRS2. * Fully recursive -- actually removes all terms of the form number(_,_) * from any Prolog structure. */ discard_number_information(X,X) :- (var(X) ; atomic(X)), !. discard_number_information([Head|Rest],Result) :- nonvar(Head), Head = number(_,_), !, discard_number_information(Rest,Result). discard_number_information([Head|Rest],[NewHead|NewRest]) :- !, discard_number_information(Head,NewHead), discard_number_information(Rest,NewRest). discard_number_information(Term,NewTerm) :- Term =.. [Functor|List], discard_number_information(List,NewList), NewTerm =.. [Functor|NewList]. /* * assert_or_process(Goal) * assert_or_process_list(GoalList) * These predicates accept Prolog goals and process each by * either adding it to the knowledge base as a fact, * or executing it as a query, as appropriate. */ assert_or_process_list([]). assert_or_process_list([H|T]) :- (H = [_|_], assert_or_process_list(H); assert_or_process(H)), assert_or_process_list(T). assert_or_process(query(X)) :- !, write('Querying: '), process_query_list(X,Result), write(Result),nl,!. assert_or_process(neg(_)) :- !, write('neg not yet supported'),nl. assert_or_process(abs(_,_,_)) :- !, write('Anaphoric Subordination'), write(' not yet supported'), nl. assert_or_process((C::-A)) :- !, ((C = neg(_); A = neg(_)), write('neg not yet supported'), nl; distribute_consequents((C::-A),Rules), note_list(Rules)). assert_or_process(Fact) :- note(Fact). /* process_query_list(+List, ?Result) takes a list of goals to be checked to see if any are of the form: A::-C. If there are, the consequents are distributed. If list memebers do not fit the form, then they are untouched. */ process_query_list([(C::-A)|[]],Result) :- distribute_consequents((C::-A),Rules), test_list(Rules,Result). process_query_list([H|[]],Result) :- test_list(H,Result). process_query_list([(C::-A)|Rest],Result) :- distribute_consequents((C::-A),Rules), test_list(Rules,Result1), process_query_list(Rest,Result2), (Result1 == Result2, Result = Result1; Result = no). process_query_list([H|Rest],Result) :- test(H,Result1), process_query_list(Rest,Result2), (Result1 == Result2, Result = Result1; Result = no). process_query_list((C::-A),Result) :- distribute_consequents((C::-A),Rules), test_list(Rules,Result). process_query_list(Goal,Result) :- test(Goal,Result). /* * note(Clause) * note_list(Clause) * adds a clause or list of clauses to the knowledge base. */ note(Goal) :- assert_fact(Goal). assert_fact((Fact1,Fact2)) :- write('Asserting: '), write(Fact1), nl, assertz(fact(Fact1)), assert_fact(Fact2), !. assert_fact(Fact) :- write('Asserting: '), write(Fact), nl, assertz(fact(Fact)). note_list([]). note_list([Clause|Rest]) :- note(Clause), note_list(Rest). /* Clears database...*/ clear_database :- abolish(fact/1). /* * test(+Query,?Result) * Attempts to find one solution for Query. If successful, * unifies Result with 'yes'; otherwise, with 'no'. * test/2 itself always succeeds if Result is initially uninstantiated. */ test(Goal,yes) :- test1(Goal), !. test(Goal,yes) :- test4(Goal), !. test(_,no). test1((Goal1,Goal2)) :- \+ Goal1 = card(_,_), fact(Goal1), check_dups(Goal1), write(Goal1), write(','), nl,assert(is_dist(Goal1)), test1(Goal2),!. test1(Goal1) :- \+ Goal1 = card(_,_), fact(Goal1), check_dups(Goal1), assert(is_dist(Goal1)), write(Goal1), write(','),nl,!. test1((Goal1,Goal2)) :- Goal1 = card(Set,Card), clause(fact(element(_,Set)),_), NewGoal =.. [card,Set,NewCard], fact(NewGoal), Card =< NewCard, write(Goal1), write(','),nl, test1(Goal2), !. test1(Goal1) :- Goal1 = card(Set,Card), NewGoal =.. [card,Set,NewCard], clause(fact(element(_,Set)),_), fact(NewGoal), Card =< NewCard, write(Goal1),write(','),nl,!. test1((Goal1,Goal2)) :- fact(Goal1), write(Goal1), write(','),nl, test1(Goal2),!. test1(Goal) :- fact(Goal), write(Goal), write(','),nl,!. test1(Goal) :- Goal = (X:-A,B,card(Set,Amt)), fact((X:-A,B,card(Set,NewAmt))), Amt =< NewAmt, write(Goal), write(','),nl,!. test1((Goal1,Goal2)) :- test2(Goal1), test1(Goal2), !. test1(Goal) :- test2(Goal), !. test2((A:-B)) :- abolish(is_dist/1), test1(B), test1(A), !. test2(Goal) :- abolish(is_dist/1), fact((Goal :- X)), write(Goal), write(','),nl,test1(X),!. /* test4 tests generic predicates by asserting the antecedent and then testing the consequents. */ test4((Goal1:-Goal2)) :- assertz(fact(Goal2)), write('Momentarily asserting '), write(Goal2), nl, test1(Goal1), retract(fact(Goal2)), !. /* test_list(+List, ?Result) takes a list of goals as input, tests them, and returns a yes or no depending on whether they suceeded or failed. */ test_list([H|[]],Result) :- test(H,Result). test_list([H|T],Result) :- test(H,R1), test_list(T,R2), (R1 == R2, Result = R1; Result = no). /* check_dups(Goal) checks to see if Goal has already been tested. */ check_dups(_) :- \+ clause(is_dist(_),_), !. check_dups(Goal) :- \+ is_dist(Goal), !. /* * list_conj(ListOfGoals,ConjoinedGoals) * Transforms [Goal1,Goal2,Goal3] into (Goal1,Goal2,Goal3). * The latter is executable in Prolog. * We must never pass a list to Prolog for execution because * it would be interpreted as a list of files to consult. */ list_conj([],true) :- !. list_conj(neg(A),neg(A)). list_conj([Goal],Goal) :- !. list_conj([Goal|Rest],(Goal,NewRest)) :- list_conj(Rest,NewRest). /* * distribute_consequents(IllFormedRule,ListOfRules) * Deals with rules that have compound (list) consequents. * Transforms a,b,c ::- d,e,f into the list: * [(a:-d,e,f),(b:-d,e,f),(c:-d,e,f)]. */ distribute_consequents(((C::-A)::-B), Goal) :- distribute_consequents((C::-A,B),Goal), !. distribute_consequents((C::-(A::-B)),[(C:-(A:-B))]). distribute_consequents(((C,Cs)::-A),[(C:-A)|Rest]) :- !, distribute_consequents((Cs::-A),Rest). distribute_consequents((C::-A), [(C:-A)]). /*------------------------------------------------------------------* TEST SUITE *------------------------------------------------------------------*/ tryd([clear]) :- write('Clearing Database...'), clear_database. tryd([clear,'.']) :- tryd([clear]). tryd(String) :- Features = sem:in:[drs([],[])], phrase(discourse(Features),String), Features = sem:out:DRS, DRS = [Current|_], write(String),nl, write('--------------------------------------------------'),nl, write('Originally constructed DRS:'),nl, write('--------------------------------------------------'),nl, display_drs(Current),nl, prologize_whole_drs(Current), write('--------------------------------------------------'),nl. /***************************************************************************** * Program Start-up. The operations which begin the program, read in the * * input, and make any corrections before sending it off to the main part * * of the program. * *****************************************************************************/ go :- nl, nl, write('****************************************************************************** * Welcome to PluralDRT! This program is designed to take natural language * * sentences as input, convert the sentences into Prolog structures, and * * assert them into the Prolog database or query the database as necessary. * * Pluraldrt uses DR structures as the semantic representation. * * * * Please enter the sentence(s) at the prompt. To clear the database, * * type: "clear". To quit, hit return at the prompt. * *******************************************************************************'), go1. go1 :- nl,nl, write('Sentence: '), read_atomics(Sentence), check_punct(Sentence,NS), (NS == [], nl, write('Thank you!'); tryd(NS), go1). go1 :- nl, nl, write('--- Your sentence is not in the grammar --- Please try again. '), go1. /* The remainder of the code is borrowed from Michael A. Covington's Natural Language Processing for Prolog Programmers for the user interface end of PluralDRT. */ % File READATOM.PL % Michael A. Covington % Natural Language Processing for Prolog Programmers % (Prentice-Hall) % Appendix B % Version of read_atomics/1 for most Prologs. See text. % read_atomics(-Atomics) % Reads a line of text, breaking it into a % list of atomic terms: [this,is,an,example]. read_atomics(Atomics) :- read_char(FirstC,FirstT), complete_line(FirstC,FirstT,Atomics). % read_char(-Char,-Type) % Reads a character and runs it through char_type/1. read_char(Char,Type) :- get0(C), char_type(C,Type,Char). % complete_line(+FirstC,+FirstT,-Atomics) % Given FirstC (the first character) and FirstT (its type), reads % and tokenizes the rest of the line into atoms and numbers. complete_line(_,end,[]) :- !. % stop at end complete_line(_,blank,Atomics) :- % skip blanks !, read_atomics(Atomics). complete_line(FirstC,special,[A|Atomics]) :- % special char !, name(A,[FirstC]), read_atomics(Atomics). complete_line(FirstC,alpha,[A|Atomics]) :- % begin word complete_word(FirstC,alpha,Word,NextC,NextT), name(A,Word), % may not handle numbers correctly - see text complete_line(NextC,NextT,Atomics). % complete_word(+FirstC,+FirstT,-List,-FollC,-FollT) % Given FirstC (the first character) and FirstT (its type), % reads the rest of a word, putting its characters into List. complete_word(FirstC,alpha,[FirstC|List],FollC,FollT) :- !, read_char(NextC,NextT), complete_word(NextC,NextT,List,FollC,FollT). complete_word(FirstC,FirstT,[],FirstC,FirstT). % where FirstT is not alpha % char_type(+Code,?Type,-NewCode) % Given an ASCII code, classifies the character as % 'end' (of line/file), 'blank', 'alpha'(numeric), or 'special', % and changes it to a potentially different character (NewCode). char_type(10,end,10) :- !. % UNIX end of line mark char_type(13,end,13) :- !. % DOS end of line mark char_type(-1,end,-1) :- !. % get0 end of file code char_type(Code,blank,32) :- % blanks, other ctrl codes Code =< 32, !. char_type(Code,alpha,Code) :- % digits 48 =< Code, Code =< 57, !. char_type(Code,alpha,Code) :- % lower-case letters 97 =< Code, Code =< 122, !. char_type(Code,alpha,NewCode) :- % upper-case letters 65 =< Code, Code =< 90, !, NewCode is Code + 32. % (translate to lower case) char_type(Code,special,Code). % all others check_punct([],[]). check_punct(['!'|[]],['.']). check_punct([H|[]],[H]). check_punct(['!'|T],Result) :- check_punct(T,TResult), append(['.'],TResult,Result). check_punct([H|T],Result) :- check_punct(T,TResult), append([H],TResult,Result).