| 1 | % (c) 2009-2026 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_printing,[ | |
| 6 | print_term_summary/1, print_term_summary_hash/1, | |
| 7 | print_term_summary_wo_nl/1, | |
| 8 | print_term_summary_user_output/1, | |
| 9 | print_value_summary/1, | |
| 10 | print_functor/1, | |
| 11 | print_var/1, print_vars/1, | |
| 12 | watch/3, | |
| 13 | trace_unify/2, trace_print/1, trace_print/2, | |
| 14 | print_error/1, | |
| 15 | print_message_on_stream/3, | |
| 16 | format_error/2, format_error_with_nl/2, | |
| 17 | no_color/0, | |
| 18 | set_no_color/1, reset_no_color_to_default/0, | |
| 19 | print_red/2, print_red/1, print_green/1, | |
| 20 | format_with_colour/4, format_with_colour_nl/4, | |
| 21 | start_terminal_colour/2, | |
| 22 | reset_terminal_colour/1, | |
| 23 | get_repl_prompt/1, get_repl_continuation_prompt/1, | |
| 24 | get_terminal_colour_code/2, | |
| 25 | check_mark_symbol_codes/1, print_green_checkmark/1, | |
| 26 | heavy_cross_symbol_codes/1, print_red_cross/1, | |
| 27 | print_time_stamp/1, | |
| 28 | print_dynamic_pred/3, print_dynamic_pred/4, | |
| 29 | print_dynamic_fact/2, | |
| 30 | print_goal/1, | |
| 31 | indentws/1, | |
| 32 | nested_print_term/2, nested_print_term/1, | |
| 33 | nested_write_term_to_codes/2, | |
| 34 | tcltk_nested_read_prolog_file_as_codes/2, | |
| 35 | write_canonical_to_codes/2,write_canonical_to_codes/3, | |
| 36 | better_write_canonical_to_codes/2,better_write_canonical_to_codes/3 | |
| 37 | ]). | |
| 38 | ||
| 39 | ||
| 40 | :- use_module(module_information). | |
| 41 | :- module_info(group,infrastructure). | |
| 42 | :- module_info(description,'This module contains printing/debug helper predicates.'). | |
| 43 | ||
| 44 | ||
| 45 | :- use_module(library(terms),[term_hash/2]). | |
| 46 | print_term_summary_hash(T) :- print_term_summary(T), | |
| 47 | term_hash(T,H), %% (H=179896960 -> trace ; true), %% | |
| 48 | format('Hash = ~w~n',H). | |
| 49 | ||
| 50 | %:- use_module(library(terms),[term_size/2]). | |
| 51 | print_term_summary(T) :- print_term_summary_wo_nl(T), | |
| 52 | %term_size(T,Sz), format(' term_size: ~w',[Sz]), | |
| 53 | nl. | |
| 54 | ||
| 55 | % always print to user_output: | |
| 56 | print_term_summary_user_output(T) :- current_output(OldStream), | |
| 57 | set_output(user_output),print_term_summary(T),set_output(OldStream). | |
| 58 | ||
| 59 | % a special version for user friendly B Value information | |
| 60 | print_value_summary(BValue) :- print_arg(BValue),nl. | |
| 61 | ||
| 62 | print_term_summary_wo_nl(N) :- var(N),!,write('VARIABLE: '),write(N). | |
| 63 | print_term_summary_wo_nl((A,B)) :- write('('), print_term_summary_wo_nl(A), | |
| 64 | write(','), print_term_summary_wo_nl(B),write(')'). | |
| 65 | print_term_summary_wo_nl(clpfd:Call) :- !, write(clpfd:Call). | |
| 66 | print_term_summary_wo_nl(Module:Call) :- !, | |
| 67 | write(Module), write(':'),print_term_summary_wo_nl(Call). | |
| 68 | print_term_summary_wo_nl(bind(Var,Val)) :- !,print_arg(bind(Var,Val)). | |
| 69 | print_term_summary_wo_nl(N) :- functor(N,F,Arity), write(F), write('/'),write(Arity), | |
| 70 | N=..[F|Args], l_print_arg(Args). | |
| 71 | ||
| 72 | l_print_arg(V) :- var(V),!, write('...'). | |
| 73 | l_print_arg([]). | |
| 74 | l_print_arg([H|T]) :- write(': '), print_arg(H), l_print_arg(T). | |
| 75 | ||
| 76 | % print nested args: avoid nesting any further to avoid blowup | |
| 77 | l_print_nested_arg(V) :- var(V),!, write('...'). | |
| 78 | l_print_nested_arg([]). | |
| 79 | l_print_nested_arg([H|T]) :- write(': '), print_nested_arg(H), l_print_nested_arg(T). | |
| 80 | ||
| 81 | print_nested_arg(N) :- var(N),!,print_var(N). | |
| 82 | print_nested_arg([H|T]) :- list_skeleton_size([H|T],Sz,Type), !, | |
| 83 | write(Type), | |
| 84 | (Type=closed,ground([H|T]) -> write('&ground') ; true), write(' LIST.size='), write(Sz). | |
| 85 | print_nested_arg(avl_set(A)) :- ground(A), avl_size(A,Sz),!, write('AVL.size='),write(Sz). | |
| 86 | print_nested_arg(A) :- print_arg(A). | |
| 87 | ||
| 88 | :- use_module(library(avl)). | |
| 89 | % USE MODULE NOT PERFORMED TO AVOID LOADING translate and all dependent modules | |
| 90 | %:- use_module(translate,[print_bexpr_with_limit/2, print_bstate/1]). | |
| 91 | ||
| 92 | print_arg(N) :- var(N),!,print_var(N). | |
| 93 | print_arg(int(N)) :- !, (var(N) -> print_var_integer(N) ; write(int(N))). | |
| 94 | print_arg(term(F)) :- nonvar(F), F=floating(N),!, (var(N) -> write(F) ; write(N)). | |
| 95 | print_arg(float(N)) :- !, write(float(N)). | |
| 96 | print_arg(freeval(ID,Case,Value)) :- !, format('freeval ~w.~w : ',[ID,Case]), print_arg(Value). | |
| 97 | print_arg(binterval(Low,Up,E)) :- !, | |
| 98 | write('binterval('),print_integer(Low),write(','), | |
| 99 | print_integer(Up),write(','),print_integer(E),write(')'). | |
| 100 | print_arg(fd(N,GS)) :- !, | |
| 101 | (number(N),b_global_sets:is_b_global_constant(GS,N,Res) -> write(Res) | |
| 102 | ; write('fd('),print_integer(N),format(',~w)',GS)). | |
| 103 | print_arg(rec(Fields)) :- !, write('rec('), print_arg(Fields),write(')'). | |
| 104 | print_arg(bind(Var,Val)) :- atomic(Var), !, write(Var),write('/'),print_arg(Val). | |
| 105 | print_arg(field(Var,Val)) :- atomic(Var), !, write('field('),write(Var),write('/'),print_arg(Val),write(')'). | |
| 106 | print_arg(typedvalc(Val,Type,VarID,EnumWarningInfos,Card)) :- | |
| 107 | atomic(VarID), !, format('typedvalc for ~w :(',[VarID]),print_arg(Val), | |
| 108 | format(',~w,~w,~w) ',[Type,EnumWarningInfos,Card]). | |
| 109 | print_arg(wfx(WF0,_Store,WFE,_Infos)) :- !, write(wfx(WF0,'...',WFE,'...')). % do not print store and Infos: TODO we could show call_stack summary | |
| 110 | print_arg(Module:Call) :- atomic(Module),!, | |
| 111 | write(Module), write(':'),print_arg(Call). | |
| 112 | print_arg(A) :- atomic(A),!, write(A). | |
| 113 | print_arg(A) :- A=node(_,_,_,_,_),ground(A), !, write('AVLnode.size='),avl_size(A,Sz), write(Sz). | |
| 114 | print_arg(avl_set(A)) :- ground(A), !, | |
| 115 | (custom_explicit_sets:singleton_set(avl_set(A),El) -> write('AVL{'),print_arg(El),write('}') | |
| 116 | ; avl_size(A,Sz), | |
| 117 | (Sz>3 -> write('AVL.size='),write(Sz) | |
| 118 | ; custom_explicit_sets:expand_custom_set_to_list(avl_set(A),ESet,_,print_arg), | |
| 119 | write('AVL{'), l_print_nested_arg(ESet), write('}') | |
| 120 | ) | |
| 121 | ). | |
| 122 | print_arg((A,B)) :- !, write('('),print_arg(A), write(','), print_arg(B), write(')'). | |
| 123 | print_arg(string(A)) :- !, write('string('),print_arg(A), write(')'). | |
| 124 | print_arg([]) :- !, write('[]'). | |
| 125 | print_arg([H|T]) :- T==[], !, write('['),print_arg(H),write(']'). | |
| 126 | print_arg([H|T]) :- nonvar(T),T=[H2|T2],T2==[], !, | |
| 127 | write('['),print_arg(H),write(','),print_arg(H2),write(']'). | |
| 128 | print_arg([H|T]) :- nonvar(H), H=bind(_,_),!, get_list_up_to([H|T],10,S,ALL), | |
| 129 | translate:print_bstate(S), | |
| 130 | (ALL=all -> true ; write(', ... ')). | |
| 131 | print_arg([H|T]) :- list_skeleton_size([H|T],Sz,Type), !, | |
| 132 | write(Type), | |
| 133 | (Type=closed,ground([H|T]) -> write('&ground') ; true), | |
| 134 | write(' LIST.size='), write(Sz), | |
| 135 | (Sz<35 -> write(' ['), l_print_nested_arg([H|T]), write(']') ; true). | |
| 136 | print_arg(closure(P,T,B)) :- !, | |
| 137 | (custom_explicit_sets:is_interval_closure(P,T,B,Low,Up) | |
| 138 | -> write('SYMBOLIC-INTERVAL ['), | |
| 139 | print_integer_with_var(Low), write('..'), | |
| 140 | print_integer_with_var(Up), write(']') | |
| 141 | ; B=b(_,_,INFO), | |
| 142 | findall(Ann,member(prob_annotation(Ann),INFO),Anns), | |
| 143 | (closures:is_symbolic_closure(P,T,B) -> INF='ANNOTATED-' | |
| 144 | ; custom_explicit_sets:is_infinite_or_very_large_closure(P,T,B,200000) -> INF='INF_OR_LARGE-' | |
| 145 | ; INF=''), | |
| 146 | format('SYMBOLIC-~wSET(~w) ~w ',[INF,Anns,P]) | |
| 147 | %terms:term_size(B,BSz), format('SYMBOLIC-~wSET(sz:~w,~w) ~w ',[INF,BSz,Anns,P]) | |
| 148 | ). %, write(' | '), write(_B). | |
| 149 | print_arg(mnf(Call)) :- !, write('mnf('),print_term_summary_wo_nl(Call), write(')'). | |
| 150 | print_arg(pp_mnf(Call)) :- !, write('mnf('),print_term_summary_wo_nl(Call), write(')'). | |
| 151 | print_arg(pp_cll(Call)) :- !, write('mnf('),print_term_summary_wo_nl(Call), write(')'). | |
| 152 | print_arg(b(Expr,T,I)) :- !, | |
| 153 | (T==pred -> write('PRED(') ; write('BEXPR(')), | |
| 154 | print_functor(Expr), write(','), | |
| 155 | translate:print_bexpr_with_limit(b(Expr,T,I),200), | |
| 156 | (error_manager:extract_line_col(b(Expr,T,I),Srow,Scol,_,_) -> format(', lne:col=~w:~w)',[Srow,Scol]) | |
| 157 | ; write(')')). | |
| 158 | print_arg(span_predicate(A,B,C)) :- | |
| 159 | error_manager:extract_line_col(span_predicate(A,B,C),Srow,Scol,Erow,Ecol), !, | |
| 160 | format('span_predicate(~w:~w-~w:~w)',[Srow,Scol,Erow,Ecol]). | |
| 161 | print_arg(N) :- print_functor(N). | |
| 162 | ||
| 163 | :- use_module(library(clpfd),[fd_dom/2, fd_degree/2, fd_size/2]). | |
| 164 | print_var(V) :- fd_dom(V,D), D \= inf..sup, fd_size(V,Sz), fd_degree(V,Dg),!, | |
| 165 | (is_real_domain(D) -> Kind = 'REAL' ; Kind = 'INT'), | |
| 166 | format('~w VAR: ~w:~w [sz:~w,dg:~w] ',[Kind,V,D,Sz,Dg]). | |
| 167 | %write(':('),frozen(V,Goal),write_term(Goal,[max_depth(3)]),write(')'). | |
| 168 | print_var(V) :- write('VARIABLE: '),write(V), print_frozen_var_info(V). | |
| 169 | print_var_integer(X) :- write('int(?:'),fd_dom(X,Dom),write(Dom), | |
| 170 | %write(':('),frozen(X,Goal),write(Goal),write(')') | |
| 171 | write(')'). | |
| 172 | ||
| 173 | is_real_domain(Low.._UP) :- (Low = finf ; float(Low)). | |
| 174 | ||
| 175 | % try determine variable type from frozen info | |
| 176 | print_frozen_var_info(V) :- frozen(V,G), (print_frozen_var_info2(G,V) -> true ; true). | |
| 177 | print_frozen_var_info2((A,B),V) :- !,(print_frozen_var_info2(A,V) -> true ; print_frozen_var_info2(B,V) -> true). | |
| 178 | print_frozen_var_info2(true,_) :- !,fail. | |
| 179 | print_frozen_var_info2(b_interpreter_check:imply_true(_,_,_),_) :- write(' : BOOL'). | |
| 180 | print_frozen_var_info2(bool_pred:blocking_force_eq(V,_,_),Var) :- Var==V, write(' : BOOL'). | |
| 181 | print_frozen_var_info2(kernel_equality:eq_empty_set(V,_),Var) :- Var==V, write(' : SET'). | |
| 182 | print_frozen_var_info2(kernel_equality:eq_empty_set_wf(V,_,_),Var) :- Var==V, write(' : SET'). | |
| 183 | print_frozen_var_info2(_:non_free(V),Var) :- Var==V, write(' : NON-FREE'). % kernel_objects | |
| 184 | print_frozen_var_info2(_:ground_value_check(V,_),Var) :- Var==V, write(' : GRVAL-CHECK'). | |
| 185 | print_frozen_var_info2(_:Call,_Var) :- functor(Call,Functor,Arity),format(' : ~w/~w',[Functor,Arity]). | |
| 186 | print_frozen_var_info2(Call,_Var) :- functor(Call,Functor,Arity),format(' : ~w/~w',[Functor,Arity]). | |
| 187 | % TO DO: add other co-routines for boolean values,... | |
| 188 | ||
| 189 | :- use_module(library(lists),[maplist/2]). | |
| 190 | print_vars(V) :- maplist(tools_printing:print_var,V),nl. | |
| 191 | ||
| 192 | print_integer(I) :- (var(I) -> fd_dom(I,Dom), write('?:'),write(Dom) ; write(I)). | |
| 193 | print_integer_with_var(I) :- (var(I) -> print_var(I) ; write(I)). | |
| 194 | ||
| 195 | get_list_up_to([],_,[],all). | |
| 196 | get_list_up_to([H|T],N,R,ALL) :- | |
| 197 | (N<1 -> R=[],ALL=no ; N1 is N-1, R=[H|TR], get_list_up_to(T,N1,TR,ALL)). | |
| 198 | ||
| 199 | list_skeleton_size(X,Sz,Type) :- var(X),!,Sz=0,Type=open. | |
| 200 | list_skeleton_size([],0,closed). | |
| 201 | list_skeleton_size([_|T],N,Type) :- list_skeleton_size(T,NT,Type), N is NT+1. | |
| 202 | ||
| 203 | ||
| 204 | print_functor(N) :- var(N),!,write('VARIABLE: '),write(N). | |
| 205 | print_functor(N) :- functor(N,F,Arity), write(F), write('/'),write(Arity). | |
| 206 | ||
| 207 | % --------------- | |
| 208 | ||
| 209 | :- block watch(-,?,?). | |
| 210 | watch([],N,Pos) :- !,print_data([],N,Pos). | |
| 211 | watch([H|T],N,Pos) :- !,print_data(H,N,Pos), P1 is Pos+1, watch(H,N,Pos), watch(T,N,P1). | |
| 212 | watch((P1,P2),N,Pos) :- print_data((P1,P2),N,Pos), watch(P2,N,Pos). | |
| 213 | watch(Other,N,Pos) :- print_data(Other,N,Pos). | |
| 214 | print_data(Data,N,Pos) :- write(N), write(' @ '), write(Pos),write(' : '), write(Data),nl. | |
| 215 | ||
| 216 | % --------------- | |
| 217 | ||
| 218 | :- use_module(library(lists),[maplist/3]). | |
| 219 | trace_unify(A,B) :- trace_unify_aux(A,B,0). | |
| 220 | trace_unify_aux(A,B,Lvl) :- (var(A);var(B)),!,indent(Lvl),write(A),write(' <-> '), write(B), | |
| 221 | if(A=B,true,(write(' FAILS'),nl,fail)),nl. | |
| 222 | trace_unify_aux(A,B,Lvl) :- functor(A,FA,FAN), functor(B,FB,FBN), | |
| 223 | indent(Lvl),write(FA/FAN), | |
| 224 | A=..[_|As], B=..[_|Bs], | |
| 225 | ((FA,FAN)=(FB,FBN) | |
| 226 | -> (As=[] -> nl ; write('('),nl,l_trace_unify(As,Bs,Lvl)) | |
| 227 | ; format(' FUNCTOR MISMATCH : ~w/~w <-> ~w/~w~n',[FA,FAN,FB,FBN]), | |
| 228 | maplist(abstract_top_level,As,AAs), AA =..[FA|AAs], | |
| 229 | maplist(abstract_top_level,Bs,ABs), AB =..[FB|ABs], | |
| 230 | indent(Lvl), | |
| 231 | format(' TOP: ~w <-> ~w~n',[AA,AB]), | |
| 232 | fail | |
| 233 | ). | |
| 234 | indent(0) :- !. | |
| 235 | indent(X) :- X>800, !, write('*800*-'), X1 is X-800, indent(X1). | |
| 236 | indent(X) :- X>80, !, write('*80*-'), X1 is X-80, indent(X1). | |
| 237 | indent(X) :- X>0, write('+-'), X1 is X-1, indent(X1). | |
| 238 | ||
| 239 | abstract_top_level(X,R) :- \+ compound(X),!,R=X. | |
| 240 | abstract_top_level(X,AX) :- functor(X,F,N), functor(AX,F,N). | |
| 241 | ||
| 242 | ||
| 243 | l_trace_unify([],[],L) :- indent(L), write(')'),nl. | |
| 244 | l_trace_unify([A|TA],[B|TB],Lvl) :- L1 is Lvl+1, trace_unify_aux(A,B,L1), | |
| 245 | l_trace_unify(TA,TB,Lvl). | |
| 246 | ||
| 247 | % --------------- | |
| 248 | ||
| 249 | trace_print(A) :- trace_print(A,0,4). | |
| 250 | trace_print(A,Max) :- trace_print(A,0,Max). | |
| 251 | ||
| 252 | trace_print(A,Lvl,MaxLvl) :- Lvl >= MaxLvl,!, indent(Lvl), print(A),nl. | |
| 253 | trace_print(A,Lvl,_MaxLvl) :- treat_as_atomic(A),!, indent(Lvl), print(A),nl. | |
| 254 | trace_print([H|T],Lvl,MaxLvl) :- !, indent(Lvl), print('['),nl, | |
| 255 | l_trace_print([H|T],Lvl,MaxLvl), | |
| 256 | indent(Lvl), print(']'),nl. | |
| 257 | %trace_print(A,Lvl,_MaxLvl) :- functor(A,_,1), arg(1,A,AN), atomic(AN),!, indent(Lvl), print(A),nl. | |
| 258 | trace_print(A,Lvl,_MaxLvl) :- A=..[_|Args], maplist(atomic,Args),!, | |
| 259 | indent(Lvl), print(A),nl. | |
| 260 | trace_print(A,Lvl,MaxLvl) :- functor(A,FA,FAN), | |
| 261 | indent(Lvl),print(FA/FAN),print('('),nl, | |
| 262 | A=..[_|As], l_trace_print(As,Lvl,MaxLvl). | |
| 263 | ||
| 264 | :- use_module(tools_positions, [is_position/1]). | |
| 265 | treat_as_atomic(A) :- var(A). | |
| 266 | treat_as_atomic(A) :- atomic(A). | |
| 267 | treat_as_atomic(nodeid(Pos)) :- is_position(Pos). | |
| 268 | treat_as_atomic(sharing(_,_,_,_)). | |
| 269 | treat_as_atomic(ID/V) :- treat_as_atomic(ID),treat_as_atomic(V). | |
| 270 | treat_as_atomic([H|T]) :- T==[], treat_as_atomic(H). | |
| 271 | ||
| 272 | l_trace_print([],L,_MaxLvl) :- indent(L), print(')'),nl. | |
| 273 | l_trace_print([A|TA],Lvl,MaxLvl) :- L1 is Lvl+1, trace_print(A,L1,MaxLvl), | |
| 274 | l_trace_print(TA,Lvl,MaxLvl). | |
| 275 | ||
| 276 | % ----------------------------------- | |
| 277 | ||
| 278 | % print on error stream | |
| 279 | print_error(Error) :- | |
| 280 | stream_is_interactive(user_error),!, | |
| 281 | start_terminal_colour([red,bold],user_error), | |
| 282 | call_cleanup(print_no_col(user_error,Error),reset_terminal_colour_and_nl(user_error)). | |
| 283 | %% flush_output(user_error), %%. | |
| 284 | print_error(Error) :- print_no_col(user_error,Error), nl(user_error). | |
| 285 | ||
| 286 | print_no_col(Stream,ErrorTerm) :- | |
| 287 | write(Stream,'! '), | |
| 288 | (var(ErrorTerm) -> write(Stream,'_') | |
| 289 | ; write_term(Stream,ErrorTerm,[max_depth(20),numbervars(true)])). | |
| 290 | ||
| 291 | reset_terminal_colour_and_nl(Stream) :- | |
| 292 | reset_terminal_colour(Stream), | |
| 293 | nl(Stream). % for SICS prompt it is important to have nl after code to reset colour | |
| 294 | ||
| 295 | print_message_on_stream(Stream,Color,Message) :- | |
| 296 | stream_is_interactive(Stream),!, | |
| 297 | start_terminal_colour(Color,Stream), | |
| 298 | call_cleanup(print_no_col(Stream,Message),reset_terminal_colour_and_nl(Stream)). | |
| 299 | print_message_on_stream(Stream,_Color,Message) :- | |
| 300 | print_no_col(Stream,Message), | |
| 301 | nl(Stream). | |
| 302 | ||
| 303 | format_error_with_nl(Str,Args) :- format_with_colour_nl(user_error,[red,bold],Str,Args). | |
| 304 | format_error(Str,Args) :- format_with_colour(user_error,[red,bold],Str,Args). | |
| 305 | ||
| 306 | format_with_colour_nl(Stream,Colour,Str,Args) :- | |
| 307 | format_with_colour(Stream,Colour,Str,Args), | |
| 308 | nl(Stream). % followed by a newline *after* resetting the colours; important for SICStus prompt | |
| 309 | ||
| 310 | format_with_colour(Stream,Colour,Str,Args) :- | |
| 311 | stream_is_interactive(Stream),!, | |
| 312 | start_terminal_colour(Colour,Stream), | |
| 313 | call_cleanup(format(Stream,Str,Args), | |
| 314 | reset_terminal_colour(Stream)). | |
| 315 | format_with_colour(Stream,_Colour,Str,Args) :- format(Stream,Str,Args). | |
| 316 | ||
| 317 | % we could provide a preference to override this; in case we do want to see the colour escape codes in a file | |
| 318 | stream_is_interactive(S) :- | |
| 319 | intelligent_get_stream(S,RealStream), stream_property(RealStream,interactive). | |
| 320 | ||
| 321 | intelligent_get_stream(user_output,S) :- !, current_output(S). | |
| 322 | intelligent_get_stream(user_intput,S) :- !, current_input(S). | |
| 323 | intelligent_get_stream(user_error,S) :- !, prolog_flag(user_error,S). | |
| 324 | intelligent_get_stream(S,S). | |
| 325 | ||
| 326 | :- load_files(library(system), [when(compile_time), imports([environ/2])]). | |
| 327 | :- if(environ(no_terminal_colors,true)). | |
| 328 | no_color. | |
| 329 | set_no_color(_). | |
| 330 | reset_no_color_to_default. | |
| 331 | print_red(X) :- write(X). | |
| 332 | print_red(Stream,T) :- write(Stream,T). | |
| 333 | print_green(X) :- write(X). | |
| 334 | reset_terminal_colour(_). | |
| 335 | start_terminal_colour(_,_). | |
| 336 | :- else. | |
| 337 | :- use_module(library(system),[environ/2]). | |
| 338 | :- dynamic colours_disabled/0. | |
| 339 | no_color :- colours_disabled. | |
| 340 | set_no_color(false) :- retractall(colours_disabled). | |
| 341 | set_no_color(true) :- % useful e.g. for --no-color cli flag | |
| 342 | (colours_disabled -> true ; assertz(colours_disabled)). | |
| 343 | reset_no_color_to_default :- | |
| 344 | (no_col_env -> set_no_color(true) ; retractall(colours_disabled)). | |
| 345 | %TODO: we could try and detect Windows 8 or earlier where no terminal colours are supported | |
| 346 | no_col_env :- environ('NO_COLOR',_),!. % see http://no-color.org | |
| 347 | no_col_env :- environ('NOCOLOR',_),!. | |
| 348 | no_col_env :- environ('CLICOLOR',V), (V=0 ; V='NO' ; V='FALSE'),!. | |
| 349 | no_col_env :- environ('TERM',dumb). | |
| 350 | % | ?- environ('TERM',X). | |
| 351 | % X = 'xterm-256color' | |
| 352 | ||
| 353 | print_red(Stream,Term) :- no_color,!, | |
| 354 | format(Stream,'~w',[Term]). | |
| 355 | print_red(Stream,Term) :- | |
| 356 | format(Stream,'\e[31m~w\e[0m',[Term]). | |
| 357 | ||
| 358 | print_red(Term) :- no_color,!, | |
| 359 | format('~w',[Term]). | |
| 360 | print_red(Term) :- | |
| 361 | format('\e[31m~w\e[0m',[Term]). | |
| 362 | ||
| 363 | print_green(Term) :- no_color,!, | |
| 364 | format('~w',[Term]). | |
| 365 | print_green(Term) :- | |
| 366 | format('\e[32m~w\e[0m',[Term]). | |
| 367 | ||
| 368 | reset_terminal_colour(_) :- no_color,!. | |
| 369 | reset_terminal_colour(Stream) :- write(Stream,'\e[0m'). | |
| 370 | ||
| 371 | % get the >>> prompt possibly in colour: | |
| 372 | get_repl_prompt(Prompt) :- no_color,!,Prompt='>>> '. | |
| 373 | get_repl_prompt('\e[35m\e[1m>>>\e[0m '). % magenta bold colour, like python | |
| 374 | ||
| 375 | % get the ... prompt for multi-line inputs, possibly in colour: | |
| 376 | get_repl_continuation_prompt(Prompt) :- no_color,!,Prompt='>>> '. | |
| 377 | get_repl_continuation_prompt('\e[35m\e[1m...\e[0m '). % magenta bold colour, like python | |
| 378 | ||
| 379 | % see https://misc.flogisoft.com/bash/tip_colors_and_formatting | |
| 380 | start_terminal_colour(_,_) :- no_color,!. | |
| 381 | start_terminal_colour(black,Stream) :- !, write(Stream,'\e[30m'). | |
| 382 | start_terminal_colour(red,Stream) :- !, write(Stream,'\e[31m'). | |
| 383 | start_terminal_colour(green,Stream) :- !, write(Stream,'\e[32m'). | |
| 384 | start_terminal_colour(yellow,Stream) :- !, write(Stream,'\e[33m'). | |
| 385 | start_terminal_colour(blue,Stream) :- !, write(Stream,'\e[34m'). | |
| 386 | start_terminal_colour(magenta,Stream) :- !, write(Stream,'\e[35m'). | |
| 387 | start_terminal_colour(cyan,Stream) :- !, write(Stream,'\e[36m'). | |
| 388 | start_terminal_colour(light_gray,Stream) :- !, write(Stream,'\e[37m'). | |
| 389 | start_terminal_colour(dark_gray,Stream) :- !, write(Stream,'\e[90m'). | |
| 390 | start_terminal_colour(light_red,Stream) :- !, write(Stream,'\e[91m'). | |
| 391 | start_terminal_colour(light_green,Stream) :- !, write(Stream,'\e[92m'). | |
| 392 | start_terminal_colour(white,Stream) :- !, write(Stream,'\e[97m'). | |
| 393 | start_terminal_colour(bold,Stream) :- !, write(Stream,'\e[1m'). | |
| 394 | start_terminal_colour(underline,Stream) :- !, write(Stream,'\e[4m'). | |
| 395 | start_terminal_colour(dim,Stream) :- !, write(Stream,'\e[2m'). | |
| 396 | start_terminal_colour(black_background,Stream) :- !, write(Stream,'\e[49m'). | |
| 397 | start_terminal_colour(red_background,Stream) :- !, write(Stream,'\e[41m'). | |
| 398 | start_terminal_colour(green_background,Stream) :- !, write(Stream,'\e[42m'). | |
| 399 | start_terminal_colour(yellow_background,Stream) :- !, write(Stream,'\e[43m'). | |
| 400 | start_terminal_colour(white_background,Stream) :- !, write(Stream,'\e[107m'). | |
| 401 | start_terminal_colour(blink,Stream) :- !, write(Stream,'\e[5m'). | |
| 402 | start_terminal_colour(reverse,Stream) :- !, write(Stream,'\e[7m'). % reversed | |
| 403 | start_terminal_colour(hidden,Stream) :- !, write(Stream,'\e[8m'). | |
| 404 | start_terminal_colour(reset,Stream) :- !, write(Stream,'\e[0m'). | |
| 405 | % cursor nagivation: | |
| 406 | start_terminal_colour(go_left,Stream) :- !, write(Stream,'\e[1000Dm'). % go to left of terminal | |
| 407 | start_terminal_colour(go_up(Nr),Stream) :- number(Nr),!, format(Stream,'\e[~wA',[Nr]). | |
| 408 | start_terminal_colour(go_down(Nr),Stream) :- number(Nr),!, format(Stream,'\e[~wB',[Nr]). | |
| 409 | start_terminal_colour(go_right(Nr),Stream) :- number(Nr),!, format(Stream,'\e[~wC',[Nr]). | |
| 410 | start_terminal_colour(go_left(Nr),Stream) :- number(Nr),!, format(Stream,'\e[~wD',[Nr]). | |
| 411 | start_terminal_colour(clear_screen,Stream) :- write(Stream,'\e[2J'). | |
| 412 | start_terminal_colour(clear_screen_to_start,Stream) :- write(Stream,'\e[1J'). | |
| 413 | start_terminal_colour(clear_screen_to_end,Stream) :- write(Stream,'\e[0J'). | |
| 414 | start_terminal_colour(clear_line,Stream) :- write(Stream,'\e[2K'). | |
| 415 | start_terminal_colour(clear_line_to_start,Stream) :- write(Stream,'\e[1K'). | |
| 416 | start_terminal_colour(clear_line_to_end,Stream) :- write(Stream,'\e[0K'). | |
| 417 | start_terminal_colour(set_column(Nr),Stream) :- number(Nr),!, format(Stream,'\e[~wG',[Nr]). | |
| 418 | start_terminal_colour(set_position(Row,Col),Stream) :- number(Row), number(Col),!, | |
| 419 | format(Stream,'\e[~w;~wHm',[Row,Col]). | |
| 420 | start_terminal_colour(next_line(Nr),Stream) :- number(Nr),!, format(Stream,'\e[~wE',[Nr]). | |
| 421 | start_terminal_colour(prev_line(Nr),Stream) :- number(Nr),!, format(Stream,'\e[~wF',[Nr]). | |
| 422 | start_terminal_colour(save_position,Stream) :- !, write(Stream,'\e[s'). | |
| 423 | start_terminal_colour(restore_position,Stream) :- !, write(Stream,'\e[u'). | |
| 424 | % more colors, not supported by all terminals: | |
| 425 | start_terminal_colour(bright_red,Stream) :- !, write(Stream,'\e[31;1m'). % also 91m ? | |
| 426 | start_terminal_colour(bright_green,Stream) :- !, write(Stream,'\e[32;1m'). | |
| 427 | start_terminal_colour(orange,Stream) :- !, write(Stream,'\e[38;5;202m'). | |
| 428 | start_terminal_colour(gray,Stream) :- !, write(Stream,'\e[38;5;241m'). | |
| 429 | start_terminal_colour(grey,Stream) :- !, write(Stream,'\e[38;5;241m'). | |
| 430 | start_terminal_colour(dark_red,Stream) :- !, write(Stream,'\e[38;5;52m'). | |
| 431 | start_terminal_colour(col256(Nr),Stream) :- number(Nr), Nr>=0, Nr=<255,!, format(Stream,'\e[38;5;~wm',[Nr]). | |
| 432 | start_terminal_colour([],_Stream) :- !. | |
| 433 | start_terminal_colour([H|T],Stream) :- !, start_terminal_colour(H,Stream),start_terminal_colour(T,Stream). | |
| 434 | start_terminal_colour(C,_Stream) :- format(user_error,'*** UNKNOWN COLOUR: ~w~n',[C]). | |
| 435 | :- endif. | |
| 436 | ||
| 437 | get_terminal_colour_code(black,'\e[30m'). | |
| 438 | get_terminal_colour_code(red,'\e[31m'). | |
| 439 | get_terminal_colour_code(green,'\e[32m'). | |
| 440 | get_terminal_colour_code(yellow,'\e[33m'). | |
| 441 | get_terminal_colour_code(blue,'\e[34m'). | |
| 442 | get_terminal_colour_code(magenta,'\e[35m'). | |
| 443 | get_terminal_colour_code(cyan,'\e[36m'). | |
| 444 | get_terminal_colour_code(light_gray,'\e[37m'). | |
| 445 | get_terminal_colour_code(reset,'\e[0m'). | |
| 446 | get_terminal_colour_code(light_red,'\e[91m'). | |
| 447 | get_terminal_colour_code(light_green,'\e[92m'). | |
| 448 | get_terminal_colour_code(light_yellow,'\e[93m'). | |
| 449 | get_terminal_colour_code(light_blue,'\e[94m'). | |
| 450 | get_terminal_colour_code(light_magenta,'\e[95m'). | |
| 451 | get_terminal_colour_code(light_cyan,'\e[96m'). | |
| 452 | get_terminal_colour_code(white,'\e[97m'). | |
| 453 | % TODO: extend and use above | |
| 454 | ||
| 455 | % To print out colors: between:between(0,255,Nr),format(user_output,'\e[38;5;~wm Number=~w~n',[Nr,Nr]),fail. | |
| 456 | % See: https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html | |
| 457 | ||
| 458 | % creates a Unicode checkmark symbol (also used by homebrew) | |
| 459 | check_mark_symbol(Atom) :- atom_codes(Atom,[10004,65038] ). | |
| 460 | check_mark_symbol_codes( [10004,65038] ). | |
| 461 | heavy_cross_symbol_codes( [10006,65038] ). | |
| 462 | ||
| 463 | print_green_checkmark(Stream) :- no_color,!, | |
| 464 | format(Stream,'~s',[ [10004,65038] ]). | |
| 465 | print_green_checkmark(Stream) :- | |
| 466 | format(Stream,'\e[32m~s\e[0m',[ [10004,65038] ]). | |
| 467 | ||
| 468 | print_red_cross(Stream) :- no_color,!, | |
| 469 | format(Stream,'~s',[ [10006,65038] ]). | |
| 470 | print_red_cross(Stream) :- | |
| 471 | format(Stream,'\e[31m~s\e[0m',[ [10006,65038] ]). | |
| 472 | ||
| 473 | % ----------------------------------- | |
| 474 | ||
| 475 | :- use_module(library(system),[datime/1]). | |
| 476 | ||
| 477 | print_time_stamp(Stream) :- | |
| 478 | datime(datime(Year,Month,Day,Hour,Min,Sec)), | |
| 479 | (Min<10 | |
| 480 | -> format(Stream,'~w/~w/~w - ~wh0~w ~ws',[Day,Month,Year,Hour,Min,Sec]) | |
| 481 | ; format(Stream,'~w/~w/~w - ~wh~w ~ws',[Day,Month,Year,Hour,Min,Sec]) | |
| 482 | ). | |
| 483 | ||
| 484 | % ----------------------------------- | |
| 485 | print_dynamic_pred(InModule,Pred,N) :- print_dynamic_pred(user_output,InModule,Pred,N). | |
| 486 | ||
| 487 | print_dynamic_pred(Stream,InModule,Pred,N) :- nl(Stream), | |
| 488 | format(Stream,':- dynamic ~w/~w.~n',[Pred,N]), | |
| 489 | functor(Call,Pred,N), | |
| 490 | call(InModule:Call), | |
| 491 | write_term(Stream,Call,[quoted(true)]),write(Stream,'.'),nl(Stream),fail. | |
| 492 | print_dynamic_pred(Stream,InModule,Pred,N) :- | |
| 493 | functor(Call,Pred,N), | |
| 494 | (call(InModule:Call) -> true | |
| 495 | ; write_with_numbervars(Stream,Call), write(Stream,' :- fail.'),nl(Stream)), | |
| 496 | nl(Stream). | |
| 497 | ||
| 498 | write_with_numbervars(Stream,Term) :- copy_term(Term,T), numbervars(T,0,_), | |
| 499 | write_term(Stream,T,[quoted(true),numbervars(true)]). | |
| 500 | ||
| 501 | print_dynamic_fact(Stream,Fact) :- | |
| 502 | functor(Fact,Pred,N), | |
| 503 | format(Stream,':- dynamic ~w/~w.~n',[Pred,N]), | |
| 504 | write_term(Stream,Fact,[quoted(true)]),write(Stream,'.'),nl(Stream). | |
| 505 | ||
| 506 | % ----------------------------------- | |
| 507 | ||
| 508 | % typically used for printing call residues: | |
| 509 | print_goal(V) :- var(V),!, format(' ~p~n',[V]). | |
| 510 | print_goal([A|B]) :- !,print_goal(A), print_goal(B). | |
| 511 | print_goal((A,B)) :- !,print_goal(A), print_goal(B). | |
| 512 | print_goal(Module:(A,B)) :- !, format(' ~p:~n',[Module]), print_goal((A,B)). | |
| 513 | print_goal(when(C,G)) :- !, write(' when('), | |
| 514 | write_term(C,[max_depth(6)]), write(','), | |
| 515 | write_term(G,[max_depth(5)]), write(')'),nl. | |
| 516 | print_goal(A) :- write(' '), write_term(A,[max_depth(5)]), nl. % avoid printing huge terms | |
| 517 | %print_goal(A) :- format(' ~p~n',[A]). | |
| 518 | ||
| 519 | ||
| 520 | % ----------------------------------- | |
| 521 | ||
| 522 | % possible applicaton: inspect size of terms transmitted to ProB2: | |
| 523 | % prob2_interface:get_machine_formulas(Formulas), tell('formulasnest.pl'), nested_print_term(Formulas,6),told. | |
| 524 | ||
| 525 | nested_print_term(Term) :- nested_print_term(Term,10). | |
| 526 | nested_print_term(Term,MaxNest) :- nested_print_term(0,MaxNest,Term). | |
| 527 | ||
| 528 | indentws(0) :- !. | |
| 529 | indentws(X) :- X>0, write(' '), X1 is X-1, indentws(X1). | |
| 530 | ||
| 531 | :- use_module(library(terms),[term_size/2]). | |
| 532 | nested_print_term(CurNesting,_Max,T) :- var(T),!, indent(CurNesting),write(T),nl. | |
| 533 | nested_print_term(CurNesting,_,T) :- atom(T), !, atom_length(T,Len), | |
| 534 | indent(CurNesting), | |
| 535 | (Len > 100 | |
| 536 | -> atom_codes(T,Codes), length(Prefix,50), append(Prefix,_,Codes), | |
| 537 | format('atom: len(~w): ~s...~n',[Len,Prefix]) | |
| 538 | ; format('~w~n',[T])). | |
| 539 | nested_print_term(CurNesting,_,T) :- number(T),!, indent(CurNesting), format('~w~n',[T]). | |
| 540 | nested_print_term(CurNesting,MaxNesting,T) :- CurNesting >= MaxNesting,!, | |
| 541 | term_size(T,Sz), functor(T,F,N), | |
| 542 | indentws(CurNesting), | |
| 543 | (Sz>25000 -> Msg='****' ; Msg=''), format('~w/~w :: sz(~w)~w~n',[F,N,Sz,Msg]). | |
| 544 | nested_print_term(CurNesting,MaxNesting,T) :- | |
| 545 | list_skeleton_size(T,Len,closed), term_size(T,Sz),!, | |
| 546 | indentws(CurNesting),format('[ list_len_sz(~w,~w)~n',[Len,Sz]), | |
| 547 | C1 is CurNesting+1, | |
| 548 | maplist(nested_print_term(C1,MaxNesting),T), | |
| 549 | indentws(CurNesting),format('] list_len(~w)~n',[Len]). | |
| 550 | nested_print_term(CurNesting,MaxNesting,T) :- | |
| 551 | term_size(T,Sz), functor(T,F,N), | |
| 552 | indentws(CurNesting),format('~w/~w( sz(~w)~n',[F,N,Sz]), | |
| 553 | T =.. [_|Args], C1 is CurNesting+1, | |
| 554 | maplist(nested_print_term(C1,MaxNesting),Args), | |
| 555 | indentws(CurNesting),format('~w/~w)~n',[F,N]). | |
| 556 | ||
| 557 | ||
| 558 | % a version of write_term_to_codes that nests the Prolog term | |
| 559 | nested_write_term_to_codes(Term,Codes) :- | |
| 560 | nested_write(Term,0,Codes,[]). | |
| 561 | ||
| 562 | :- use_module(library(codesio)). | |
| 563 | % Note: write_term_to_codes(+Term, -Codes, +Options) with option indented(true) only indents clauses | |
| 564 | ||
| 565 | nested_write(Atom,Level) --> {simple(Atom)},!, indent(Level), format_to_codes('~q',[Atom]). | |
| 566 | nested_write(Term,Level) --> {Level>40},!, indent(Level), format_to_codes('~q',[Term]). | |
| 567 | nested_write([H|T],Level) --> !, | |
| 568 | indent(Level), "[\n", | |
| 569 | {L1 is Level+1}, | |
| 570 | l_nested_write([H|T],L1), | |
| 571 | indent(Level), "]". | |
| 572 | nested_write(Compound,Level) --> {decompose_compound(Compound,F,Args)}, | |
| 573 | indent(Level), | |
| 574 | ({maplist(simple,Args)} | |
| 575 | -> format_to_codes('~q',[Compound]) | |
| 576 | ; write_to_codes(F), "(\n", | |
| 577 | {L1 is Level+1}, | |
| 578 | l_nested_write(Args,L1), | |
| 579 | indent(Level), ")" | |
| 580 | ). | |
| 581 | ||
| 582 | decompose_compound(Module:Compound,Functor,Args) :- nonvar(Compound), !, | |
| 583 | Compound =.. [F|Args], Functor = Module:F. | |
| 584 | decompose_compound(Compound,F,Args) :- Compound =.. [F|Args]. | |
| 585 | ||
| 586 | indent(0) --> !, "". | |
| 587 | indent(L) --> {L>0, L1 is L-1}, " ", indent(L1). | |
| 588 | ||
| 589 | l_nested_write([],_) --> "". | |
| 590 | l_nested_write([H],Level) --> !, | |
| 591 | nested_write(H,Level), "\n". | |
| 592 | l_nested_write([H|T],Level) --> | |
| 593 | nested_write(H,Level), ",\n", | |
| 594 | l_nested_write(T,Level). | |
| 595 | ||
| 596 | % use_module(probsrc(tools_printing)), nested_write_term_to_codes(f(a,[0,s(X)]),C), format('~s~n',[C]). | |
| 597 | ||
| 598 | tcltk_nested_read_prolog_file_as_codes(File,list(Codes)) :- | |
| 599 | open(File, read, Stream, [encoding(utf8)]), | |
| 600 | call_cleanup(nested_read_terms(Stream, Codes, []), close(Stream)). | |
| 601 | ||
| 602 | nested_read_terms(Stream) --> {safe_read_stream(Stream,Term)}, | |
| 603 | ({Term=end_of_file} -> "\n" | |
| 604 | ; nested_write(Term,0), ".\n", | |
| 605 | nested_read_terms(Stream) | |
| 606 | ). | |
| 607 | ||
| 608 | safe_read_stream(Stream,T) :- | |
| 609 | catch(read(Stream,T), E, ( | |
| 610 | error_manager:add_error(nested_read_prolog_file_as_codes,'Exception reading term from file:',E), | |
| 611 | T=end_of_file)). | |
| 612 | ||
| 613 | % write_canonical that generates a code list | |
| 614 | %% write_canonical_to_codes(+Term, -Codes) | |
| 615 | write_canonical_to_codes(Term, Codes) :- write_canonical_to_codes(Term, Codes, []). | |
| 616 | ||
| 617 | write_canonical_to_codes(Term, Codes, CodesOut) :- | |
| 618 | write_term_to_codes(Term,Codes,CodesOut,[quoted(true),ignore_ops(true),quoted_charset(portable)]). | |
| 619 | ||
| 620 | ||
| 621 | % A variant of write_canonical_to_codes that writes lists with square-brackets. | |
| 622 | %% write_canonical_to_codes(+Term, -Codes) | |
| 623 | better_write_canonical_to_codes(Term, Codes) :- better_write_canonical_to_codes(Term, Codes, []). | |
| 624 | ||
| 625 | better_write_canonical_to_codes(Term, Codes, CodesOut) :- | |
| 626 | proper_list(Term), | |
| 627 | !, | |
| 628 | Codes = [0'[ | C1], | |
| 629 | better_write_canonical_to_codes_list(Term, C1, [0'] | CodesOut]). | |
| 630 | better_write_canonical_to_codes(Term, Codes, CodesOut) :- | |
| 631 | compound(Term), | |
| 632 | !, | |
| 633 | Term =.. [Functor | Args], | |
| 634 | write_canonical_to_codes(Functor, Codes, C1), | |
| 635 | (length(Args, L), L=0 -> CodesOut=C1 ; ( | |
| 636 | C1 = [0'( | C2], | |
| 637 | better_write_canonical_to_codes_list(Args, C2, [0') | CodesOut]) | |
| 638 | )). | |
| 639 | better_write_canonical_to_codes(Term, Codes, CodesOut) :- | |
| 640 | !, % fallback for variables and atomic terms | |
| 641 | write_canonical_to_codes(Term, Codes, CodesOut). | |
| 642 | ||
| 643 | better_write_canonical_to_codes_list([], Codes, CodesOut) :- | |
| 644 | !, | |
| 645 | CodesOut=Codes. | |
| 646 | better_write_canonical_to_codes_list([H|T], Codes, CodesOut) :- | |
| 647 | !, | |
| 648 | better_write_canonical_to_codes(H, Codes, C1), | |
| 649 | (T=[] -> C1=C2 ; C1=[0', | C2]), | |
| 650 | better_write_canonical_to_codes_list(T, C2, CodesOut). | |
| 651 | ||
| 652 | proper_list(L) :- nonvar(L), L=[], !. | |
| 653 | proper_list(L) :- nonvar(L), L=[_|T], !, proper_list(T). |