%	Family database program extended by Thierry Schang
%	U. of Illinois CS347, 1893


%try to add errors
%
%


%-----go

go :-
      repeat,
      retractall(errnb(_)),
      assert(errnb(0)),
      sentence,
      fail.

%-----errors

error(New_err) :-
         errnb(Current_err),
         retract(errnb(Current_err)),
         assert(errnb(New_err)).

prerror(0) :- print('not a valid statement').
prerror(1) :- print("'?' expected at end of question").
prerror(2) :- print("bad condition").
prerror(3) :- print("'.' expected at end of assertion").
prerror(4) :- print("incorrect question").
prerror(5) :- print("bad assertion after 'and' symbol").
prerror(6) :- print("bad assertion"). 

%----sentence

sentence :- question(P),
            P,!.
sentence :- assertion(P),
            assert(P),
	    print('Understood.'),!.
sentence :- # bye,
            ask('output file : ', File),
            save File,
            abort.
sentence :- prin('Error -- '),
            errnb(X),prerror(X),
            clearstream,!.

clearstream :-  # '.' ,!.
clearstream :-  # '?' ,!.
clearstream :-  # _,
		clearstream.

%-----question
%
%   The first clause deals with sentences beginning 
%         with 'what' or 'who'.
%   The second matches a 'do you' statement.

question(P) :-
            pronoun(P,P1,Value), error(4) ,
            aux_verb(Form),
            noun_phrase(P1,Value,Form,_,_),
            error(1) ,
            # '?'.
question(P) :-
            ques, error(4) ,
            verb(P,Att),
            pronoun(_,Att,Value),
            noun_phrase(Att,Value,Form,_,_),
            aux_verb(Form),
            # '?'.

%-----assertion
%
%     assertion1 processes a sentence of the form :
%         the 'Attribute' of 'Object'
%     and return in P the predicate :
%             Attribute(Object,_)
%
%     The variables Vli and Vlo are two lists of
%     the form [ Variable, atom, ..Tail ] , where
%     Variable is bounded to the string atom.
%     Vli is an input list and must be instantiated.
%     Vlo is an output list and must not be instantiated.
%     These lists are necessary to keep track of
%     the variables typed in by the user.
%     (see procedure 'bind').
%
assertion(P) :-
           assertion1(P1, Vli, Vli2),
           restofassert(P,P1, Vli2, Vlo),
           error(3) ,
           # '.' .

assertion1(P, Vli, Vlo) :-
           noun_phrase(P,Value,Form, Vli, Vli2),
           aux_verb(Form),
           noun(Value,Form, Vli2, Vlo).

restofassert((P1 :- P2),P1, Vli, Vlo) :-
           # if , error(2) ,
           assertion_list(P2, Vli, Vlo).
restofassert(P1,P1,_,_).

%-----assertion_list
%  This procedure returns the body of a clause to
%    be asserted.  

assertion_list(P, Vli, Vlo) :-
           assertion1(P1, Vli, Vli2),
           restoflist(P,P1, Vli2, Vlo).

restoflist((P1,P2),P1, Vli, Vlo) :-
           # and , error(5) ,
           assertion_list(P2, Vli, Vlo).
restoflist(P1,P1,_,_).

%-----ques

ques :- # do , # you .

%-----pronoun

pronoun((setof(Value,P,Result),print_list(Result)),P,Value) :-
              # who.
pronoun((confirm(P,yes) -> pp P | print("I don't know.")),P,Value) :-
              # what.

%-----aux_verb

aux_verb(singular) :- # is.
aux_verb(plurial) :- # are.

%-----verb

verb((confirm(Att,A),print(A)),Att) :- # know.

%-----noun_phrase

noun_phrase(Att(Object,Value),Value,Form, Vli, Vlo) :-
		determiner(def),
		noun(Att,Form,_,_),
                error(6) ,
                preposition,
                noun(Object,_,Vli,Vlo).
noun_phrase(Att,Value,Form,_,_) :-
                determiner(indef),
                noun(Att,Form,_,_).

%-----determiner

determiner(indef) :- # a.
determiner(indef) :- # an.
determiner(def) :- # the.

%-----noun

noun(Attribute,singular,Vli,Vlo) :-
              # Word,
              look(Attribute,Word,Vli,Vlo).
noun(Attribute,plurial,_,_) :-
              # Word,
              plurial_form(Attribute,Word).
 
look(Var,Word,Vli,Vlo) :-
              char(1, Word ,X),
              X <= 'Z' ,
              bind(Var,Word,Vli,Vlo).
look(Word,Word,Vli,Vlo).

%-----bind
%
%    This procedure returns in V the variable
%      that is to be bound with atom W.
%      It first try to find W in the input list.
%      If this fails, a new binding is created
%      and added to the input list to give the
%      output list.
   
bind(V, W, [V,W|Vtail], [V,W|Vtail]) :- !.

bind(V, W, [X,Y|Vtail], [X,Y|Vtail]) :-
                     bind(V, W, Vtail, Vtail),!.

bind(V, W, Tail, [V,W|Tail]).

%-----plurial_form

plurial_form(child,children) :- !.
plurial_form(X,X_s) :-
         name(X_s,Plur),
         change(Plur,Sing),
         name(X,Sing).

change([v,e,s],[f,e]) :- !.
change([s],[]) :- !.
change([L|T],[L|T1]) :- change(T,T1).

%-----preposition

preposition :- # of.

%-----

member(A, [A, ..B]).
member(A, [H, ..B]) :- member(A, B).

confirm(Attribute, yes) :- Attribute(X, Y), !.
confirm(_, no).

print_list([])	:- !, print("I don't know.").
print_list([A]) :- !, print(A).
print_list([A|B]) :- prin(A, ', '), print_list(B).

setof(X, P, L1) :-
	bagof(X, P, L),
	sort(L, L1).

sort([A|B], Y) :- !, sort(B, X), insert(A, X, Y).
sort([], []).

insert(X, [], [X]) :- !.
insert(X, [X|Y], [X|Y]) :- !.
insert(X, [A|B], [A|B1]) :- X > A, !, insert(X, B, B1).
insert(X, L, [X|L]).



