1 % (c) 2009-2024 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(kernel_freetypes,
6 [registered_freetype/2
7 ,freetype_case_db/3
8 ,reset_freetypes/0
9 ,create_freetype_typeid/3
10 ,add_freetype/2
11 ,freetype_with_single_case/2
12 ,enumerate_freetype_wf/4
13 ,max_freetype_enum_depth/1
14 ,max_cardinality_freetype/2
15 ,set_freetype_depth/1
16 ,is_a_freetype_wf/3
17 ,freetype_cardinality/2
18 ,is_recursive_freetype/1
19 ,is_infinite_freetype/1
20 ,get_freetype_type_parameters/3
21 ,is_maximal_freetype/1
22 ,is_empty_freetype/1
23 ,is_non_empty_freetype/1
24 ,test_empty_freetype/2
25 ,get_freeval_type/3
26 ,registered_freetype_case_value/3
27 ,get_freetype_id/2]).
28
29 /* This module contains predicates for the support for free types as they are used in Z specifications */
30
31 :- use_module(module_information,[module_info/2]).
32 :- module_info(group,proz).
33 :- module_info(description,'This module contains predicates that cope with the internal freetype. This is currently only used by the Z extension.').
34
35 :- use_module(library(lists)).
36
37 :- use_module(error_manager,[add_internal_error/2]).
38 :- use_module(debug,[debug_format/3]).
39 :- use_module(self_check).
40 :- use_module(preferences,[get_preference/2]).
41 :- use_module(kernel_objects,[basic_type/2,
42 enumerate_type_wf/4,
43 max_cardinality/2,safe_add/3]).
44 :- use_module(typing_tools,[is_infinite_type/1]).
45
46
47 :- dynamic freetype_register_db/2, freetype_with_single_case/2, freetype_case_db/3,
48 max_iteration_depth/1, marked_cases/3, max_cardinality_db/3.
49
50 /* freetype_register queries the database of registered free types for the list of cases */
51 registered_freetype(Id, Cases) :- freetype_register_db(Id,Cases).
52
53 /* the extended register contains for each case a mark if it is recursive or not,
54 for internal use only (regarding the module) */
55 extended_register(Id, ECases, ERec) :-
56 marked_cases(Id,Cs,Rec),!,ECases = Cs, ERec=Rec.
57 extended_register(Id, ECases, Rec) :-
58 registered_freetype(Id,Cases),
59 mark_recursive_cases(Cases,Id,ECases),
60 (memberchk( case(_,_,recursive), ECases ) -> Rec=recursive ; Rec=non_recursive),
61 assertz(marked_cases(Id,ECases,Rec)).
62
63 /* reset_freetypes clears the database of registered free types */
64 reset_freetypes :-
65 retractall(freetype_register_db(_,_)),
66 retractall(freetype_case_db(_,_,_)),
67 retractall(freetype_with_single_case(_,_)),
68 retractall(marked_cases(_,_,_)),
69 retractall(max_iteration_depth(_)),
70 retractall(max_cardinality_db(_,_,_)).
71
72 portray_freetypes :- freetype_register_db(Id,Cases), format('Freetype ~w~n cases:~w~n',[Id,Cases]),fail.
73 portray_freetypes.
74
75 % can be used to deconstruct or construct a freetype(.) argument term
76 % the freetype does not yet need to be registered
77 get_freetype_type_parameters(Id,Functor,TypeParas) :- var(Id),!,
78 Id =.. [Functor|TypeParas].
79 %(freetype_register_db(Id,_) -> true ; add_internal_error('Illegal freetype: ',get_freetype_type_parameters(Id,Functor,TypeParas))).
80 get_freetype_type_parameters(Id,Functor,TypeParas) :-
81 %freetype_register_db(Id,_),
82 (atom(Id) -> TypeParas=[], Functor=Id
83 ; Id =.. [Functor|TypeParas]).
84
85 /* with set_freetype_depth it is possible to set the maximum recursion depth of
86 free types when enumerating them. If the depth is not set manually the
87 depth is taken from the preference globalsets_fdrange. Mainly for testing */
88 set_freetype_depth(Depth) :-
89 retractall(max_iteration_depth(_)),assertz(max_iteration_depth(Depth)).
90
91
92 % encode type parameters into freetype id:
93 % Type parameters are Prolog variables, or for B types (e.g., integer) for freetype uses
94 create_freetype_typeid(Id,TypeParameters,FreetypeId) :-
95 FreetypeId =.. [Id|TypeParameters].
96
97
98 /* add_freetypes appends a new free type to the database of free types */
99 % example (test 1304):
100 % add_freetype('List'(T),[case(nil,constant([nil])),case(cons,couple(T,freetype(List(T))))]).
101 add_freetype(Id, Cases) :-
102 debug_format(19,'Add freetype ~w with cases ~w~n',[Id,Cases]),
103 ( is_caselist(Cases,Id) ->
104 ( registered_freetype(Id,OldCases) ->
105 add_internal_error('freetype already registered: ', add_freetype(Id, Cases)),
106 OldCases=Cases
107 ? ; member(case(C,_),Cases),freetype_case_db(C,OId,_) ->
108 add_internal_error('freetype case registered twice: ', (C,OId,Id)),fail
109 ;
110 assertz(freetype_register_db(Id,Cases)),
111 (Cases = [case(Name,_)] -> assertz(freetype_with_single_case(Id,Name)) ; true),
112 maplist(register_case(Id),Cases)
113 )
114 ;
115 add_internal_error('list of cases invalid', add_freetype(Id, Cases)),fail).
116
117 register_case(FreetypeId,case(CaseId,CaseType)) :-
118 assertz(freetype_case_db(CaseId,FreetypeId,CaseType)).
119
120 % compute a value representing a freetype case identifier; for use in REPL, VisB, JSON trace replay, ...
121 % it requires the type of the ID in context to instantiate polymorphic Freetype IDs
122 registered_freetype_case_value(ID,Type,Value) :-
123 freetype_case_db(ID,FreeTypeId,ArgType),
124 get_freetype_id(Type,FreeTypeId),
125 convert_case_type(ArgType,FreeTypeId,ID,Value).
126
127 get_freetype_id(freetype(FID),FID). % a constructor without args
128 get_freetype_id(set(couple(_,freetype(FID))),FID). % a constructor with args
129
130 convert_case_type(Var,FreeTypeId,ConstId,Val) :- var(Var),
131 add_internal_error('Variable freetype case: ',convert_case_type(Var,FreeTypeId,ConstId,Val)),
132 fail.
133 convert_case_type(constant(_),FreeTypeId,ConstId,Val) :- !,
134 Val=freeval(FreeTypeId,ConstId,term(ConstId)).
135 convert_case_type(Type,FreeTypeId,CaseId,
136 closure(['_zzzz_unary','_zzzz_binary'],[Type,freetype(FreeTypeId)],EQ)) :- % could be pre-compiled
137 % {x,y| y = CaseId(x)} -> a lambda function constructing free values
138 ARG = b(identifier('_zzzz_unary'),Type,[]),
139 LHS = b(identifier('_zzzz_binary'),freetype(FreeTypeId),[]),
140 RHS = b(freetype_constructor(FreeTypeId,CaseId,ARG),freetype(FreeTypeId),[]),
141 EQ = b(equal(LHS,RHS),pred,[prob_annotation('SYMBOLIC')]).
142
143
144 /* enumerates a free type with regard to the maximum recursive iteration of free types.
145 The first argument is "basic" or "tight", depending on wich enumeration method
146 should be used for the value of the included types. */
147 enumerate_freetype_wf(EType,freeval(Id,C,Value),freetype(Id),WF) :-
148 max_freetype_enum_depth(MaxDepth),
149 ? enumerate_freetype_wf(EType,freeval(Id,C,Value),freetype_lim_depth(Id,MaxDepth),WF).
150 enumerate_freetype_wf(EType,freeval(Id,C,Value),freetype_lim_depth(Id,Depth),WF) :-
151 Depth > 0,
152 extended_register(Id,Cases,_),
153 ? member(case(C,CT,Mark),Cases),
154 /* If the case is recursive we have to append information about the
155 maximum recursion depth to limit the size of the data structure.
156 In the non-recursive case we leave the type untouched */
157 (Mark = recursive ->
158 NewDepth is Depth - 1, insert_recursion_depth(CT,NewDepth,NewCT)
159 ; NewCT = CT),
160 ? enumerate_type_wf(Value, NewCT, EType,WF).
161
162
163 /* gives the maximumm cardinality of the free type with regard to the maximum
164 recursive iteration of free types */
165 max_cardinality_freetype(freetype(Id),Card) :-
166 max_freetype_enum_depth(MaxDepth),
167 max_cardinality_freetype(freetype_lim_depth(Id,MaxDepth),Card).
168 max_cardinality_freetype(freetype_lim_depth(Id,Depth),Card) :-
169 (Depth > 0 ->
170 /* we save the computed cardinality for a certain maximum depth to
171 prevent exponential computation steps */
172 (max_cardinality_db(Id,Depth,Card) -> true;
173 (NewDepth is Depth - 1,
174 extended_register(Id,Cases,_),
175 sum_cases_cardinalities(Cases,NewDepth,Card),
176 assertz(max_cardinality_db(Id,Depth,Card))));
177 Card = 0).
178
179 sum_cases_cardinalities([],_,0).
180 sum_cases_cardinalities([case(_,Type,Mark)|Rest],Depth,Card) :-
181 sum_cases_cardinalities(Rest,Depth,CRest),
182 /* like in enumerate_freetype we distinguish between recursive and
183 non-recursive cases and add information about the maximum recursion
184 depth in the former case. */
185 (Mark = recursive ->
186 insert_recursion_depth(Type,Depth,NewType);
187 NewType = Type),
188 max_cardinality(NewType,C),
189 add_with_inf(CRest,C,Card).
190
191 add_with_inf(inf,_,R) :- !,R=inf.
192 add_with_inf(_,inf,R) :- !,R=inf.
193 add_with_inf(A,B,R) :- R is A+B.
194
195 max_freetype_enum_depth(Depth) :-
196 max_iteration_depth(Depth) -> true; get_preference(globalsets_fdrange,Depth).
197
198 :- use_module(typing_tools,[valid_ground_type/1]).
199 /* Is the list a valid list of cases? */
200 is_caselist([],_).
201 is_caselist([case(Case,CaseType)|Rest],Id) :-
202 atom(Case),
203 (ground(Id) -> valid_ground_type(CaseType)
204 ; true % TODO: we could check if variables are subset of variables of Id
205 ),
206 is_caselist(Rest,Id).
207
208
209 /* Appends to all freetype references a maximum depth */
210 insert_recursion_depth(freetype(Id),Depth,freetype_lim_depth(Id,Depth)) :- !.
211 insert_recursion_depth(set(A),Depth,set(B)) :- !,insert_recursion_depth(A,Depth,B).
212 insert_recursion_depth(seq(A),Depth,seq(B)) :- !,insert_recursion_depth(A,Depth,B).
213 insert_recursion_depth(couple(A,B),Depth,couple(C,D)) :- !,
214 insert_recursion_depth(A,Depth,C),insert_recursion_depth(B,Depth,D).
215 insert_recursion_depth(record(Fields),Depth,record(RFields)) :- !,insert_recursion_depth_fields(Fields,Depth,RFields).
216 insert_recursion_depth(X,_,X).
217
218 insert_recursion_depth_fields([],_,[]).
219 insert_recursion_depth_fields([field(Name,Type)|Rest],Depth,[field(Name,RType)|RRest]) :-
220 insert_recursion_depth(Type,Depth,RType),
221 insert_recursion_depth_fields(Rest,Depth,RRest).
222
223
224 :- assert_must_succeed((kernel_freetypes:find_references([case(a,integer),
225 case(b,set(freetype(aa))),
226 case(c,couple(boolean,seq(freetype(bb)))),
227 case(d,record([field(f1,constant),
228 field(f2,freetype(cc)),
229 field(f3,freetype(aa)),
230 field(f4,integer)]))],[cc,bb,aa]))).
231 /* finds all references to other free types */
232 find_references(Cases,Ids) :- find_references(Cases,[],Ids).
233 find_references([],Refs,Refs).
234 find_references([case(_,Type)|Rest],Acc,Refs) :- find_references_type(Type,Acc,TRefs),find_references(Rest,TRefs,Refs).
235 find_references_type(Type,Refs) :- find_references_type(Type,[],Refs).
236 find_references_type(freetype(Id),Acc,Res) :- !,(member(Id,Acc) -> Res=Acc ; Res=[Id|Acc]).
237 find_references_type(set(X),Acc,Refs) :- !, find_references_type(X,Acc,Refs).
238 find_references_type(seq(X),Acc,Refs) :- !, find_references_type(X,Acc,Refs).
239 find_references_type(couple(X,Y),Acc,Refs) :- !, find_references_type(X,Acc,XRefs),find_references_type(Y,XRefs,Refs).
240 find_references_type(record(Fields),Acc,Refs) :- !, find_references_fields(Fields,Acc,Refs).
241 find_references_type(_,Refs,Refs).
242 find_references_fields([],Refs,Refs).
243 find_references_fields([field(_,Type)|Rest],Acc,Refs) :-
244 find_references_type(Type,Acc,TRefs),
245 find_references_fields(Rest,TRefs,Refs).
246
247 /* find all references of a type (also indirect references) by breadth first search */
248 find_transitive_references(Type,Refs) :-
249 find_references_type(Type,Refs1),
250 find_transitive_references(Refs1,[],Refs).
251 find_transitive_references([],Refs,Refs).
252 find_transitive_references([Id|Rest],VisitedRefs,AllRefs) :-
253 registered_freetype(Id,Cases),
254 find_references(Cases,Refs),
255 append([Id|Rest],VisitedRefs,FoundRefs),
256 find_new_ids(Refs,FoundRefs,NewRefs),
257 append(Rest,NewRefs,UnvisitedRefs),
258 find_transitive_references(UnvisitedRefs,[Id|VisitedRefs],AllRefs).
259
260 find_new_ids([],_,[]).
261 find_new_ids([Id|Rest],Old,New) :-
262 member(Id,Old),!,
263 find_new_ids(Rest,Old,New).
264 find_new_ids([Id|Rest],Old,[Id|New]) :-
265 find_new_ids(Rest,Old,New).
266
267 /* translate each case/2 into a case/3 where the third argument is "recursive" or "plain" */
268 mark_recursive_cases([],_,[]).
269 mark_recursive_cases([case(C,Type)|Rest],Id,[case(C,Type,Mark)|MRest]) :-
270 ? (recursive_case(Id,Type) ->
271 Mark = recursive;
272 Mark = plain),
273 mark_recursive_cases(Rest,Id,MRest).
274
275 recursive_case(Id,Type) :-
276 find_transitive_references(Type,Refs),
277 ? member(Id,Refs).
278
279 /* used by kernel_objects:check_element_of */
280 % TODO: we could enumerate cases, maybe only if non-recursive
281 is_a_freetype_wf(freeval(Id,Case,Value),Id,_WF) :-
282 registered_freetype(Id,Cases),
283 (freetype_with_single_case(Id,SC) -> Case=SC ; true),
284 is_a_freetype_wf2(Case,Cases,Value).
285 :- block is_a_freetype_wf2(-,?,?).
286 is_a_freetype_wf2(Case,Cases,Value) :-
287 member(case(Case,CT),Cases),!,
288 basic_type(Value,CT).
289
290
291 is_recursive_freetype(Id) :-
292 extended_register(Id,_,recursive).
293
294 freetype_cardinality(Id,Card) :-
295 extended_register(Id,Cases,Rec),
296 % if there is a recursive case, the cardinality is infinite
297 ( Rec=recursive ->
298 Card = inf
299 ;
300 sum_case_cardinality(Cases,0,Card)).
301 sum_case_cardinality([],Card,Card).
302 sum_case_cardinality([case(_Name,Type,_)|Crest],In,Out) :-
303 max_cardinality(Type,TypeCard),
304 safe_add(In,TypeCard,NewCard),
305 sum_case_cardinality(Crest,NewCard,Out).
306
307 is_infinite_freetype(Id) :-
308 extended_register(Id,Cases,Rec),
309 (Rec=recursive -> true ; has_infinite_case(Cases)).
310 has_infinite_case([case(_Name,Type,Mark)|_]) :-
311 is_infinite_case(Type,Mark),!.
312 has_infinite_case([_|Crest]) :-
313 has_infinite_case(Crest).
314 is_infinite_case(_Type,recursive) :- !. % recursive types are always infinite [unless no base-case !?!?]
315 is_infinite_case(Type,_) :-
316 is_infinite_type(Type).
317
318 is_maximal_freetype(_).
319 % we assume a freetype is always maximal (i.e., covers all possible values with its basic type)
320 % TO DO: Daniel check this !
321
322 is_empty_freetype(_) :- fail.
323 is_non_empty_freetype(_).
324 test_empty_freetype(_,pred_false).
325 % we assume all freetypes are not empty; TO DO: DANIEL please check
326
327 % get the basic type of a freeval(Id,Case,Val) for Val
328 get_freeval_type(Id,Case,CaseType) :-
329 registered_freetype(Id,_),
330 freetype_case_db(Case,Id,CaseType).