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(tools_meta,[safe_time_out/3,
6 safe_time_out_or_virtual_time_out/3,
7 no_time_out_value/1,
8 call_residue/2,
9 safe_on_exception/3, safe_on_exception_silent/3,
10 reraise_important_exception/1,
11 det_call_cleanup/2,
12 frozen_member/2,
13 catch_matching/3,
14 safe_numbervars/3,
15 translate_term_into_atom/2,
16 translate_term_into_atom_with_max_depth/2, translate_term_into_atom_with_max_depth/3,
17 setof4/4
18 ]).
19
20 :- use_module(module_information).
21
22 :- meta_predicate safe_time_out(0,*,*).
23 :- meta_predicate setof4(*,*,0,*).
24
25 :- module_info(group,infrastructure).
26 :- module_info(description,'A utility on timeouts safe_time_out seperated out from tools.pl to avoid cyclic module dependencies.').
27
28 no_time_out_value(2147483646). % special value to turn time_out off
29
30 :- use_module(library(timeout),[time_out/3]).
31 safe_time_out(Call,TO,Res) :- \+ integer(TO),!,
32 print('### Warning: TIME_OUT value not an integer: '), print(TO),nl,
33 ITO is round(TO),
34 safe_time_out(Call,ITO,Res).
35 safe_time_out(Call,TO,Res) :-
36 % unsat cores used to set time_out to float; SICStus time_out/3 silently fails with float
37 no_time_out_value(MaxTO),
38 (TO >= MaxTO
39 -> (TO=MaxTO -> true % special value to turn time_out off (set by -disable_time_out); time_out has an overhead
40 ; print('### Warning: TIME_OUT value too high (>2147483646): '), print(TO),nl,
41 print('### Calling goal without TIME_OUT (use 2147483646 to turn TIME_OUT off silently)'),nl),
42 call(Call), Res=success
43 ; TO < 1 -> print('### Warning: TIME_OUT value too small: '), print(TO),nl,
44 time_out(Call,1,Res)
45 ? ; time_out(Call,TO,Res)).
46
47 :- meta_predicate safe_time_out_or_virtual_time_out(0,*,*).
48 % catches virtual-time_out exceptions and returns them as normal time_out result
49 safe_time_out_or_virtual_time_out(Call,TO,Res) :-
50 catch(safe_time_out(Call,TO,Res), enumeration_warning(_,_,_,_,_), Res=time_out).
51
52 :- meta_predicate call_residue(0,*).
53
54
55
56 % If possible, call frozen/2 on the entire list of variables at once.
57 % This is possible on SICStus 4.6 and later as well as SWI,
58 % where frozen/2 can be called on any term
59 % to get the goals for all attributed variables in that term.
60 % On SICStus 4.5 and older, Ciao, and YAP (and possibly others),
61 % frozen/2 can only be called directly on a variable.
62 % In that case we need to manually iterate over the residual variables
63 % and call frozen/2 on each one.
64 :- if(catch((dif(X,Y), frozen([X,Y],_)), _, false)).
65
66 ?call_residue(X,Residue) :- call_residue_vars(X,V),
67 frozen(V,R),
68 %(R=true -> Residue=[] ; Residue = [R]).
69 flatten_conj(R,Residue,[]).
70
71 flatten_conj(true) --> !,[].
72 flatten_conj((A,B)) --> !, flatten_conj(A), flatten_conj(B).
73 flatten_conj(C) --> [C].
74
75 :- else.
76
77 /* from File: sp4_compatibility_mappings.pl */
78 /* Created: 08/05/2007 by Michael Leuschel */
79 call_residue(X,Residue) :- call_residue_vars(X,V),filter_residue_vars(V,Residue).
80
81 filter_residue_vars([],[]).
82 filter_residue_vars([H|T],Res) :-
83 frozen(H,FH),
84 (FH=true -> Res=RT
85 ; %format('Residue for variable ~w: ~w~n',[H,FH]),
86 Res = [FH|RT]),
87 filter_residue_vars(T,RT).
88
89 :- endif.
90
91 % --------------------------
92
93 :- meta_predicate safe_on_exception(*,0,0).
94 % use if you want to catch any exception; ensures time_out not treated and passed on
95 safe_on_exception(E,Call,ExcCode) :-
96 catch(call(Call), E, (
97 print(exception(E)),nl,
98 reraise_important_exception(E),
99 ExcCode
100 )).
101 :- meta_predicate safe_on_exception_silent(*,0,0).
102 safe_on_exception_silent(E,Call,ExcCode) :-
103 catch(call(Call), E, (
104 reraise_important_exception(E),
105 ExcCode
106 )).
107
108 % reraise important exceptions
109 reraise_important_exception(time_out) :- !, throw(time_out).
110 reraise_important_exception(_).
111
112 % --------------------------
113
114
115 :- meta_predicate det_call_cleanup(0,0).
116 % a simplified version of call_cleanup; only really works for deterministic predicates
117 % or if it is ok to call CleanUpCall multiple times on success of Call
118 % is much faster, as call_cleanup has an overhead of about 300 Prolog instructions
119 % but this seems even slower ?? add_transitions__with_timeout_fail_loop
120
121 det_call_cleanup(Call,CleanUpCall) :-
122 catch(
123 if(Call,CleanUpCall,CleanUpCall),
124 E,
125 (CleanUpCall, throw(E))).
126
127 % --------------------------
128
129 %:- meta_predicate frozen_member(*,0). % without meta_predicate we can call frozen_member with a variable for Goal
130 % check if Goal is attached as a pending co-routine to Var
131 frozen_member(Var,Goal) :- var(Var), frozen(Var,Frozen),
132 frozen_mem_aux(Frozen,Goal).
133
134 frozen_mem_aux((A,B),Goal) :- !, (frozen_mem_aux(A,Goal) ; frozen_mem_aux(B,Goal)).
135 frozen_mem_aux(Goal,Goal).
136
137
138 % --------------------------
139
140 % like catch/3, but it does not fail if an exception occurs that is not
141 % unifiable with the second argument. Instead it re-throws the original
142 % exception.
143 :- meta_predicate catch_matching(0,*,0).
144 catch_matching(Call,Exception,Handler) :-
145 catch(Call, E, (E=Exception -> Handler ; throw(E))).
146
147 % --------------------------
148
149 safe_numbervars(Term,Start,End) :-
150 catch(numbervars(Term,Start,End), E, (
151 print('Exception during numbervars: '),print(E),nl,nl,
152 reraise_important_exception(E)
153 )).
154
155 % --------------------------
156
157
158 :- use_module(library(codesio), [write_term_to_codes/3]).
159 translate_term_into_atom(CTerm,Atom) :- atom(CTerm),!, Atom=CTerm.
160 translate_term_into_atom(Nr,Atom) :- number(Nr),!, number_codes(Nr,C), atom_codes(Atom,C).
161 translate_term_into_atom(CTerm,Atom) :-
162 copy_term(CTerm,Term), safe_numbervars(Term,0,_),
163 write_term_to_codes(Term,Temp,[quoted(true),numbervars(true)]),
164 atom_codes(Atom,Temp).
165 translate_term_into_atom_with_max_depth(Term,Atom) :-
166 translate_term_into_atom_with_max_depth(Term,20,Atom).
167 translate_term_into_atom_with_max_depth(Term,_,Atom) :- atomic(Term),!,Atom=Term.
168 translate_term_into_atom_with_max_depth(Term,Limit,Atom) :-
169 write_term_to_codes(Term,Temp,[quoted(true),numbervars(true),max_depth(Limit)]),
170 atom_codes(Atom,Temp).
171
172 % --------------------------
173
174 % a re-implementation of setof to overcome issue that
175 % the order of solutions of setof in SICStus was different on Intel and Arm platforms (cf test 1033)
176
177 setof4(DynamicPart,StaticPart,P,DynamicSolutions) :-
178 % first find all solutions
179 findall(sol(StaticPart,DynamicPart),
180 call(P), SolList),
181 sort(SolList,SList),
182 % now extract static parts and for each static part a list of all dynamic solutions
183 ? get_merged_solution(SList,StaticPart,DynamicSolutions).
184
185
186
187 % extract solutions in a set_of style manner
188 get_merged_solution([sol(StaticPart1,Sol1)|T],StaticPart,MergedSols) :-
189 merge_sols(T,StaticPart1,TSol,Rest), % get all solutions with same static part
190 (StaticPart=StaticPart1, MergedSols = [Sol1|TSol]
191 ;
192 ? get_merged_solution(Rest,StaticPart,MergedSols)).
193
194 % get all solutions with same staticPart and return tail of list
195 merge_sols([sol(StaticPart,Sol)|TS],StaticPart,[Sol|TSol],Rest) :- !,
196 merge_sols(TS,StaticPart,TSol,Rest).
197 merge_sols(Rest,_,[],Rest).