1 | % (c) 2009-2019 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 | ,reset_freetypes/0 | |
8 | ,add_freetype/2 | |
9 | ,enumerate_freetype/3 | |
10 | ,max_cardinality_freetype/2 | |
11 | ,set_freetype_depth/1 | |
12 | ,is_a_freetype_wf/3 | |
13 | ,freetype_cardinality/2 | |
14 | ,is_infinite_freetype/1 | |
15 | ,is_maximal_freetype/1 | |
16 | ,is_empty_freetype/1 | |
17 | ,is_non_empty_freetype/1 | |
18 | ,test_empty_freetype/2 | |
19 | ,get_freeval_type/3]). | |
20 | ||
21 | /* This module contains predicates for the support for free types as they are used in Z specifications */ | |
22 | ||
23 | :- use_module(module_information,[module_info/2]). | |
24 | :- module_info(group,proz). | |
25 | :- module_info(description,'This module contains predicates that cope with the internal freetype. This is currently only used by the Z extension.'). | |
26 | ||
27 | :- use_module(library(lists)). | |
28 | ||
29 | :- use_module(error_manager,[add_internal_error/2]). | |
30 | :- use_module(self_check). | |
31 | :- use_module(preferences,[get_preference/2]). | |
32 | :- use_module(kernel_objects,[basic_type/2, | |
33 | enumerate_type/3, | |
34 | max_cardinality/2,safe_add/3]). | |
35 | :- use_module(typing_tools,[is_infinite_type/1]). | |
36 | ||
37 | ||
38 | :- dynamic freetype_register_db/2, max_iteration_depth/1, marked_cases/2, max_cardinality_db/3. | |
39 | ||
40 | /* freetype_register queries the database of registered free types for the list of cases */ | |
41 | registered_freetype(Id, Cases) :- freetype_register_db(Id,Cases). | |
42 | ||
43 | /* the extended register contains for each case a mark if it is recursive or not, | |
44 | for internal use only (regarding the module) */ | |
45 | extended_register(Id, ECases) :- marked_cases(Id,Cs),!,ECases = Cs. | |
46 | extended_register(Id, ECases) :- | |
47 | registered_freetype(Id,Cases),mark_recursive_cases(Cases,Id,ECases), | |
48 | assert(marked_cases(Id,ECases)). | |
49 | ||
50 | /* reset_freetypes clears the database of registered free types */ | |
51 | reset_freetypes :- | |
52 | retractall(freetype_register_db(_,_)), | |
53 | retractall(marked_cases(_,_)), | |
54 | retractall(max_iteration_depth(_)), | |
55 | retractall(max_cardinality_db(_,_,_)). | |
56 | ||
57 | /* with set_freetype_depth it is possible to set the maximum recursion depth of | |
58 | free types when enumerating them. If the depth is not set manually the | |
59 | depth is taken from the preference globalsets_fdrange. Mainly for testing */ | |
60 | set_freetype_depth(Depth) :- | |
61 | retractall(max_iteration_depth(_)),assert(max_iteration_depth(Depth)). | |
62 | ||
63 | /* add_freetypes appends a new free type to the database of free types */ | |
64 | add_freetype(Id, Cases) :- | |
65 | ( is_caselist(Cases) -> | |
66 | ( registered_freetype(Id,_) -> | |
67 | add_internal_error('freetype already registered: ', add_freetype(Id, Cases)),fail | |
68 | ; registered_freetype(OId,Cs),member(case(C,_),Cs),member(case(C,_),Cases) -> | |
69 | add_internal_error('freetype case registered twice: ', (C,OId,Id)),fail | |
70 | ; otherwise -> | |
71 | assert(freetype_register_db(Id,Cases)) | |
72 | ) | |
73 | ; otherwise -> | |
74 | add_internal_error('list of cases invalid', add_freetype(Id, Cases)),fail). | |
75 | ||
76 | /* enumerates a free type with regard to the maximum recursive iteration of free types. | |
77 | The first argument is "basic" or "tight", depending on wich enumeration method | |
78 | should be used for the value of the included types. */ | |
79 | enumerate_freetype(EType,freeval(Id,C,Value),freetype(Id)) :- | |
80 | max_freetype_depth(MaxDepth), | |
81 | enumerate_freetype(EType,freeval(Id,C,Value),freetype(Id,MaxDepth)). | |
82 | enumerate_freetype(EType,freeval(Id,C,Value),freetype(Id,Depth)) :- | |
83 | Depth > 0, | |
84 | extended_register(Id,Cases), | |
85 | member(case(C,CT,Mark),Cases), | |
86 | /* If the case is recursive we have to append information about the | |
87 | maximum recursion depth to limit the size of the data structure. | |
88 | In the non-recursive case we leave the type untouched */ | |
89 | (Mark = recursive -> | |
90 | (NewDepth is Depth - 1, insert_recursion_depth(CT,NewDepth,NewCT)); | |
91 | NewCT = CT), | |
92 | enumerate_type(Value, NewCT, EType). | |
93 | ||
94 | ||
95 | /* gives the maximumm cardinality of the free type with regard to the maximum | |
96 | recursive iteration of free types */ | |
97 | max_cardinality_freetype(freetype(Id),Card) :- | |
98 | max_freetype_depth(MaxDepth), | |
99 | max_cardinality_freetype(freetype(Id,MaxDepth),Card). | |
100 | max_cardinality_freetype(freetype(Id,Depth),Card) :- | |
101 | (Depth > 0 -> | |
102 | /* we save the computed cardinality for a certain maximum depth to | |
103 | prevent exponential computation steps */ | |
104 | (max_cardinality_db(Id,Depth,Card) -> true; | |
105 | (NewDepth is Depth - 1, | |
106 | extended_register(Id,Cases), | |
107 | sum_cases_cardinalities(Cases,NewDepth,Card), | |
108 | assert(max_cardinality_db(Id,Depth,Card)))); | |
109 | Card = 0). | |
110 | ||
111 | sum_cases_cardinalities([],_,0). | |
112 | sum_cases_cardinalities([case(_,Type,Mark)|Rest],Depth,Card) :- | |
113 | sum_cases_cardinalities(Rest,Depth,CRest), | |
114 | /* like in enumerate_freetype we distinguish between recursive and | |
115 | non-recursive cases and add information about the maximum recursion | |
116 | depth in the former case. */ | |
117 | (Mark = recursive -> | |
118 | insert_recursion_depth(Type,Depth,NewType); | |
119 | NewType = Type), | |
120 | max_cardinality(NewType,C), | |
121 | add_with_inf(CRest,C,Card). | |
122 | ||
123 | add_with_inf(inf,_,R) :- !,R=inf. | |
124 | add_with_inf(_,inf,R) :- !,R=inf. | |
125 | add_with_inf(A,B,R) :- R is A+B. | |
126 | ||
127 | max_freetype_depth(Depth) :- | |
128 | max_iteration_depth(Depth) -> true; get_preference(globalsets_fdrange,Depth). | |
129 | ||
130 | /* Is the list a valid list of cases? */ | |
131 | is_caselist([]). | |
132 | is_caselist([case(_,_)|Rest]) :- is_caselist(Rest). | |
133 | ||
134 | ||
135 | /* Appends to all freetype references a maximum depth */ | |
136 | insert_recursion_depth(freetype(Id),Depth,freetype(Id,Depth)) :- !. | |
137 | insert_recursion_depth(set(A),Depth,set(B)) :- !,insert_recursion_depth(A,Depth,B). | |
138 | insert_recursion_depth(seq(A),Depth,seq(B)) :- !,insert_recursion_depth(A,Depth,B). | |
139 | insert_recursion_depth(couple(A,B),Depth,couple(C,D)) :- !, | |
140 | insert_recursion_depth(A,Depth,C),insert_recursion_depth(B,Depth,D). | |
141 | insert_recursion_depth(record(Fields),Depth,record(RFields)) :- !,insert_recursion_depth_fields(Fields,Depth,RFields). | |
142 | insert_recursion_depth(X,_,X). | |
143 | ||
144 | insert_recursion_depth_fields([],_,[]). | |
145 | insert_recursion_depth_fields([field(Name,Type)|Rest],Depth,[field(Name,RType)|RRest]) :- | |
146 | insert_recursion_depth(Type,Depth,RType), | |
147 | insert_recursion_depth_fields(Rest,Depth,RRest). | |
148 | ||
149 | ||
150 | :- assert_must_succeed((kernel_freetypes:find_references([case(a,integer), | |
151 | case(b,set(freetype(aa))), | |
152 | case(c,couple(boolean,seq(freetype(bb)))), | |
153 | case(d,record([field(f1,constant), | |
154 | field(f2,freetype(cc)), | |
155 | field(f3,freetype(aa)), | |
156 | field(f4,integer)]))],[cc,bb,aa]))). | |
157 | /* finds all references to other free types */ | |
158 | find_references(Cases,Ids) :- find_references(Cases,[],Ids). | |
159 | find_references([],Refs,Refs). | |
160 | find_references([case(_,Type)|Rest],Acc,Refs) :- find_references_type(Type,Acc,TRefs),find_references(Rest,TRefs,Refs). | |
161 | find_references_type(Type,Refs) :- find_references_type(Type,[],Refs). | |
162 | find_references_type(freetype(Id),Acc,Res) :- !,(member(Id,Acc) -> Res=Acc ; Res=[Id|Acc]). | |
163 | find_references_type(set(X),Acc,Refs) :- !, find_references_type(X,Acc,Refs). | |
164 | find_references_type(seq(X),Acc,Refs) :- !, find_references_type(X,Acc,Refs). | |
165 | find_references_type(couple(X,Y),Acc,Refs) :- !, find_references_type(X,Acc,XRefs),find_references_type(Y,XRefs,Refs). | |
166 | find_references_type(record(Fields),Acc,Refs) :- !, find_references_fields(Fields,Acc,Refs). | |
167 | find_references_type(_,Refs,Refs). | |
168 | find_references_fields([],Refs,Refs). | |
169 | find_references_fields([field(_,Type)|Rest],Acc,Refs) :- | |
170 | find_references_type(Type,Acc,TRefs), | |
171 | find_references_fields(Rest,TRefs,Refs). | |
172 | ||
173 | /* find all references of a type (also indirect references) by breadth first search */ | |
174 | find_transitive_references(Type,Refs) :- | |
175 | find_references_type(Type,Refs1), | |
176 | find_transitive_references(Refs1,[],Refs). | |
177 | find_transitive_references([],Refs,Refs). | |
178 | find_transitive_references([Id|Rest],VisitedRefs,AllRefs) :- | |
179 | registered_freetype(Id,Cases), | |
180 | find_references(Cases,Refs), | |
181 | append([Id|Rest],VisitedRefs,FoundRefs), | |
182 | find_new_ids(Refs,FoundRefs,NewRefs), | |
183 | append(Rest,NewRefs,UnvisitedRefs), | |
184 | find_transitive_references(UnvisitedRefs,[Id|VisitedRefs],AllRefs). | |
185 | ||
186 | find_new_ids([],_,[]). | |
187 | find_new_ids([Id|Rest],Old,New) :- | |
188 | member(Id,Old),!, | |
189 | find_new_ids(Rest,Old,New). | |
190 | find_new_ids([Id|Rest],Old,[Id|New]) :- | |
191 | find_new_ids(Rest,Old,New). | |
192 | ||
193 | /* translate each case/2 into a case/3 where the third argument is "recursive" or "plain" */ | |
194 | mark_recursive_cases([],_,[]). | |
195 | mark_recursive_cases([case(C,Type)|Rest],Id,[case(C,Type,Mark)|MRest]) :- | |
196 | (recursive_case(Id,Type) -> | |
197 | Mark = recursive; | |
198 | Mark = plain), | |
199 | mark_recursive_cases(Rest,Id,MRest). | |
200 | ||
201 | recursive_case(Id,Type) :- | |
202 | mnf(kernel_freetypes:find_transitive_references(Type,Refs)), | |
203 | member(Id,Refs). | |
204 | ||
205 | /* used by kernel_objects:check_element_of */ | |
206 | is_a_freetype_wf(freeval(Id,Case,Value),Id,_WF) :- | |
207 | registered_freetype(Id,Cases), | |
208 | is_a_freetype_wf2(Case,Cases,Value). | |
209 | :- block is_a_freetype_wf2(-,?,?). | |
210 | is_a_freetype_wf2(Case,Cases,Value) :- | |
211 | member(case(Case,CT),Cases),!, | |
212 | basic_type(Value,CT). | |
213 | ||
214 | freetype_cardinality(Id,Card) :- | |
215 | extended_register(Id,Cases), | |
216 | % if there is a recursive case, the cardinality is infinite | |
217 | ( memberchk( case(_,_,recursive), Cases ) -> | |
218 | Card = inf | |
219 | ; otherwise -> | |
220 | sum_case_cardinality(Cases,0,Card)). | |
221 | sum_case_cardinality([],Card,Card). | |
222 | sum_case_cardinality([case(_Name,Type,_)|Crest],In,Out) :- | |
223 | max_cardinality(Type,TypeCard), | |
224 | safe_add(In,TypeCard,NewCard), | |
225 | sum_case_cardinality(Crest,NewCard,Out). | |
226 | ||
227 | is_infinite_freetype(Id) :- | |
228 | extended_register(Id,Cases), | |
229 | has_infinite_case(Cases). | |
230 | has_infinite_case([case(_Name,Type,Mark)|_]) :- | |
231 | is_infinite_case(Type,Mark),!. | |
232 | has_infinite_case([_|Crest]) :- | |
233 | has_infinite_case(Crest). | |
234 | is_infinite_case(_Type,recursive) :- !. % recursive types are always infinite | |
235 | is_infinite_case(Type,_) :- | |
236 | is_infinite_type(Type). | |
237 | ||
238 | is_maximal_freetype(_). | |
239 | % we assume a freetype is always maximal (i.e., covers all possible values with its basic type) | |
240 | % TO DO: Daniel check this ! | |
241 | ||
242 | is_empty_freetype(_) :- fail. | |
243 | is_non_empty_freetype(_). | |
244 | test_empty_freetype(_,pred_false). | |
245 | % we assume all freetypes are not empty; TO DO: DANIEL please check | |
246 | ||
247 | % get the basic type of a freeval(Id,Case,Val) for Val | |
248 | get_freeval_type(Id,Case,CaseType):- | |
249 | registered_freetype(Id,Cases), | |
250 | member(case(Case,CaseType),Cases). |