     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     % 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. 
%
load_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 ).



