%
%        This module defines the commands that compile and upload
%         transaction programs, "comp_trans", and database programs,
%         "comp_db", into the transaction base and database respectively.
%

:- import ins/1, asrt/1, db/1 from updates.
:- import wff/2 from parser.
:- import append/3 from basics.

 
% The "updatable" operator can only be used in database programs
% to declare the predicates representing tables that allow updates.
%

:- op(500, fx, [updatable]).

 
% A transaction program is stored in a file with the name of the
% form "name.ctr". The "comp_trans" command takes the "name" as
% argument, reads and parses the transaction program line by line
% creating the output as "name.ctr.o"; the rules written in the output
% file are then loaded into the transaction base.
%

comp_trans(File) :-
                in_filename(File,File1),
                see(File1),
                out_filename(File1,FileOut),
                unix('rm -f FileOut 2>/dev/null', R1),
                tell(FileOut),
                repeat_read_and_compile(File1,FileOut),
                seen, told.
               insert_file(FileOut).

 
% The input file is read and processed recursively until
% the end of file is reached.
%

repeat_read_and_compile(FileIn,FileOut) :-
        read(Term),
        (
        (Term == end_of_file) -> true
        ;
        read_and_write_rule(Term,FileIn,FileOut),
        repeat_read_and_compile(FileIn,FileOut)  ).

 
% To translate a rule from the transaction program, the head
% of the rule becomes trans(head) and the remainder of the rule
% does not change.
%
% For each rule, a new rule is created with the form head :- fail,
% to let Prolog know that the corresponding predicate is defined.
% The translated rule and the new rule associated to it are written
% in the output file.
%

read_and_write_rule(':-'(Lhs,Rhs),_,_) :-
        write('trans('),
        write(Lhs),
        write(')'),
        write(' :-  '),
        wff(Rhs, PRhs),
        write(PRhs),
        write('.'), nl,
        write(Lhs),
        write(' :- fail.'), nl.

 
% If the transaction is an atom then it is just copied to
% the output file.
%

read_and_write_rule(Term,_,_) :-
        not(Term = ':-'(_,_)),
        write(Term),
        write('.'), nl.

 
% A database program is stored in a file with the name of the
% form "name.db". The "name" has to be the same with the
% corresponding transaction program "name".
%
% The "comp_db" command takes the "name" as argument, reads
% and parses the database program line by line creating the output
% as "name.db.o"; the rules written in the output file are then
% inserted into the database.
%

comp_db(File) :-
                db_filename(File,File2),
                see(File2),
                read(Term),
                tmp_filename(File,File3),
                unix('rm -f File3 2>/dev/null', R1),
                tell(File3),
                write_a_line(Term,File2,File3),
                seen,
                told,
                see(File3),
                read(T),
                load_a_line(T,File3),
                seen.

 
% Any "updatable" declaration of a predicate "p" is translated
% to "p(X) = db(p(X))" and written into the output file.
%

write_a_line(updatable(Name/Num),File2,File3) :-
                db_mod(Name,Num,Arg),
                write(Arg), nl,
                db_ins(Name,Num,Argi),
                write(Argi), nl,
                read(Next), !,
                write_a_line(Next,File2,File3).

 
% The database rules are just copied to the output file.
%

write_a_line((Lhs :- Rhs),File2,File3) :-
                write(Lhs),
                write(' :- '),
                write(Rhs),
                write('.'), nl,
                read(Next), !,
                write_a_line(Next,File2,File3).

 
% If the input line contains an atom, then it is copied to the
% output file and inserted into the database.
%

write_a_line(Term,File2,File3) :-
                not(Term = updatable(_)),
                write_and_assert(Term,File2,File3).

write_and_assert(Term,File2,File3) :-
                (Term == end_of_file), !, true
                ;
                write(Term),
                write('.'), nl,
                read(Next), !,
                write_a_line(Next,File2,File3).

 
% For each "updatable" predicate "p", a tuple with the form
% "modif(p)" is inserted into the database.
%

oad_a_line(modif(P),File3) :-
                asrt(P),
                read(Next), !,
                load_a_line(Next,File3).

% The rules are inserted into the database
%
load_a_line((Lhs :- Rhs),File3) :-
                assert((Lhs :- Rhs)),
                read(Next), !,
                load_a_line(Next,File3).

 
% The database tuples are inserted into the database using
% "strong inserts". This type of inserts is implemented in the
% "updates" module and ensures that the database is not corrupted
% during backtracking.
%

load_a_line(Term,File3) :-
                not(Term = modif(_)),
                read_and_assert(Term,File3).

read_and_assert(Term,File3) :-
                (Term == end_of_file), !, true
                ;
                ins(Term),
                read(Next), !,
                load_a_line(Next,File3).

 
% For an updatable predicate, p, the clause "p(X) = db(p(X))"
% is built.
%

db_ins(Name,Num,Argj) :- name(Name,Namel),
                        db_name(Namel,Name1),
                        db_arg(Name1,Num,Argj).
 

db_name(Namel,Name1) :- append(Namel,"(",Name1).

db_arg(Name1,0,Argj) :- append(Name1,")",Arg1),
                       append(Arg1," :- db(",Arg2),
                       append(Arg2,Arg1,Arg3),
                       append(Arg3,").",Argl),
                       name(Argj,Argl).

db_arg(Name1,Num,Argj) :- name(Num,L),
                         append("X",L,Argi),
                         append(Name1,Argi,Tmp1),
                         Num1 is Num - 1,
                         (Num1 = 0,
                         db_arg(Tmp1,Num1,Argj)
                         ;
                         append(Tmp1,",",Tmp2),
                         db_arg(Tmp2,Num1,Argj)).

 
% For each updatable predicate, p, the tuple "modif(p(X))"
% is created for insertion into the database.
%

db_mod(Name,Num,Arg) :- name(Name,Namel),
                        db_name1(Namel,Name1),
                        db_arg1(Name1,Num,Arg).
 

db_name1(Namel,Name1) :- append("modif(",Namel,Namel1),
                         append(Namel1,"(",Name1).

db_arg1(Name1,0,Arg) :- append(Name1,")).",Arg1),
                        name(Arg,Arg1).

db_arg1(Name1,Num,Arg) :- name(Num,L),
                          append("X",L,Argi),
                          append(Name1,Argi,Tmp1),
                          Num1 is Num - 1,
                          (Num1 = 0,
                          db_arg1(Tmp1,Num1,Arg)
                          ;
                          append(Tmp1,",",Tmp2),
                          db_arg1(Tmp2,Num1,Arg)).

 
% The transaction and database program file names are
% created based on the arguments entered in "comp_trans" and
% "comp_db" commands. These names are required to read and
% write the files.
%

in_filename(FileIn,FileOut) :-
        name(FileIn,NameIn),
        append(NameIn,".ctr",NameOut),
        name(FileOut,NameOut).

out_filename(FileIn,FileOut) :-
        name(FileIn,NameIn),
        append(NameIn,".o",NameOut),
        name(FileOut,NameOut).

db_filename(FileIn,FileOut) :-
        name(FileIn,NameIn),
        append(NameIn,".db",NameOut),
        name(FileOut,NameOut).

tmp_filename(FileIn,FileOut) :-
        name(FileIn,NameIn),
        append(NameIn,".db.o",NameOut),
        name(FileOut,NameOut).

 
% The translated transaction program is inserted into the
% transaction base line by line.

insert_file(FileName) :-
        seeing(X),
        see(FileName),
        read_and_add_line,
        see(X).

read_and_add_line :-
        read(Line),
        (
        (Line == end_of_file)   -> true
        ;
        assert(Line),
        read_and_add_line ).