%
% mapping ICode to CIF-code
%
% replace all restrict/4 by generate/5 with the appropriate result type
%

:- module(i2cif,[i2cif/2]).


i2cif([],[]) :-
	!.

i2cif([I|Is],[Cif|Cifs]) :-
	!,
	i2cif(I,Cif),
	i2cif(Is,Cifs).

i2cif(I,Cif) :-
	I=restrict(Fname,Arg_types,Args,Result),			%found a restrict/4
	!,
	find_function_result_type(Fname,Arg_types,Result_type),		%look up function result type
	Cif=generate(Fname,Arg_types,Args,Result_type,Result).		%replace with generate/5

i2cif(I,Cif) :-
	I=restrict_subquery(Generator,Set,Predicate),			%a restrict_subquery/3
	!,
	i2cif(Set,Set_in_cif),						%recursively work on G
	i2cif(Predicate,Predicate_in_cif),				%and P
	Cif=restrict_subquery(Generator,Set_in_cif,Predicate_in_cif).

i2cif(I,Cif) :-
	I=generate_subquery(Gen_type,Gen_var,Sub,Aux1,Aux2),		%a generate_subquery/5
	!,
	i2cif(Sub,Sub_in_cif),						%recursively work on it
	i2cif(Aux1,Aux1_in_cif),
	i2cif(Aux2,Aux2_in_cif),
	Cif=generate_subquery(Gen_type,Gen_var,Sub_in_cif,Aux1_in_cif,Aux2_in_cif).

i2cif(I,Cif) :-
	I=expression(Gen,Req,Agg,Set),			%a expression/4
	!,
	i2cif(Set,Set_in_cif),				%work on the set icode
	Cif=expression(Gen,Req,Agg,Set_in_cif).

i2cif(I,Cif) :-
	I=logical_or(Lhs,Rhs),		%logical_or/2
	!,
	i2cif(Lhs,Lhs_in_cif),
	i2cif(Rhs,Rhs_in_cif),
	Cif=logical_or(Lhs_in_cif,Rhs_in_cif).

i2cif(I,Cif) :-
	I=logical_and(Lhs,Rhs),		%logical_and/2
	!,
	i2cif(Lhs,Lhs_in_cif),
	i2cif(Rhs,Rhs_in_cif),
	Cif=logical_and(Lhs_in_cif,Rhs_in_cif).

i2cif(I,Cif) :-
	I=logical_not(X),		%logical_not/1
	i2cif(X,X_in_cif),
	Cif=logical_not(X_in_cif).

i2cif(I,I).				%all the rest


%
% given a function name & arg-types
% find its result type
%
% in order to do so, we have to access P/FDM meta-schema
%
find_function_result_type(Fname,Arg_types,Result_type) :-
	user:getentity(funmeta,Fobj),				%find the function object
	user:getfnval(fname,[Fobj],Fname),			%with the required function name Fname
	check_all_args(Fobj,Arg_types),				%check arg-types of this function object
	user:getfnval(result_type,[Fobj],Result_type_object),	%find result type object
	user:getfnval(oname,[Result_type_object],Result_type).	%find result type name

%
% check_all_args(Fobj,Types)
%
% Fobj =	the function object (in P/FDM meta-schema)
% Types =	a list of types (NOT type-object)
%
% check to see if all arg-types appear in function
%
check_all_args(_,[]) :-
	!.

check_all_args(Fobj,[Type|Types]) :-
	user:getfnval(fun_args,[Fobj],Expected_arg_obj),		%get an argument type-object
	user:getentity(objmeta,Test_type_obj),				%get an objmeta
	user:getfnval(oname,[Test_type_obj],Type),			%this is object specified by the type
	user:relative(entmeta,Expected_arg_obj,Expected_entmeta_obj),	%cast into entmeta
	user:relative(entmeta,Test_type_obj,Test_entmeta_obj),		%cast into entmeta
	class_or_subclass(Expected_entmeta_obj,Test_entmeta_obj),	%check if Type_obj is a subclass of Arg_obj
	check_all_args(Fobj,Types).					%proceed

%
% class_or_subclass(Type_obj1,Type_obj2)
%
% Type_obj1, Type_obj2 = entmeta objects in P/FDM meta-schema
%
% check to see if Type_obj1 equals Type_obj2
% or is a (indirect) superclass of Type_obj2
%
class_or_subclass(Type_obj,Type_obj).					%they're the same

class_or_subclass(Expected_entmeta_obj,Test_entmeta_obj) :-
	user:getfnval(supertype,[Test_entmeta_obj],Super_entmeta_obj),	%find a super-class Test_entmeta_obj
	class_or_subclass(Expected_entmeta_obj,Super_entmeta_obj).	%now they're equal or is subclass

