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 | | |
6 | | :- module(tools, [exact_member/2, |
7 | | exact_member_lookup/4, exact_member_replace/5, |
8 | | remove/3, remove_all/3, insert/3, |
9 | | remove_variables/3, |
10 | | list_intersection/3, list_difference/3, |
11 | | disjoint_list_union/3, lists_are_disjoint/2, |
12 | | string_concatenate/3, is_upper_case_name/1, |
13 | | %write_to_file/2, write_to_utf8_file/2, put_codes/2, % now in tools_files |
14 | | %print_error/1, |
15 | | print_message/1, print_message_with_max_depth/2, |
16 | | print_short_msg/1, |
17 | | print_bt_message/1, print_bt_trace_message/1, bt_trace/1, |
18 | | print_wtime/1, |
19 | | print_mb/1, |
20 | | prints/1, |
21 | | host_platform/1, platform_is_64_bit/0, max_tagged_integer/1, max_tagged_pow2/1, |
22 | | is_tagged_integer/1, |
23 | | is_absolute_path/1, |
24 | | get_parent_directory/2, get_tail_filename/2, get_modulename_filename/2, |
25 | | get_filename_extension/2, |
26 | | get_options/4,get_options/5, |
27 | | arg_is_number/2, arg_is_number_or_wildcard/2, |
28 | | check_filename_arg/2, |
29 | | arg_is_integer/2, |
30 | | split_filename/3, |
31 | | safe_absolute_file_name/2, safe_absolute_file_name/3, |
32 | | |
33 | | filter/4, |
34 | | flatten/2, |
35 | | % count_occurences/2, % now in tools_lists |
36 | | split_last/4, |
37 | | split_atom/3, split_chars/3, split_complex_sep/3, |
38 | | latex_escape_atom/2, |
39 | | b_escape_string_atom/2, b_string_escape_codes/2, |
40 | | string_escape/2, print_escaped/1, % can also be used for dot |
41 | | html_escape/2, |
42 | | read_string_from_file/2, read_string_from_file/3, |
43 | | % write_lines_to_file/2, % now in tools_files |
44 | | |
45 | | is_list_simple/1, |
46 | | ajoin/2, |
47 | | ajoin_with_limit/3, |
48 | | ajoin_with_sep/3, |
49 | | substitute/4, |
50 | | % call_residue/2, % now in tools_meta |
51 | | |
52 | | catch_call/1, |
53 | | %safe_on_exception/3, safe_on_exception_silent/3, % now in tools_meta |
54 | | %reraise_important_exception/1, % now in tools_meta |
55 | | % catch_matching/3, % now in tools_meta |
56 | | |
57 | | convert_list_into_pairs/2, convert_pairs_into_list/3, |
58 | | |
59 | | safe_univ/2, safe_univ_no_cutoff/2, |
60 | | safe_sort/3, |
61 | | safe_functor/4, |
62 | | |
63 | | safe_atom_codes/2, safe_atom_chars/3, |
64 | | atom_codes_with_limit/2, atom_codes_with_limit/3, |
65 | | truncate_atom/3, wrap_and_truncate_atom/4, |
66 | | safe_number_codes/2, |
67 | | ensure_atom/2, |
68 | | number_suffix/3, |
69 | | |
70 | | print_size_of_table/1, |
71 | | print_runtime/0, start_ms_timer/1, stop_ms_timer/2, stop_ms_timer/1, |
72 | | stop_ms_timer_with_msg/2, stop_ms_walltimer_with_msg/2, |
73 | | bt_start_ms_timer/1, bt_stop_ms_timer/1, |
74 | | get_elapsed_walltime/2, |
75 | | cputime/1, walltime/1, |
76 | | |
77 | | retract_with_statistics/2, |
78 | | get_memory_used/1, print_memory_used_difference/2, print_memory_used/1, |
79 | | print_memory_used_wo_gc/0, |
80 | | |
81 | | split_list/4, |
82 | | split_list_idx/5, re_split_list_idx/4, |
83 | | foldl/4,foldl/5,foldl/6, |
84 | | average/2, |
85 | | |
86 | | (space_call)/1, |
87 | | assert_once/1, |
88 | | |
89 | | % safe_time_out/3, % now in tools_meta |
90 | | % time_out_call/2, time_out_call/1, time_out_with_factor_call/3, % now in tools_timeout |
91 | | |
92 | | unique_id/2, |
93 | | |
94 | | get_PROBPATH/1 |
95 | | ]). |
96 | | |
97 | | :- use_module(module_information). |
98 | | |
99 | | :- module_info(group,infrastructure). |
100 | | :- module_info(description,'This module contains many general helper predicates.'). |
101 | | |
102 | | :- use_module(pathes,[runtime_application_path/1]). % we just import it to set-up pathes, we don't need any predicates |
103 | | |
104 | | :- use_module(library(lists)). |
105 | | :- use_module(library(system)). |
106 | | %%:- use_module(library(file_systems)). %% not required ? |
107 | | %% :- use_module(library(codesio)). |
108 | | |
109 | | |
110 | | :- use_module(tools_meta,[reraise_important_exception/1, safe_on_exception/3]). |
111 | | |
112 | | |
113 | | :- meta_predicate catch_call(0). |
114 | | catch_call(Call) :- |
115 | | on_exception(Exception,call(Call), |
116 | | (add_error(catch_call,'Call raised an exception: ',(Call:Exception)), |
117 | | /* read(_), */ |
118 | | reraise_important_exception(Exception), |
119 | | fail)). |
120 | | |
121 | | |
122 | | |
123 | | :- use_module(self_check). % put after search paths have been set |
124 | | |
125 | | % -------------------------------------- |
126 | | |
127 | | cputime(T) :- |
128 | | statistics(runtime,[T,_]). |
129 | | |
130 | | walltime(WT) :- |
131 | | statistics(walltime,[WT,_]). |
132 | | % -------------------------------------- |
133 | | |
134 | | platform_is_64_bit :- |
135 | | prolog_flag(max_tagged_integer,X), |
136 | | X >= 1152921504606846975. |
137 | | |
138 | | % equals 268435455 on 32-bit systems, i.e., 2**28 - 1 |
139 | | % equals 1152921504606846975 on 64-bit systems |
140 | | max_tagged_integer(X) :- prolog_flag(max_tagged_integer,X). |
141 | | |
142 | | max_tagged_pow2(Exp) :- |
143 | | prolog_flag(max_tagged_integer,X), |
144 | | (X >= 1152921504606846975 -> Exp = 60 % we can represent 2**60 |
145 | | ; Exp = 27). % assume 32 bit |
146 | | |
147 | | is_tagged_integer(X) :- prolog_flag(max_tagged_integer,Lim), X =< Lim, |
148 | | prolog_flag(min_tagged_integer,Low), X >= Low. |
149 | | |
150 | | host_platform(Res) :- |
151 | | prolog_flag(host_type,HostType), |
152 | | atom_codes(HostType,AsciiList), |
153 | ? | map_host_platform(AsciiList,Res),!. |
154 | | |
155 | | map_host_platform(HT,darwin) :- append("powerpc-darwin",_,HT). |
156 | | map_host_platform(HT,darwin) :- append("i386-darwin",_,HT). |
157 | | map_host_platform(HT,darwin) :- append("x86_64-darwin",_,HT). |
158 | | map_host_platform(HT,windows) :- append("x86-win",_,HT). |
159 | | map_host_platform(HT,windows) :- append("x86_64-win",_,HT). |
160 | | map_host_platform(HT,linux) :- append("x86-linux",_,HT). |
161 | | map_host_platform(HT,linux) :- append("x86_64-linux",_,HT). |
162 | | map_host_platform(_,unknown). |
163 | | |
164 | | % on Windows XP: 'x86-win32-nt-4' |
165 | | % on MacPro: 'x86_64-darwin-10.6.0' |
166 | | % on Linux: 'x86-linux-glibc2.7' |
167 | | |
168 | | |
169 | | :- use_module(error_manager,[add_error/3, add_internal_error/2]). |
170 | | print_message(Msg) :- print_message_with_max_depth(Msg,20). |
171 | | print_message_with_max_depth(Msg,MaxDepth) :- |
172 | | safe_on_exception(E,print_message2(Msg,MaxDepth), |
173 | | add_error(tools,'Exception in print_message: ',E)). % added because sometimes in Windows/Vista we get an exception here |
174 | | print_message2(Msg,MaxDepth) :- |
175 | | current_output(X), |
176 | | set_output(user), |
177 | | (var(Msg) -> print_message(informational,'_') |
178 | | ; write('% '),write_term(Msg,[max_depth(MaxDepth)]),nl ), |
179 | | set_output(X). |
180 | | print_short_msg(Msg) :- |
181 | | current_output(X), |
182 | | set_output(user), |
183 | | write(Msg), |
184 | | set_output(X). |
185 | | |
186 | | print_bt_message(Msg) :- print_message(Msg). |
187 | | print_bt_message(Msg) :- print_message(backtrack(Msg)), |
188 | | %(Msg = found_enumeration_of_constants(_,_) -> trace ; true), |
189 | | fail. |
190 | | |
191 | | % like print_bt_message but trace upon backtrack |
192 | | print_bt_trace_message(Msg) :- print_message(Msg). |
193 | | print_bt_trace_message(Msg) :- trace, |
194 | | print_message(backtrack(Msg)), |
195 | | fail. |
196 | | |
197 | | % trace upon backtrack: |
198 | | bt_trace(_) :- true. |
199 | | bt_trace(PP) :- print(' * BACKTRACK: '), print(PP),nl, trace,fail. |
200 | | |
201 | | print_wtime(PP) :- statistics(walltime,[WT,_]), |
202 | | current_output(X), |
203 | | set_output(user), |
204 | | print(PP), print(' : '), print(WT), print(' ms'),nl, |
205 | | set_output(X). |
206 | | |
207 | | % a print that will automatically stop after 25 prints and give the user the option to inspect the printed messages |
208 | | :- dynamic prints_count/1. |
209 | | prints_count(25). |
210 | | prints(L) :- print_bt_message(L), |
211 | | (retract(prints_count(X)) -> true ; X=25), |
212 | | (X<1 |
213 | | -> print('*** Stopped printing >'), |
214 | | read(RT), |
215 | | (number(RT) -> X1=RT ; X1=25) |
216 | | ; X1 is X-1 |
217 | | ), assert(prints_count(X1)). |
218 | | |
219 | | :- assert_pre(tools:exact_member(_Var,Vs), |
220 | | (list_skeleton(Vs))). |
221 | | :- assert_post(tools:exact_member(_Var,_Vs), true). |
222 | | :- assert_must_succeed(tools:exact_member(V,[V])). |
223 | | :- assert_must_succeed(tools:exact_member(V,[_X,_Z,V])). |
224 | | :- assert_must_fail(tools:exact_member(_W,[_X,_Z,_V])). |
225 | | :- assert_must_fail(tools:exact_member(_W,[])). |
226 | | |
227 | | exact_member(X,[Y|T]) :- |
228 | | (X==Y -> true ; exact_member(X,T)). |
229 | | |
230 | | |
231 | | :- assert_pre(tools:exact_member_lookup(_Var,_ValRes,Vs,Vals), |
232 | | (list_skeleton(Vs),list_skeleton(Vals))). |
233 | | :- assert_post(tools:exact_member_lookup(_Var,_ValRes,_Vs,_Vals), true). |
234 | | :- assert_must_succeed(tools:exact_member_lookup(V,2,[V],[2])). |
235 | | :- assert_must_succeed(tools:exact_member_lookup(V,2,[_X,_Z,V],[1,3,2])). |
236 | | :- assert_must_fail(tools:exact_member_lookup(V,3,[_X,_Z,V],[1,3,2])). |
237 | | :- assert_must_fail(tools:exact_member_lookup(_W,3,[_X,_Z,_V],[1,3,2])). |
238 | | :- assert_must_fail(tools:exact_member_lookup(_W,3,[],[1,3,2])). |
239 | | |
240 | | exact_member_lookup(Var,ValRes,[V|TV],[Val|TVal]) :- |
241 | | (Var==V -> ValRes=Val ; exact_member_lookup(Var,ValRes,TV,TVal)). |
242 | | |
243 | | |
244 | | :- assert_pre(tools:exact_member_replace(_Var,_ValRes,Vs,Vals,_), |
245 | | (list_skeleton(Vs),list_skeleton(Vals))). |
246 | | :- assert_post(tools:exact_member_replace(_Var,_ValRes,_Vs,_Vals,NewVals), |
247 | | list_skeleton(NewVals)). |
248 | | :- assert_must_succeed(tools:exact_member_replace(V,44,[_X,_Z,V],[1,3,2],[1,3,44])). |
249 | | :- assert_must_fail(tools:exact_member_replace(_V,44,[_X,_Z,_VV],[1,3,2],[1,3,44])). |
250 | | |
251 | | exact_member_replace(Var,NewVal,[V|TV],[Val|TVal],[NV|TN]) :- |
252 | | ((Var==V) -> (NV=NewVal,TN=TVal) |
253 | | ; (NV=Val,exact_member_replace(Var,NewVal,TV,TVal,TN))). |
254 | | |
255 | | |
256 | | |
257 | | remove([X|T],X,T). |
258 | ? | remove([Y|T],X,[Y|DT]) :- \+(X=Y), remove(T,X,DT). |
259 | | |
260 | | :- assert_must_succeed(tools:flatten([],[])). |
261 | | :- assert_must_succeed(tools:flatten([[1,2,3]],[1,2,3])). |
262 | | :- assert_must_succeed(tools:flatten([[1],[[99]],[2,3],[4,5]],[1,99,2,3,4,5])). |
263 | | :- assert_must_succeed(tools:flatten([[]],[])). |
264 | | |
265 | | flatten(List,FlatList) :- flatten1(List,[],FlatList). |
266 | | flatten1([],L,L) :- !. |
267 | | flatten1([H|T],Tail,List) :- !, flatten1(H,FlatList,List), flatten1(T,Tail,FlatList). |
268 | | flatten1(NonList,Tail,[NonList|Tail]). |
269 | | |
270 | | |
271 | | :- use_module(tools_lists,[count_occurences/2]). |
272 | | :- assert_must_succeed((tools_lists:count_occurences([a,b,a,a,b],R),R == [a-3,b-2])). |
273 | | |
274 | | |
275 | | |
276 | | :- meta_predicate filter(1,*,*,*). |
277 | | :- assert_must_succeed((tools:filter(var,[1,2,X,3,Y],R,Out),R == [X,Y],Out == [1,2,3])). |
278 | | :- assert_must_succeed((tools:filter(nonvar,[1,2,X,3,Y],R,Out),R == [1,2,3],Out == [X,Y])). |
279 | | |
280 | | filter(_Pred,[],[],[]). |
281 | | filter(Pred,[H|T],True,False) :- |
282 | | (call(Pred,H) -> True = [H|TT], filter(Pred,T,TT,False) |
283 | | ; False = [H|FF], filter(Pred,T,True,FF)). |
284 | | |
285 | | :- public is_a_comment/3. % used for testing filter |
286 | | :- assert_must_succeed((tools: filter(tools:is_a_comment('/*','*/'),['This comment ','/* comment */','will be ignored!'],R,Out), |
287 | | R==['/* comment */'],Out==['This comment ','will be ignored!'])). |
288 | | % checking whether an atom is a comment /* ... */ |
289 | | is_a_comment(Begin,End,Comment) :- |
290 | | maplist(atom_codes,[Begin,End,Comment],[BL,EL,CL]), |
291 | | prefix(CL,BL),suffix(CL,EL). |
292 | | |
293 | | :- assert_must_succeed((tools:remove_variables([X,Y,Z],[Y],R),R==[X,Z])). |
294 | | remove_variables(List,Vars,Remaining) :- |
295 | | exclude(exact_member_rev(Vars),List,Remaining). |
296 | | % a version of exact member with reversed parameters - usefull with |
297 | | % higher-order functions |
298 | | exact_member_rev(List,Member) :- exact_member(Member,List). |
299 | | |
300 | | :- assert_must_succeed(tools:insert([],a,[a])). |
301 | | :- assert_must_succeed(tools:insert([a,b,c,d],a,[a,b,c,d])). |
302 | | :- assert_must_succeed(tools:insert([a,b,c,d],b,[a,b,c,d])). |
303 | | :- assert_must_succeed(tools:insert([a,b,c,d],d,[a,b,c,d])). |
304 | | :- assert_must_succeed(tools:insert([a,b,c,d],x,[a,b,c,d,x])). |
305 | | insert([],X,[X]). |
306 | | insert([H|T],X,R) :- (H=X -> R=[H|T] ; R=[H|R2],insert(T,X,R2)). |
307 | | |
308 | | |
309 | | :- assert_must_succeed(tools:list_intersection([a,b,c,d],[d,f,b],[b,d])). |
310 | | |
311 | | list_intersection([],_L,[]). |
312 | | list_intersection([H|T],L,Res) :- |
313 | ? | (remove(L,H,NL) -> (Res=[H|RR]) ; (Res=RR,NL=L)), |
314 | | list_intersection(T,NL,RR). |
315 | | |
316 | | insert_new([],X,[X]). |
317 | | insert_new([H|T],X,R) :- (H=X -> fail ; R=[H|R2],insert_new(T,X,R2)). |
318 | | |
319 | | :- assert_must_succeed(tools:disjoint_list_union([a,b,c,d],[e,f,g],[e,f,g,a,b,c,d])). |
320 | | :- assert_must_fail(tools:disjoint_list_union([a,b,c,d],[d,f,b],_)). |
321 | | :- assert_must_fail(tools:disjoint_list_union([a,b,c,d],[d,f,b],[d,f,b,a,c])). |
322 | | disjoint_list_union([],L,L). |
323 | | disjoint_list_union([H|T],L,Res) :- insert_new(L,H,L2), disjoint_list_union(T,L2,Res). |
324 | | |
325 | | :- assert_must_succeed(tools:lists_are_disjoint([a,b,c,d],[e,f,g])). |
326 | | :- assert_must_succeed(tools:lists_are_disjoint([a,b,c,d],[])). |
327 | | :- assert_must_succeed(tools:lists_are_disjoint([],[e,f,g])). |
328 | | :- assert_must_fail(tools:lists_are_disjoint([a,b,c,d],[e,f,g,d])). |
329 | | lists_are_disjoint([],_). |
330 | | lists_are_disjoint([H|T],List2) :- \+ member(H,List2), lists_are_disjoint(T,List2). |
331 | | |
332 | | :- assert_must_succeed(tools:list_difference([a,b,c,d],[b,f,d],[a,c])). |
333 | | |
334 | | list_difference([],_L,[]). |
335 | | list_difference([H|T],L,Res) :- |
336 | | (remove(L,H,NL) -> (Res=RR) ; (Res=[H|RR],NL=L)), |
337 | | list_difference(T,NL,RR). |
338 | | |
339 | | :- use_module(tools_strings,[string_concatenate/3]). |
340 | | :- assert_must_succeed(( tools_strings:string_concatenate('5','.10',R), R=='5.10' )). |
341 | | |
342 | | |
343 | | |
344 | | |
345 | | |
346 | | :- assert_must_succeed(tools:is_upper_case_name('GOODS')). |
347 | | :- assert_must_succeed(tools:is_upper_case_name('ZZAA')). |
348 | | :- assert_must_fail(tools:is_upper_case_name('capacity')). |
349 | | :- assert_must_fail(tools:is_upper_case_name('PARAs')). |
350 | | |
351 | | :- use_module(tools_strings,[safe_name/2]). |
352 | | is_upper_case_name(Name) :- |
353 | | safe_name(Name,AsciiList), |
354 | | upper_case_list(AsciiList). |
355 | | |
356 | | upper_case_list([]). |
357 | | upper_case_list([H|T]) :- H >="A", H=<"Z", upper_case_list(T). |
358 | | |
359 | | |
360 | | |
361 | | :- assert_must_succeed(tools:is_absolute_path('/aaaa/bbb/cc/d.app')). |
362 | | :- assert_must_fail(tools:is_absolute_path('cc/d.app')). |
363 | | :- assert_must_fail(tools:is_absolute_path('./cc/')). |
364 | | is_absolute_path(Path) :- |
365 | | atom_chars(Path,PathAscii), |
366 | | PathAscii = ['/'|_]. % TO DO: add other rules for windows C: ... |
367 | | |
368 | | :- assert_must_succeed(tools:get_parent_directory('/aaaa/bbb/cc/d.app','/aaaa/bbb/cc/')). |
369 | | :- assert_must_succeed(tools:get_parent_directory('/aaaa/bbb/cc/','/aaaa/bbb/cc/')). |
370 | | :- assert_must_succeed(tools:get_parent_directory('d.app','')). |
371 | | |
372 | | get_parent_directory(Path,NewPath) :- |
373 | | atom_chars(Path,PathAscii), |
374 | | strip_last(PathAscii,[],[],New), |
375 | | atom_chars(NewPath,New). |
376 | | |
377 | | :- use_module(library(lists)). |
378 | | |
379 | | strip_last([],ResSoFar,_,Res) :- reverse(ResSoFar,Res). |
380 | | strip_last(['/'|Tail],ResSoFar,StripSoFar,Res) :- !, |
381 | | append(['/'|StripSoFar],ResSoFar,NewRes), |
382 | | strip_last(Tail,NewRes,[],Res). |
383 | | strip_last([A|Tail],ResSoFar,StripSoFar,Res) :- |
384 | | strip_last(Tail,ResSoFar,[A|StripSoFar],Res). |
385 | | |
386 | | :- assert_must_succeed((tools:split_atom('ef,g',[','],R), R==[ef,g])). |
387 | | :- assert_must_succeed((tools:split_atom('ef, g',[',',' '],R), R==[ef,g])). |
388 | | :- assert_must_succeed((tools:split_atom('ab,cd,ef,g',[','],R), R==['ab','cd',ef,g])). |
389 | | :- assert_must_succeed((tools:split_atom('ab,cd,ef;g',[',',';'],R), R==['ab','cd',ef,g])). |
390 | | |
391 | | split_atom(Atom,SepList,SplitList) :- |
392 | | atom_chars(Atom,ListAscii), |
393 | | split2(ListAscii,SepList,SplitList). |
394 | | |
395 | | split2([],_,R) :- !,R=[]. |
396 | | split2(List,Sep,Res) :- get_next_word(List,Sep,Word,Tail),!, |
397 | | (Word=[] -> split2(Tail,Sep,Res) |
398 | | ; Res=[Atom|TA], atom_chars(Atom,Word), split2(Tail,Sep,TA)). |
399 | | |
400 | | get_next_word([],_Sep,[],[]). |
401 | | get_next_word([H|T],Sep,Word,Tail) :- |
402 | | member(H,Sep) -> Word=[],Tail=T |
403 | | ; Word=[H|TR], get_next_word(T,Sep,TR,Tail). |
404 | | |
405 | | |
406 | | |
407 | | :- assert_must_succeed((tools:split_complex_sep("ef,,g,h",",,",R), R==["ef","g,h"])). |
408 | | % a version of split that allows longer seperators, % TO DO: make more efficient |
409 | | split_complex_sep(L,Sep,[First|Rest]) :- append(Sep,LRest,SepR), |
410 | | append(First,SepR,L), |
411 | | !, |
412 | | split_complex_sep(LRest,Sep,Rest). |
413 | | split_complex_sep(H,_,[H]). |
414 | | |
415 | | |
416 | | :- assert_must_succeed((tools:split_chars("ef,g",",",R), R==["ef","g"])). |
417 | | :- assert_must_succeed((tools:split_chars("10",".",R), R==["10"])). |
418 | | :- assert_must_succeed((tools:split_chars("",".",R), R==[""])). |
419 | | :- assert_must_succeed((tools:split_chars("1.0",".",R), R==["1","0"])). |
420 | | :- assert_must_succeed((tools:split_chars("1.",".",R), R==["1",""])). |
421 | | :- assert_must_succeed((tools:split_chars(".1",".",R), R==["","1"])). |
422 | | |
423 | | split_chars(List,Sep,Res) :- get_next_word_until_sep(List,Sep,Word,Tail),!, |
424 | | Res=[Word|TA], |
425 | | split_chars(Tail,Sep,TA). |
426 | | split_chars(List,_,[List]). |
427 | | |
428 | | get_next_word_until_sep([H|T],Sep,Word,Tail) :- |
429 | | member(H,Sep) -> Word=[],Tail=T |
430 | | ; Word=[H|TR], get_next_word_until_sep(T,Sep,TR,Tail). |
431 | | |
432 | | :- assert_must_succeed(tools:split_last('/aaaa/bbb/cc/d.app','/','/aaaa/bbb/cc','d.app')). |
433 | | :- assert_must_succeed(tools:split_last('/aaaa/bbb/cc/d.app','.','/aaaa/bbb/cc/d','app')). |
434 | | split_last(Atom, Sep, Head, Tail) :- \+ atom(Sep),!, |
435 | | add_internal_error('Separator not an atom: ', split_last(Atom, Sep, Head, Tail)),fail. |
436 | | split_last(Atom, Sep, Head, Tail) :- atom_chars(Sep,SepACodes), |
437 | | split_last_lst(Atom,SepACodes,Head,Tail). |
438 | | |
439 | | |
440 | | :- assert_must_succeed(tools:split_last_lst('/aaaa/bbb;cc/d.app',['/',';'],'/aaaa/bbb;cc','d.app')). |
441 | | :- assert_must_succeed(tools:split_last_lst('/aaaa/bbb/cc/d.app',['.'],'/aaaa/bbb/cc/d','app')). |
442 | | % a list version of split_last: obtains a list of sperator chars |
443 | | split_last_lst(Atom, Seps, Head, Tail) :- \+ atom(Atom),!, |
444 | | add_internal_error('First arg not an atom: ', split_last_lst(Atom, Seps, Head, Tail)),fail. |
445 | | split_last_lst(Atom, Seps, Head, Tail) :- |
446 | | atom_chars(Atom,ListAscii), |
447 | | split_last2_lst(ListAscii,Seps,[],[],HeadA, TailA), |
448 | | atom_chars(Head,HeadA), atom_chars(Tail,TailA). |
449 | | |
450 | | split_last2_lst([],_,CurSplit,[_|Head],ResH,ResT) :- |
451 | | reverse(CurSplit,ResT), |
452 | | reverse(Head,ResH). |
453 | ? | split_last2_lst([Sep|Tail],Seps,CurSplit,Head,ResH,ResT) :- member(Sep,Seps), % TO DO: use ord_member ? |
454 | | !, |
455 | | append([Sep|CurSplit],Head,NewHead), |
456 | | split_last2_lst(Tail,Seps,[],NewHead,ResH,ResT). |
457 | | split_last2_lst([H|Tail],Seps,CurSplit,Head,ResH,ResT) :- |
458 | | split_last2_lst(Tail,Seps,[H|CurSplit],Head,ResH,ResT). |
459 | | |
460 | | |
461 | | |
462 | | :- assert_must_succeed(tools:split_filename('/aaaa/bbb/cc/d.app','/aaaa/bbb/cc/d','app')). |
463 | | :- assert_must_succeed((Z='/aaaa/bbb/cc/d',tools:split_filename(Z,R,X),X=='',R==Z)). |
464 | | |
465 | | split_filename(Filename,Base,Ext) :- |
466 | | (split_last(Filename,'.',Base,Ext) -> true ; Base=Filename,Ext=''). |
467 | | |
468 | | |
469 | | :- assert_must_succeed(tools:get_tail_filename('/aaaa/bbb/cc/d.app','d.app')). |
470 | | :- assert_must_succeed(tools:get_tail_filename('\\aaaa\\bbb\\c\\d.app','d.app')). |
471 | | :- assert_must_succeed(tools:get_tail_filename('d.app','d.app')). |
472 | | :- assert_must_succeed(tools:get_tail_filename('/aaaa/bbb/cc/','')). |
473 | | get_tail_filename(Path,Tail) :- compound(Path),!, |
474 | | add_internal_error('Not a filename: ',get_tail_filename(Path,Tail)), |
475 | | Tail=Path. |
476 | | get_tail_filename(Path,Tail) :- (split_last_lst(Path, ['/','\\'], _, T) -> Tail=T ; Tail=Path). |
477 | | |
478 | | :- assert_must_succeed(tools:get_modulename_filename('/aaaa/bbb/cc/d.app','d')). |
479 | | :- assert_must_succeed(tools:get_modulename_filename('d.app','d')). |
480 | | get_modulename_filename(Path,Module) :- |
481 | | get_tail_filename(Path,Tail), |
482 | | (split_last(Tail, '.', M, _) -> Module=M ; Module=other). |
483 | | |
484 | | |
485 | | :- assert_must_succeed(get_filename_extension('/aaaa/bbb/cc/d.app','app')). |
486 | | get_filename_extension(Path,Ext) :- split_filename(Path,_,Ext). |
487 | | |
488 | | % also works if numbers passed (can happen by accident in test_runner ...) |
489 | | % absolute_file_name('$a',X) generates a permission error |
490 | | safe_absolute_file_name(F,AF,Options) :- |
491 | | ensure_atom(F,A), |
492 | | on_exception(error(permission_error(_,_,_),ERR), |
493 | | absolute_file_name(A,AF,Options), |
494 | | (format('*** Permission Error for absolute_file_name: ~w~n',[ERR]),AF=F)). |
495 | | |
496 | | safe_absolute_file_name(F,AF) :- safe_absolute_file_name(F,AF,[]). |
497 | | |
498 | | |
499 | | |
500 | | %******************************************************************************* |
501 | | % remove_all(A,B,Result): Result is the list of elements of A which do not occur in B |
502 | | remove_all([],_,[]). |
503 | | remove_all([H|T],Remove,Result) :- |
504 | ? | (member(H,Remove) -> !,Result = Rest ; Result = [H|Rest]), |
505 | | remove_all(T,Remove,Rest). |
506 | | |
507 | | %******************************************************************************* |
508 | | % get_options/4 for parsing command line arguments |
509 | | |
510 | | :- meta_predicate get_options(+,4,-,-). |
511 | | :- meta_predicate get_options(+,4,-,-,0). |
512 | | |
513 | | get_options(List,Pred,Options,Rest) :- |
514 | | get_options(List,Pred,Options,Rest,halt). |
515 | | get_options([],_,[],[],_). |
516 | | get_options([X|T],Module:Recognised,Options,Args,HALTCMD) :- |
517 | ? | ( call(Module:Recognised,X,Opt,Values,Action) |
518 | | -> |
519 | | ( append(Values, Rest, T) -> true |
520 | | ; otherwise -> |
521 | | length(Values,Len), |
522 | | length(T,TLen), |
523 | | (TLen < Len |
524 | | -> format('Command ~w expects ~w argument(s); ~w provided.~n',[X,Len,TLen]) |
525 | | ; format('~nInvalid argument(s) for option: ~w.~n',[X]) % will never happen ?? |
526 | | ), |
527 | | HALTCMD), |
528 | | ( call(Action) -> true |
529 | | ; otherwise -> |
530 | | format('~nInvalid argument(s) for option ~w : ~w.~n',[X,Values]), |
531 | | HALTCMD), |
532 | | RT = Rest, |
533 | | Options = [Opt|OT], Args = AT |
534 | | ; otherwise -> % option not recognised, keep in Args list (for probcli these are assumed to be files) |
535 | | Options = OT, Args = [X|AT], |
536 | | RT = T |
537 | | ), |
538 | | get_options(RT,Module:Recognised,OT,AT,HALTCMD). |
539 | | |
540 | | arg_is_number(Arg,Nr) :- number(Arg),!,Nr=Arg. |
541 | | arg_is_number(Arg,Nr) :- atom(Arg),atom_codes(Arg,Str),safe_number_codes(Nr,Str),number(Nr). |
542 | | arg_is_integer(Arg,Nr) :- |
543 | | ( append("-",SPos,Arg) -> % negative number |
544 | | arg_is_number(SPos,Pos), |
545 | | Nr is -Pos |
546 | | ; otherwise -> |
547 | | arg_is_number(Arg,Nr)). |
548 | | |
549 | | % utilities for command-line arguments |
550 | | |
551 | | arg_is_number_or_wildcard('_',R) :- !, R=_. |
552 | | arg_is_number_or_wildcard(Arg,N) :- arg_is_number(Arg,N). |
553 | | |
554 | | |
555 | | :- use_module(error_manager,[add_warning/3]). |
556 | | check_filename_arg(File,Command) :- tools:arg_is_number(File,_),!, |
557 | | ajoin(['File argument to -',Command,' is a number: '],Msg), |
558 | | add_warning(Command,Msg,File). |
559 | | check_filename_arg(File,Command) :- atom(File),atom_concat('-',_,File),!, |
560 | | ajoin(['File argument to -',Command,' starts with a hypen: '],Msg), |
561 | | add_warning(Command,Msg,File). |
562 | | check_filename_arg(_,_). |
563 | | |
564 | | |
565 | | :- assert_must_succeed( tools:convert_list_into_pairs([a],a)). |
566 | | :- assert_must_succeed( (tools:convert_list_into_pairs([a,b,c],R), R = ((a,b),c) )). |
567 | | |
568 | | convert_list_into_pairs([X|T],Res) :- conv2(T,X,Res). |
569 | | convert_list_into_pairs([],[]). |
570 | | conv2([],X,X). |
571 | | conv2([X|T],Acc,Res) :- conv2(T,(Acc,X),Res). |
572 | | |
573 | | :- assert_must_succeed(( tools:convert_pairs_into_list([x],a,R), R==[a] )). |
574 | | %:- assert_must_succeed(( b_interpreter:convert_pairs_into_list([x,y,z],((a,b),c),R), R == [a,b,c] )). |
575 | | :- assert_must_succeed(( tools:convert_pairs_into_list([x,y,z],(a,(b,c)),R), R == [a,b,c] )). |
576 | | :- assert_must_succeed(( tools:convert_pairs_into_list([x,y,z],A,B),nonvar(A),nonvar(B),A=((_,_),_),B=[_,_,_] )). |
577 | | |
578 | | % the first argument just indicates the length of the list |
579 | | convert_pairs_into_list([_],X,R) :- !,R=[X]. |
580 | | convert_pairs_into_list([_|Guide],(A,B),[A|Rest]) :- |
581 | | convert_pairs_into_list(Guide,B,Rest). |
582 | | |
583 | | |
584 | | |
585 | | |
586 | | /* ex: substitute(1, [1,2,3,4], 5, X). */ |
587 | | :- assert_must_succeed(( tools:substitute(1, [1,2,3,4], 5, X), X==[5,2,3,4])). |
588 | | substitute(X,L,Y,Res) :- sub(L,X,Y,Res). |
589 | | sub([],_,_,[]). |
590 | | sub([H|T],X,Y,[SH|ST]) :- |
591 | | (H=X -> SH=Y ; SH=H), |
592 | | sub(T,X,Y,ST). |
593 | | |
594 | | |
595 | | /* Checks if the argument is a list, but unlike is_list/1 it just |
596 | | checks the head and does not iterate through the list */ |
597 | | is_list_simple([]). |
598 | | is_list_simple([_|_]). |
599 | | |
600 | | :- use_module(tools_strings,[ajoin/2, ajoin_with_sep/3, ajoin_with_limit/3]). |
601 | | % tests are stored here to avoid cyclic module dependencies |
602 | | :- assert_must_succeed((tools_strings: ajoin_with_sep([link,a,xa],'.',Text), Text == 'link.a.xa')). |
603 | | :- assert_must_succeed((tools_strings: ajoin_with_sep([link],'.',Text), Text == 'link')). |
604 | | |
605 | | :- assert_must_succeed((tools_strings: ajoin_with_limit(['A','B','C','D'],100,Text), Text == 'ABCD')). |
606 | | :- assert_must_succeed((tools_strings: ajoin_with_limit(['A','B','C','D'],2,Text), Text == 'AB...')). |
607 | | |
608 | | |
609 | | :- use_module(error_manager,[add_error_and_fail/3]). |
610 | | safe_univ(Term,List) :- nonvar(Term),!,Term=..List. |
611 | | safe_univ(Term,List) :- var(List), !,add_error_and_fail(tools,'Arguments to safe_univ (=..) both var:', safe_univ(Term,List)). |
612 | | safe_univ(Term,List) :- %Term is a variable |
613 | | prolog_flag(max_arity,MA), cut_off_list(List,MA,CL), |
614 | | !, % avoid pending choice points |
615 | | Term =.. CL. |
616 | | |
617 | | |
618 | | % a version of safe_univ which does not remove args; just puts the extra arguments into the last arg |
619 | | |
620 | | safe_univ_no_cutoff(Term,List) :- nonvar(Term),!,Term=..List. |
621 | | safe_univ_no_cutoff(Term,List) :- var(List), !, |
622 | | add_error_and_fail(tools,'Arguments to safe_univ (=..) both var:', safe_univ_no_cutoff(Term,List)). |
623 | | safe_univ_no_cutoff(Term,List) :- %Term is a variable |
624 | ? | prolog_flag(max_arity,MA), squash_list(List,MA,CL), |
625 | | !, % avoid pending choice points |
626 | | Term =.. CL. |
627 | | squash_list([],_,[]). |
628 | | squash_list([H|T],MA,R) :- (MA<3,T\=[] -> R=[H,T] ; R=[H|TR],MA1 is MA-1, squash_list(T,MA1,TR)). |
629 | | |
630 | | safe_atom_chars(A,B,Loc) :- |
631 | | on_exception(error(E1,E2),atom_chars(A,B), |
632 | | (add_internal_error('atom_chars error: ',Loc:E1), |
633 | | throw(error(E1,E2)))). |
634 | | |
635 | | :- use_module(tools_strings,[atom_codes_with_limit/2, atom_codes_with_limit/3]). |
636 | | |
637 | | safe_atom_codes(V,C) :- var(V),var(C),!, |
638 | | add_internal_error('Variables in call: ',safe_atom_codes(V,C)), C='$VARIABLE$'. |
639 | | safe_atom_codes(A,C) :- |
640 | | on_exception(error(representation_error(max_atom_length),_),atom_codes(A,C), |
641 | | (print(exception(max_atom_length)),nl,atom_codes_with_limit(A,1000,C))). |
642 | | |
643 | | safe_number_codes(V,C) :- var(V),var(C),!, |
644 | | add_internal_error('Variables in call: ',safe_number_codes(V,C)), C='$VARIABLE$'. |
645 | | safe_number_codes(A,C) :- |
646 | | on_exception(error(syntax_error(_N),_),number_codes(A,C), |
647 | | ( %print(9,syntax_error_in_number_codes(_N)),nl, |
648 | | fail)). % in this case safe_number_codes fails ; we cannot convert the codes into a number |
649 | | |
650 | | % for an identifier "x" and a number N, create a new identifier "x$N" |
651 | | number_suffix(Id,N,FullId) :- |
652 | | safe_atom_chars(Id,IdChars,number_suffix1),number_chars(N,NChars), |
653 | | append(IdChars,['$'|NChars],FullIdChars), |
654 | | safe_atom_chars(FullId,FullIdChars,number_suffix2). |
655 | | |
656 | | |
657 | | :- assert_must_succeed(ensure_atom(19,'19')). |
658 | | % ensure that numbers get converted to atoms: |
659 | | ensure_atom(Var,A) :- var(Var),!, A='_'. |
660 | | ensure_atom(N,Res) :- number(N),!,number_codes(N,C), atom_codes(A,C), Res=A. |
661 | | ensure_atom(A,A). |
662 | | |
663 | | |
664 | | :- use_module(tools_strings,[truncate_atom/3]). |
665 | | :- assert_must_succeed((tools_strings:truncate_atom(abcd,100,Text), Text == 'abcd')). |
666 | | :- assert_must_succeed((tools_strings:truncate_atom(abcd,2,Text), Text == 'ab...')). |
667 | | :- assert_must_succeed((tools_strings:truncate_atom(abcd,0,Text), Text == '...')). |
668 | | |
669 | | |
670 | | wrap_and_truncate_atom(Atom,LineLength,Limit,NewAtom) :- \+ atom(Atom),!, |
671 | | add_internal_error('Argument to wrap_and_truncate_atom not atom: ',wrap_and_truncate_atom(Atom,LineLength,Limit,NewAtom)), |
672 | | NewAtom=Atom. |
673 | | wrap_and_truncate_atom(Atom,LineLength,Limit,NewAtom) :- |
674 | | atom_codes(Atom,Codes), |
675 | | wrap_and_truncate_codes(Codes,LineLength,LineLength,Limit,NewCodes,Chng), |
676 | | (Chng=true -> atom_codes(NewAtom,NewCodes) ; NewAtom=Atom). |
677 | | |
678 | | wrap_and_truncate_codes([],_,_,_,[],false). |
679 | | wrap_and_truncate_codes([H|T],CharsOnLineCount,MaxLineLength,TotCount,Res,Chng) :- |
680 | | (TotCount<1 -> Res = [46,46,46],Chng=true /* '...' */ |
681 | | ; (CharsOnLineCount<1 -> Res=[92,110,H|TT], /* add newline \n */ |
682 | | TR=T, L1 is MaxLineLength, Chng=true |
683 | | ; H=92,T=[H2|T2] -> Res = [H,H2|TT], /* do not split escaped char */ |
684 | | TR=T2, L1 is CharsOnLineCount-1, Chng2=Chng |
685 | | ; Res = [H|TT], TR=T, L1 is CharsOnLineCount-1, Chng2=Chng), |
686 | | TC1 is TotCount-1, wrap_and_truncate_codes(TR,L1,MaxLineLength,TC1,TT,Chng2) |
687 | | ). |
688 | | |
689 | | |
690 | | cut_off_list([],_,[]). |
691 | | cut_off_list([H|T],MA,R) :- (MA<2 -> R=['...'] ; R=[H|TR],MA1 is MA-1, cut_off_list(T,MA1,TR)). |
692 | | |
693 | | |
694 | | % print_size_of_table counts the number of succeeded calls of the |
695 | | % given predicate |
696 | | get_calls_for_table(Module:P/N,Call) :- !, |
697 | | functor(Call,P,N), |
698 | | call(Module:Call). |
699 | | get_calls_for_table(P/N,Call) :- |
700 | | functor(Call,P,N), |
701 | | call(Call). |
702 | | |
703 | | :- volatile count/1. |
704 | | :- dynamic count/1. |
705 | | print_size_of_table(Pred) :- retractall(count(_)), |
706 | | assert(count(0)),get_calls_for_table(Pred,_),inc_size_of_table,fail. |
707 | | print_size_of_table(Pred) :- |
708 | | print('% size of table for '), print(Pred), print(': '), count(X), print(X),nl. |
709 | | inc_size_of_table :- retract(count(X)),X1 is X+1, assert(count(X1)). |
710 | | |
711 | | |
712 | | safe_sort(Orig,A,B) :- var(A),!, add_error(tools,'Illegal call: ',mysort(Orig,A,B)),A=B. |
713 | | safe_sort(_,A,B) :- sort(A,B). |
714 | | |
715 | | safe_functor(Src,F,A,Term) :- var(F),var(A),var(Term),!, |
716 | | add_internal_error('Illegal functor call: ',safe_functor(Src,F,A,Term)), |
717 | | fail. |
718 | | safe_functor(_,F,A,T) :- functor(F,A,T). |
719 | | |
720 | | |
721 | | print_runtime :- statistics(runtime,[Tot,SinceLast]), print(' Total runtime: '), print(Tot), |
722 | | print(' ms, since last: '), print(SinceLast), print(' ms'),nl. |
723 | | |
724 | | |
725 | | start_ms_timer(timer(R,T,W)) :- statistics(runtime,[R,_]), |
726 | | statistics(total_runtime,[T,_]), |
727 | | statistics(walltime,[W,_]). |
728 | | stop_ms_timer(T) :- stop_ms_timer(T,[runtime/RT,total_runtime/RTT,walltime/WT]), |
729 | | format('% Runtime: ~w ms (with gc: ~w ms, walltime: ~w ms)~n',[RT,RTT,WT]). |
730 | | stop_ms_timer_with_msg(T,Msg) :- stop_ms_timer(T,[runtime/RT,total_runtime/RTT,walltime/WT]), |
731 | | statistics(walltime,[WE,_]), |
732 | | format('% Runtime for ~w: ~w ms (with gc: ~w ms, walltime: ~w ms); time since start: ~w ms~n',[Msg,RT,RTT,WT,WE]). |
733 | | stop_ms_walltimer_with_msg(T,Msg) :- stop_ms_timer(T,[runtime/_RT,total_runtime/_RTT,walltime/WT]), |
734 | | format('% Walltime for ~w: ~w ms~n',[Msg,WT]). |
735 | | stop_ms_timer(timer(R,T,W),[runtime/RT,total_runtime/RTT,walltime/WT]) :-!, |
736 | | statistics(runtime,[RE,_]), |
737 | | % These refer to CPU time used while executing, excluding time spent in memory management tasks or or in system calls. |
738 | | statistics(total_runtime,[TE,_]), |
739 | | % These refer to total CPU time used while executing, including memory management tasks such as garbage collection but excluding system calls. |
740 | | statistics(walltime,[WE,_]), |
741 | | % These refer to absolute time elapsed. |
742 | | RT is RE-R, RTT is TE-T, WT is WE-W. |
743 | | stop_ms_timer(X,Y) :- |
744 | | add_internal_error('Illegal call: ', stop_ms_timer(X,Y)), |
745 | | Y = []. |
746 | | get_elapsed_walltime(timer(_R,_T,W),WTot) :- |
747 | | statistics(walltime,[W2,_]), WTot is W2-W. |
748 | | |
749 | | % a timer that measures backtracking times |
750 | | :- dynamic last_bt_timer/2. |
751 | | bt_start_ms_timer(Msg) :- retractall(last_bt_timer(Msg,_)), |
752 | | start_ms_timer(T), assert(last_bt_timer(Msg,T)). |
753 | | bt_start_ms_timer(Msg) :- retract(last_bt_timer(Msg,Timer)), |
754 | | stop_ms_timer(Timer,[runtime/RT,total_runtime/_RTT,walltime/WT]), |
755 | | format('% Runtime to FINALISE ~w: ~w ms (walltime: ~w ms)~n',[Msg,RT,WT]), |
756 | | fail. |
757 | | |
758 | | |
759 | | bt_stop_ms_timer(Msg) :- |
760 | | retract(last_bt_timer(Msg,Last)), |
761 | | stop_ms_timer(Last,[runtime/RT,total_runtime/_RTT,walltime/WT]), |
762 | | format('% Runtime for SOLUTION for ~w: ~w ms (walltime: ~w ms)~n',[Msg,RT,WT]). |
763 | | bt_stop_ms_timer(Msg) :- start_ms_timer(BT_Timer), |
764 | | assert(last_bt_timer(Msg,BT_Timer)), |
765 | | fail. |
766 | | % --------------------------- |
767 | | |
768 | | |
769 | | retract_with_statistics(Module,ListOfFacts) :- |
770 | | nl,print('Retracting Facts'),nl, |
771 | | get_memory_used(M), |
772 | | print(' Memory usage: '),print_memory_used(M),nl, |
773 | | retract_with_statistics(Module,ListOfFacts,M). |
774 | | |
775 | | :- meta_predicate retract_all_count(0,-,-). |
776 | | |
777 | | retract_with_statistics(_Module,[],_) :- nl,nl. |
778 | | retract_with_statistics(Module,[Fact|T],Mem) :- |
779 | | format('~w : ',[Fact]), |
780 | | %retractall(Module:Fact), |
781 | | retract_all_count(Module:Fact,0,Nr), format(' ~w facts : ',[Nr]), |
782 | | get_memory_used(NewMem), |
783 | | print_memory_used(NewMem), |
784 | | print_memory_used_difference(Mem,NewMem),nl, |
785 | | retract_with_statistics(Module,T,NewMem). |
786 | | |
787 | | retract_all_count(Fact,Acc,Res) :- \+ (\+ (retract(Fact))),!, A1 is Acc+1, retract_all_count(Fact,A1,Res). |
788 | | retract_all_count(_,R,R). |
789 | | |
790 | | get_memory_used([M,PU]) :- garbage_collect,garbage_collect_atoms, get_memory_used_wo_gc([M,PU]). |
791 | | get_memory_used_wo_gc([M,PU]) :- statistics(program,[PU,_]),statistics(memory_used,M). |
792 | | print_memory_used_wo_gc :- get_memory_used_wo_gc(M), print_memory_used(M). |
793 | | print_memory_used([M,PU]) :- print_mb(M), print(' ('), print_mb(PU), print(' program) '). |
794 | | print_memory_used_difference([M1,_PU1],[NewM2,_PU2]) :- Diff is (M1)-(NewM2), |
795 | | (Diff >= 0 -> print(' freed: '), print_bytes(Diff) |
796 | | ; print(' allocated: '), D2 is -(Diff), print_bytes(D2)),nl. |
797 | | %print(' / '),Diff2 is (PU1)-(PU2), print_mb(Diff2). |
798 | | print_bytes(X) :- (X<50000 -> print_kb(X) ; print_mb(X)). |
799 | | print_mb(X) :- XMB is X / 1048576, |
800 | | format(' ~3f MB',[XMB]). |
801 | | print_kb(X) :- XKB is X / 1024, |
802 | | format(' ~3f KB',[XKB]). |
803 | | |
804 | | :- op(1150,fx,space_call). |
805 | | :- meta_predicate space_call(0). |
806 | | space_call(Call) :- get_memory_used(M1), |
807 | | call(Call), |
808 | | get_memory_used(M2), |
809 | | print_memory_used(M2), |
810 | | print_memory_used_difference(M1,M2),nl. |
811 | | |
812 | | % --------------------------- |
813 | | |
814 | | read_string_from_file(Filename,String) :- |
815 | | open(Filename,read,S), % utf |
816 | | read_string(S,String), |
817 | | close(S). |
818 | | |
819 | | % Encoding can be any value of text_encoding preference category: auto, 'ISO-8859-1', 'UTF-8', ... |
820 | | read_string_from_file(Filename,auto,String) :- !, read_string_from_file(Filename,String). |
821 | | read_string_from_file(Filename,Encoding,String) :- |
822 | | open(Filename,read,S,[encoding(Encoding)]), |
823 | | read_string(S,String), |
824 | | close(S). |
825 | | |
826 | | read_string(S,String) :- |
827 | | get_code(S,C),!, |
828 | | (C= -1 |
829 | | -> String = [] |
830 | | ; String=[C|Rest],read_string(S,Rest)). |
831 | | |
832 | | % ------------------------------ |
833 | | |
834 | | % encoding atoms for Latex: |
835 | | |
836 | | latex_escape_atom(Atom,EscAtom) :- \+ atom(Atom),!, |
837 | | add_internal_error('Cannot escape: ',latex_escape_atom(Atom,EscAtom)), |
838 | | EscAtom=Atom. |
839 | | latex_escape_atom(Atom,EscAtom) :- |
840 | | atom_codes(Atom,Codes), latex_escape_codes(Codes,ECodes), atom_codes(EscAtom,ECodes). |
841 | | |
842 | | latex_escape_codes([],[]). |
843 | | %latex_escape_codes([92,C|T],[92,C|ET]) :- !, % already escaped |
844 | | % latex_escape_codes(T,ET). |
845 | | latex_escape_codes([C|T],[92,C|ET]) :- latex_escape_code(C),!, % 95 = _ underscore, 92 = \ backslash |
846 | | latex_escape_codes(T,ET). |
847 | | latex_escape_codes([Code|T],ET) :- translate_code(Code,String), |
848 | | !, |
849 | | append(String,ET2,ET), |
850 | | latex_escape_codes(T,ET2). |
851 | | latex_escape_codes([H|T],[H|ET]) :- latex_escape_codes(T,ET). |
852 | | |
853 | | latex_escape_code(35). % # |
854 | | latex_escape_code(36). % $ |
855 | | latex_escape_code(37). % % |
856 | | latex_escape_code(38). % & |
857 | | latex_escape_code(95). % 95 = _ underscore |
858 | | latex_escape_code(123). % { |
859 | | latex_escape_code(125). % } |
860 | | |
861 | | translate_code(92,"\\textbackslash{}"). % \ % \textbackslash seems to work in both math and normal mode |
862 | | translate_code(94,"\\textasciicircum{}"). % ^ |
863 | | translate_code(126,"\\textasciitilde{}"). % ~ |
864 | | %translate_code(126,"\\~{}"). % 126 = ~ tilde -> \~{} (\sim would be alternative in math mode) |
865 | | |
866 | | % ------------------------------ |
867 | | |
868 | | % encoding atoms for B Strings: |
869 | | % escape special characters so that we can output the string between quotes "..." and obtain a valid value |
870 | | |
871 | | :- assert_must_succeed(tools:b_escape_string_atom('{"a"}','{\\"a\\"}')). |
872 | | |
873 | | b_escape_string_atom(Atom,EscAtom) :- \+ atom(Atom),!, |
874 | | add_internal_error('Cannot escape: ',b_escape_string_atom(Atom,EscAtom)), |
875 | | EscAtom=Atom. |
876 | | b_escape_string_atom(Atom,EscAtom) :- |
877 | | atom_codes(Atom,Codes), b_string_escape_codes(Codes,ECodes), atom_codes(EscAtom,ECodes). |
878 | | |
879 | | b_string_escape_codes([],[]). |
880 | | b_string_escape_codes([92,C|T],[92,C|ET]) :- % 92 = \ backslash |
881 | | \+ valid_backslash_escape(C), % we do not need to escape the \ in \x for example, but we need to escape in \n |
882 | | !, |
883 | | b_string_escape_codes(T,ET). |
884 | | b_string_escape_codes([C|T],[92,EC|ET]) :- % 92 = \ backslash |
885 | | b_escape_code(C,EC),!, |
886 | | b_string_escape_codes(T,ET). |
887 | | b_string_escape_codes([H|T],[H|ET]) :- b_string_escape_codes(T,ET). |
888 | | |
889 | | % note currently only \" is supported by the Java parser; but this will change soon |
890 | | b_escape_code(9,116). % tab, 116 = t |
891 | | b_escape_code(10,110). % newline, 110 = n |
892 | | b_escape_code(13,114). % return 114 = r |
893 | | b_escape_code(34,34). % " |
894 | | %b_escape_code(39,39). % ' % not necessary for "..." literals |
895 | | b_escape_code(92,92). % \ |
896 | | |
897 | | % these are the escape codes the parser / ProB currently supports: |
898 | | valid_backslash_escape(9). |
899 | | valid_backslash_escape(10). |
900 | | valid_backslash_escape(13). |
901 | | valid_backslash_escape(34). % " |
902 | | valid_backslash_escape(39). % ' |
903 | | valid_backslash_escape(92). % \ |
904 | | |
905 | | % ------------------------------ |
906 | | |
907 | | |
908 | | %% |
909 | | % Escape is needed for &, \/, /\, ", ', etc. |
910 | | % used mainly for dot output |
911 | | % It seems dotty do not understand escapes, neither C style nor HTML style. |
912 | | % Dot does render '&' correctly, no matter of escape. |
913 | | % |
914 | | |
915 | | print_escaped(Atom) :- string_escape(Atom,E), print(E). |
916 | | |
917 | | :- use_module(library(lists), [ |
918 | | maplist/3, |
919 | | scanlist/4]). |
920 | | |
921 | | string_escape(Number, EscapedAtom):- |
922 | | number(Number),!, number_chars(Number,C), atom_chars(EscapedAtom,C). % to detect when label=value |
923 | | string_escape(Atom, EscapedAtom):- |
924 | | atom(Atom), |
925 | | atom_chars(Atom, Chars), |
926 | | maplist(dot_string_escape_map, Chars, EscapedChars), |
927 | | scanlist(x_atom_concat_rev, EscapedChars, '', EscapedAtom),!. |
928 | | string_escape(Term, EscapedTerm):- |
929 | | Term =.. [Fkt|Args], |
930 | | string_escape(Fkt, EscapedFkt), |
931 | | maplist(string_escape, Args, EscapedArgs),!, |
932 | | EscapedTerm =.. [EscapedFkt|EscapedArgs]. |
933 | | string_escape(X,X). |
934 | | |
935 | | |
936 | | x_atom_concat_rev(A,B,BA):- |
937 | | atom_concat(B,A,BA). |
938 | | |
939 | | |
940 | | html_escape(Number, EscapedAtom):- |
941 | | number(Number),!, number_chars(Number,C), atom_chars(EscapedAtom,C). % to detect when label=value |
942 | | html_escape(Atom, EscapedAtom):- |
943 | | atom_chars(Atom, Chars), |
944 | | maplist(html_string_escape_map, Chars, EscapedChars), |
945 | | scanlist(x_atom_concat_rev, EscapedChars, '', EscapedAtom). |
946 | | |
947 | | |
948 | | dot_string_escape_map('\n', '\\n'). |
949 | | dot_string_escape_map('\\', '\\\\'). |
950 | | dot_string_escape_map('"', '\\"'). |
951 | | dot_string_escape_map('\'', '\\\''). |
952 | | dot_string_escape_map('{', '\\{'). % important if used inside dot records |
953 | | dot_string_escape_map('}', '\\}'). % important if used inside dot records |
954 | | dot_string_escape_map('|', '\\|'). % important if used inside dot records |
955 | | dot_string_escape_map('>', '\\>'). % important if used inside dot records |
956 | | dot_string_escape_map('<', '\\<'). % important if used inside dot records |
957 | | dot_string_escape_map(A,B) :- x_string_escape_map(A,B). |
958 | | |
959 | | html_string_escape_map('&', '&'). % michael: habe diese 3 Zeilen auskommentiert |
960 | | html_string_escape_map('<', '<'). |
961 | | html_string_escape_map('>', '>'). |
962 | | html_string_escape_map(A,B) :- x_string_escape_map(A,B). |
963 | | |
964 | | % this is the SICSTus encoding for Unicode either \octal_number\ or \xHexNumber\ |
965 | | % See Section 4.1.7.6 Escape Sequences in SICStus Manual (page 60) |
966 | | x_string_escape_map('\344\', 'ä'). % a mit Umlaut |
967 | | x_string_escape_map('\366\', 'ö'). % o mit Umlaut |
968 | | x_string_escape_map('\374\', 'ü'). % u mit Umlaut |
969 | | x_string_escape_map('\304\', 'Ä'). % A mit Umlaut |
970 | | x_string_escape_map('\326\', 'Ö'). % O mit Umlaut |
971 | | x_string_escape_map('\334\', 'Ü'). % U mit Umlaut |
972 | | |
973 | | x_string_escape_map('\353\', 'ë'). % e mit Umlaut |
974 | | x_string_escape_map('\313\', 'Ë'). % E mit Umlaut |
975 | | |
976 | | x_string_escape_map('\350\', 'è'). % e mit Accent |
977 | | x_string_escape_map('\351\', 'é'). |
978 | | x_string_escape_map('\352\', 'ê'). |
979 | | x_string_escape_map('\310\', 'È'). % E mit Accent |
980 | | x_string_escape_map('\311\', 'É'). |
981 | | x_string_escape_map('\312\', 'Ê'). |
982 | | |
983 | | x_string_escape_map('\340\', 'à'). % a mit Accent |
984 | | x_string_escape_map('\341\', 'á'). |
985 | | x_string_escape_map('\342\', 'â'). |
986 | | x_string_escape_map('\300\', 'À'). % A mit Accent |
987 | | x_string_escape_map('\301\', 'Á'). |
988 | | x_string_escape_map('\302\', 'Â'). |
989 | | |
990 | | x_string_escape_map('\354\', 'ì'). % i mit Accent |
991 | | x_string_escape_map('\355\', 'í'). |
992 | | x_string_escape_map('\356\', 'î'). |
993 | | x_string_escape_map('\314\', 'Ì'). % I mit Accent |
994 | | x_string_escape_map('\315\', 'Í'). |
995 | | x_string_escape_map('\316\', 'Î'). |
996 | | |
997 | | x_string_escape_map('\362\', 'ò'). % o mit Accent |
998 | | x_string_escape_map('\363\', 'ó'). |
999 | | x_string_escape_map('\364\', 'ô'). |
1000 | | x_string_escape_map('\322\', 'Ò'). % O mit Accent |
1001 | | x_string_escape_map('\323\', 'Ó'). |
1002 | | x_string_escape_map('\324\', 'Ô'). |
1003 | | |
1004 | | x_string_escape_map('\347\', 'ç'). % cedille |
1005 | | x_string_escape_map('\307\', 'Ç'). % Cedille |
1006 | | |
1007 | | x_string_escape_map('\337\', 'ß'). % scharfes S (sz) |
1008 | | x_string_escape_map('\361\', 'ñ'). % n with tilde |
1009 | | |
1010 | | x_string_escape_map('\1661\', 'α'). % Greek |
1011 | | x_string_escape_map('\1662\', 'β'). |
1012 | | x_string_escape_map('\1663\', 'γ'). |
1013 | | x_string_escape_map('\1664\', 'δ'). |
1014 | | x_string_escape_map('\1665\', 'ε'). |
1015 | | x_string_escape_map('\1666\', 'ζ'). |
1016 | | x_string_escape_map('\1667\', 'η'). |
1017 | | x_string_escape_map('\1670\', 'θ'). |
1018 | | x_string_escape_map('\1671\', 'ι'). |
1019 | | x_string_escape_map('\1672\', 'κ'). |
1020 | | x_string_escape_map('\1673\', 'λ'). |
1021 | | x_string_escape_map('\1674\', 'μ'). |
1022 | | x_string_escape_map('\1675\', 'ν'). |
1023 | | x_string_escape_map('\1676\', 'ξ'). |
1024 | | x_string_escape_map('\1677\', 'ο'). |
1025 | | x_string_escape_map('\1700\', 'π'). |
1026 | | x_string_escape_map('\1701\', 'ρ'). |
1027 | | x_string_escape_map('\1702\', 'ς'). |
1028 | | x_string_escape_map('\1703\', 'σ'). |
1029 | | x_string_escape_map('\1704\', 'τ'). |
1030 | | x_string_escape_map('\1705\', 'υ'). |
1031 | | x_string_escape_map('\1706\', 'φ'). |
1032 | | x_string_escape_map('\1707\', 'χ'). |
1033 | | x_string_escape_map('\1710\', 'ψ'). |
1034 | | x_string_escape_map('\1711\', 'ω'). |
1035 | | |
1036 | | % to do: add missing upper-case Greek letters: |
1037 | | x_string_escape_map('\1624\', 'Δ'). |
1038 | | x_string_escape_map('\1630\', 'Θ'). |
1039 | | x_string_escape_map('\1633\', 'Λ'). |
1040 | | x_string_escape_map('\1636\', 'Ξ'). |
1041 | | x_string_escape_map('\1645\', 'Υ'). |
1042 | | x_string_escape_map('\1647\', 'Χ'). |
1043 | | x_string_escape_map('\1650\', 'Ψ'). |
1044 | | x_string_escape_map('\1651\', 'Ω'). |
1045 | | |
1046 | | % other symbols (converted using http://www.online-toolz.com/tools/unicode-html-entities-convertor.php ) |
1047 | | x_string_escape_map('\21242\','⊢'). %vdash turnstyle decimal: 8866 |
1048 | | x_string_escape_map('\21250\','⊨'). %models turnstyle decimal: 8872 |
1049 | | x_string_escape_map('\x21D4\','⇔'). % equivalence |
1050 | | x_string_escape_map('\x21D2\','⇒'). % implication |
1051 | | x_string_escape_map('\x2203\','∃'). % exists |
1052 | | x_string_escape_map('\x2200\','∀'). % forall |
1053 | | x_string_escape_map('ยท','·'). % dot used for quantifiers, ASCII 183 |
1054 | | x_string_escape_map('\x2227\','∧'). % conjunct |
1055 | | x_string_escape_map('\x2228\','∨'). % disjunct |
1056 | | x_string_escape_map('\xAC\','¬'). % negation |
1057 | | x_string_escape_map('\x21A6\','↦'). % maplet |-> |
1058 | | x_string_escape_map('\x2286\','⊆'). % <: subseteq |
1059 | | x_string_escape_map('\x222A\','∪'). % union |
1060 | | x_string_escape_map('\x2229\','∩'). % intersection |
1061 | | x_string_escape_map('\x2205\','∅'). % empty set |
1062 | | x_string_escape_map('\x2260\','≠'). % not equal |
1063 | | x_string_escape_map('\x2264\','≤'). % less equal |
1064 | | x_string_escape_map('\x2265\','≥'). % greater equal |
1065 | | x_string_escape_map('\x2124\','ℤ'). % Z (INTEGER) |
1066 | | x_string_escape_map('\x2115\','ℕ'). % NATURAL |
1067 | | |
1068 | | % Numbers without HTML translation: .. 8229 |
1069 | | |
1070 | | % translate unknown unicode chars to lozenge |
1071 | | x_string_escape_map(Unicode, Result) :- atom(Unicode), |
1072 | | atom_codes(Unicode,[Nr]), Nr>127, |
1073 | | !, |
1074 | | ajoin(['',Nr,';'],Result). |
1075 | | %x_string_escape_map(Unicode, '◊') :- |
1076 | | % atom(Unicode), atom_codes(Unicode,[Code]), Code>127. |
1077 | | % format(user_output,'Uni: ~w ~n',[Unicode]). |
1078 | | x_string_escape_map(X, X). |
1079 | | |
1080 | | % |
1081 | | % split_list(Pred,List,ListA,ListB): |
1082 | | % List contains exactly the same elements as ListA and ListB |
1083 | | % An element E is member of ListA iff Pred(E) is true and E is element of List. |
1084 | | % An element E is member of ListB iff Pred(E) is false and E is element of List. |
1085 | | :- meta_predicate split_list(1,?,?,?). |
1086 | | :- meta_predicate split_list2(?,1,?,?). |
1087 | | split_list(Pred,List,A,B) :- |
1088 | | split_list2(List,Pred,A,B). |
1089 | | split_list2([],_Pred,[],[]). |
1090 | | split_list2([Elem|Rest],Pred,A,B) :- |
1091 | ? | ( call(Pred,Elem) -> A=[Elem|AR], B=BR |
1092 | | ; otherwise -> A=AR, B=[Elem|BR]), |
1093 | | split_list2(Rest,Pred,AR,BR). |
1094 | | |
1095 | | % a variation of split_list which also returns a list of predicate results |
1096 | | % with re_split_list_idx(L,PredResult,A,B) : we can split another list using the same pattern |
1097 | | :- meta_predicate split_list_idx(1,?,?,?,?). |
1098 | | :- meta_predicate split_list_idx2(?,1,?,?,?). |
1099 | | split_list_idx(Pred,List,PredResult,A,B) :- |
1100 | | split_list_idx2(List,Pred,PredResult,A,B). |
1101 | | split_list_idx2([],_Pred,[],[],[]). |
1102 | | split_list_idx2([Elem|Rest],Pred,[PredTrue|PT],A,B) :- |
1103 | | (call(Pred,Elem) -> PredTrue=true, A=[Elem|AR], B=BR |
1104 | | ; otherwise -> PredTrue=false, A=AR, B=[Elem|BR]), |
1105 | | split_list_idx2(Rest,Pred,PT,AR,BR). |
1106 | | |
1107 | | |
1108 | | re_split_list_idx([],[],[],[]). |
1109 | | re_split_list_idx([Elem|Rest],[PredTrue|PT],A,B) :- |
1110 | | (PredTrue=true -> A=[Elem|AR], B=BR |
1111 | | ; otherwise -> A=AR, B=[Elem|BR]), |
1112 | | re_split_list_idx(Rest,PT,AR,BR). |
1113 | | |
1114 | | |
1115 | | :- meta_predicate foldl(3,?,?,?). |
1116 | | :- meta_predicate foldl2(?,3,?,?). |
1117 | | just_for_unit_test_add(A,B,C) :- C is A+(10*B). |
1118 | | :- assert_must_succeed(( foldl(just_for_unit_test_add,[],0,R), R==0 )). |
1119 | | :- assert_must_succeed(( foldl(just_for_unit_test_add,[3,6,7,2,6],0,R), R==36726 )). |
1120 | | |
1121 | | foldl(MPred,List,Start,Result) :- |
1122 | ? | foldl2(List,MPred,Start,Result). |
1123 | | foldl2([],_Pred,Value,Value). |
1124 | | foldl2([Elem|Rest],MPred,OldValue,NewValue) :- |
1125 | ? | call(MPred,Elem,OldValue,Value), |
1126 | ? | foldl2(Rest,MPred,Value,NewValue). |
1127 | | |
1128 | | |
1129 | | :- meta_predicate foldl(4,?,?,?,?). |
1130 | | :- meta_predicate foldl2(?,4,?,?,?). |
1131 | | just_for_unit_test_add2(A,B,C,D) :- D is 100*C+10*A+B. |
1132 | | :- assert_must_succeed(( foldl(just_for_unit_test_add2,[],[],0,R), R==0 )). |
1133 | | :- assert_must_succeed(( foldl(just_for_unit_test_add2,[3,6,7,2,6],[5,2,9,0,8],0,R), R==3562792068 )). |
1134 | | |
1135 | | foldl(MPred,List,List1,Start,Result) :- |
1136 | | foldl2(List,MPred,List1,Start,Result). |
1137 | | foldl2([],_Pred,[],Value,Value). |
1138 | | foldl2([Elem|Rest],MPred,[H1|R1],OldValue,NewValue) :- |
1139 | | call(MPred,Elem,H1,OldValue,Value), |
1140 | | foldl2(Rest,MPred,R1,Value,NewValue). |
1141 | | |
1142 | | :- meta_predicate foldl(5,?,?,?,?,?). |
1143 | | :- meta_predicate foldl2(?,5,?,?,?,?). |
1144 | | just_for_unit_test_add3(A,B,C,D,E) :- E is D*1000+100*A+10*B+C. |
1145 | | :- assert_must_succeed(( foldl(just_for_unit_test_add3,[],[],[],0,R), R==0 )). |
1146 | | :- assert_must_succeed(( foldl(just_for_unit_test_add3,[3,6,7],[5,2,9],[4,1,0],0,R), |
1147 | | R==354621790 )). |
1148 | | |
1149 | | foldl(MPred,A,B,C,Start,Result) :- |
1150 | | foldl2(A,MPred,B,C,Start,Result). |
1151 | | foldl2([],_Pred,[],[],Value,Value). |
1152 | | foldl2([Elem|Rest],MPred,[H1|R1],[H2|R2],OldValue,NewValue) :- |
1153 | | call(MPred,Elem,H1,H2,OldValue,Value), |
1154 | | foldl2(Rest,MPred,R1,R2,Value,NewValue). |
1155 | | |
1156 | | :- assert_must_succeed(( average([2,4,8,10],Avg), D is abs(Avg-6),D<0.001 )). |
1157 | | :- assert_must_succeed(( average([2],Avg), D is abs(Avg-2), D<0.001 )). |
1158 | | average(List,Avg) :- |
1159 | | length(List,N),sumlist(List,Sum),Avg is Sum/N. |
1160 | | |
1161 | | |
1162 | | % assert_once works like assert, but checks if the fact has already been stored before and |
1163 | | % will not store it a second time |
1164 | | :- meta_predicate assert_once(0). |
1165 | | assert_once(MPredicate) :- |
1166 | | ( call(MPredicate) -> true |
1167 | | ; otherwise -> assert(MPredicate)). |
1168 | | |
1169 | | |
1170 | | |
1171 | | :- dynamic id_counter/1. |
1172 | | unique_id(Prefix,Id) :- |
1173 | | ( id_counter(V) -> retractall(id_counter(_)) |
1174 | | ; otherwise -> V is 0), |
1175 | | N is V+1, |
1176 | | assert( id_counter(N) ), |
1177 | | number_codes(N,NCodes), |
1178 | | append(Prefix,NCodes,ICodes), |
1179 | | atom_codes(Id,ICodes). |
1180 | | |
1181 | | % PROBPATH is the runtime search path for defintion files not found relative to the original machine. |
1182 | | % By default this is the stdlib directory relative to the prob base dir. |
1183 | | % The user can provide a list of : separated directories in the PROBPATH |
1184 | | % environment variables which are prepended to the default path. |
1185 | | get_PROBPATH(PROBPATH) :- |
1186 | | environ('PROBPATH', CustomPATH), |
1187 | | atom_length(CustomPATH, L), |
1188 | | L > 0, !, |
1189 | | get_path_separator(PS), |
1190 | | get_stdlib_path(STDLIB), ajoin([CustomPATH, PS, STDLIB], PROBPATH). |
1191 | | |
1192 | | get_PROBPATH(PROBPATH) :- get_stdlib_path(PROBPATH). |
1193 | | |
1194 | | get_stdlib_path(STDLIB) :- runtime_application_path(Base), atom_concat(Base, '/stdlib', STDLIB). |
1195 | | |
1196 | | % Platform specific path separator char |
1197 | | map_path_separator(windows, ';'). |
1198 | | map_path_separator(_, ':'). |
1199 | | |
1200 | | get_path_separator(PS) :- host_platform(Platform), map_path_separator(Platform, PS), !. |