% Version of LADDER GRAMMAR & IDA QUERY INTERPRETER

% AUTHOR: PWG

:-dynamic saved_attrib_q/1.

saved_attrib_q(dummy).

% dictionaries

ship_dict('Constellation').
ship_dict('Nautilus').
ship_dict('JFK').

attrib_dict(length).
attrib_dict(displacement).
attrib_dict(class).

% database
attrib_db('Constellation',length,10000).
attrib_db('Constellation',displacement,5000).
attrib_db('Constellation',class,carrier). 

attrib_db('Nautilus',length,10000).
attrib_db('Nautilus',displacement,600).
attrib_db('Nautilus',class,sub).

attrib_db('JFK',length,8000).
attrib_db('JFK',displacement,1600).
attrib_db('JFK',class,destroyer).

% values attributes can take

poss_vals(length,N) :- number(N).
poss_vals(length,N) :- member(N,[long,medium,short]).

poss_vals(displacement,N) :- number(N).
poss_vals(class,C) :- member(C,[frigate,carrier,sub,destroyer]).

% GRAMMAR RULES & TRANSLATION

attrib(A) --> {attrib_dict(A)}, [A] ;
                     {attrib_dict(A)},the, [A].

and --> [and].
the --> [the].
of -->[of].
or -->[or].
equal --> [equals];[=];[value].
gt --> [greater],[than];[>].
lt --> [less],[than]; [<].
how_many --> [how],[many];
             [what],[number],of.


attrib_prop(Var,attrib_db(Var,At,V))  -->
         attrib(At),equal,val(At,V).
% > if numeric value
attrib_prop(Var,(attrib_db(Var,At,W),W >V))  -->
         attrib(At),gt,val(At,V).
attrib_prop(Var,(attrib_db(Var,At,W),W <V))  -->
         attrib(At),lt,val(At,V).


attrib_props(Var,Prop)  --> attrib_prop(Var,Prop).
attrib_props(Var,((P),Ps))  --> attrib_prop(Var,P),and,
                                attrib_props(Var,Ps).
attrib_props(Var,(P,Ps))  --> attrib_prop(Var,P),
                              attrib_props(Var,Ps).

attrib_props(Var,(P;Ps))  --> attrib_prop(Var,P),or,
                              attrib_props(Var,Ps).
        

val(A,N) --> [N],{poss_vals(A,N)}.   % must be of correct type

attribute([A]) --> attrib(A).
attribute([A|As]) --> attrib(A),and,
                               attribute(As).
attribute([A|As]) --> attrib(A),
                               attribute(As).

ship(S) --> the,ship_name(S);
                 ship_name(S).
ship_name(name(S)) --> {ship_dict(S)},[S].

present(print) --> [what],[is];
                   [show];
                   [give],[me];
										         [display].

% what is/are attribute values of ship
query(print(S,A)) -->  present(P),attribute(A),of,ship(S).

% which ships have given attributes
query(which_ships(S,Props)) -->
              [which],[ships],[have],attrib_props(S,Props).

% how many ships have given attributes
query(how_many_ships(S,Props)) -->
              how_many,[ships],[have],attrib_props(S,Props).

% ellipsis for asking for attributes

query(ellipsis(print(S,A))) -->
              of,ship(S),{saved_attrib_q(print(_,A))}.


% processing the query 
 process_query(print(name(S),As)) :-
        update_saved_query(print(name(S),As)),
        write('For ship '),write(S),write(' :'),nl,
        find_prt_attribs(S,As).

 process_query(ellipsis(print(name(S),As))) :-
        write('For ship '),write(S),write(' :'),nl,
        find_prt_attribs(S,As).

 process_query(which_ships(S,Props)) :-
                my_setof(S,Props,Set),
                write(Set),nl.

 process_query(how_many_ships(S,Props)) :-
                my_setof(S,Props,Set),
                length(Set,L),
                write(L),nl.

find_prt_attribs(S,[]) :- nl.

find_prt_attribs(S,[A|As]) :-
        attrib_db(S,A,V),
        write(A),write(' is '),
        write(V),nl,
        find_prt_attribs(S,As).   

% updating saved queries

update_saved_query(print(S,As)) :-
        retract(saved_attrib_q(P)),
        assert(saved_attrib_q(print(S,As))).    % always assert the new attrib-qry

% top level query call
ida(Q) :-
          phrase(query(QI),Q),
          process_query(QI).         

% utils

% can't use setof 'cause of existential quantifiers

my_setof(X,G,S) :-
      findall(X,G,S1),     % finds all solutions for X of G for all bindings
						list_to_set(S1,S).

list_to_set([],[]).
list_to_set([X|Xs],Y) :- member(X,Xs),!,  
       list_to_set(Xs,Y).
list_to_set([X|Xs],[X|Y]) :-   
       list_to_set(Xs,Y).

