s(s(NP,VP),X,Z) :- np(NP,X,Y), vp(VP,Y,Z).
np(np(DET,N),X,Z) :- det(DET,X,Y), n(N,Y,Z).
vp(vp(V,NP),X,Z) :- v(V,X,Y), np(NP,Y,Z).
vp(vp(V),X,Z) :- v(V,X,Z).

det(det(the),[the|W],W).
det(det(a),[a|W],W).

n(n(woman),[woman|W],W).
n(n(man),[man|W],W).

v(v(loves),[loves|W],W).

sentence(X,Z) :- s(s(NP,VP),X,Z), pptree(s(NP,VP)), nl, nl.


%% The wrapper predicate. Calls pptree/2 with 0 indentation.
pptree(Term) :- pptree(0,Term), nl, nl.

%% pptree/2: The first argument says how much a new line has to be
%% indented, the second argument is the term to be printed.

%% If the term is an atom, print it.
pptree(_,A) :- 
	atom(A), 
	write(A).

%% If the term is a complex term, print a newline, print as many
%% blanks as specified by the first argument, print the functor and an
%% opening parenthesis, then call the predicate process_args to print
%% the arguments.
pptree(Ident,C) :- 
	functor(C,F,A),
	A > 0,
	nl, tab(Ident),
	write(F),write('('),
	NewIdent is Ident + 3,
	process_args(1,A,C,NewIdent),
	write(')').

%% process_args/4: The first two arguments say which argument is
%% currently being processed and how many arguments there are all
%% together. If these numbers are the same, we are processing the last
%% argument. So, just call pptree/1 for this argument.
process_args(A,A,C,Ident) :-
	arg(A,C,AthArg),
	pptree(Ident,AthArg).

%% If the first argument (X) is smaller than the second argument, then
%% get the Xth argument of the complex term and print it. Then process
%% the remaining args: that is, call process_args with the first
%% argument (X) being increased by 1.
process_args(X,A,C,Ident) :-
	X < A,
	arg(X,C,XthArg),
	pptree(Ident,XthArg),
	NewX is X + 1,
	process_args(NewX,A,C,Ident).



parse :- write('Enter English input: '),
         read_line(Input),
         trim_period(Input,I),
         nl,
         s(Parse_form,I,[]),
         pptree(Parse_form), 
         nl, nl.

trim_period([.],[]).     
trim_period([X|R],[X|T]) :- trim_period(R,T).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%  read_line
%%%  Similar to read_sent in Pereira and Shieber, Prolog and
%%%        Natural Language Analysis, CSLI, 1987.
%%%
%%%  Examples:
%%%           % read_line(L).
%%%           The sky was blue, after the rain.
%%%           L = [the,sky,was,blue,',',after,the,rain,'.']
%%%           % read_line(L).
%%%           Which way to the beach?
%%%           L = [which,way,to,the, beach,'?']
%%%

read_line(Words) :- get0(C),
                    read_rest(C,Words).
          
/* A period or question mark ends the input. */
read_rest(46,['.']) :- !.
read_rest(63,['?']) :- !.

/* Spaces and newlines between words are ignored. */
read_rest(C,Words) :- ( C=32 ; C=10 ) , !,
                     get0(C1),
                     read_rest(C1,Words).

/* Commas between words are absorbed. */
read_rest(44,[','|Words]) :- !,
                             get0(C1),
                             read_rest(C1,Words).

/* Otherwise get all of the next word. */
read_rest(C,[Word|Words]) :- lower_case(C,LC),
                             read_word(LC,Chars,Next),
                             name(Word,Chars),
                             read_rest(Next,Words).

/* Space, comma, newline, period or question mark separate words. */
read_word(C,[],C) :- ( C=32 ; C=44 ; C=10 ;
                         C=46 ; C=63 ) , !.

/* Otherwise, get characters, convert alpha to lower case. */
read_word(C,[LC|Chars],Last) :- lower_case(C,LC),
                                get0(Next),
                                read_word(Next,Chars,Last).

/* Convert to lower case if necessary. */
lower_case(C,C) :- ( C <  65 ; C > 90 ) , !.
lower_case(C,LC) :- LC is C + 32.


/* for reference ... 
newline(10).
comma(44).
space(32).
period(46).
question_mark(63).
*/