36
37:- module(prolog_explain,
38 [ explain/1,
39 explain/2
40 ]). 41:- autoload(library(apply),[maplist/2,maplist/3]). 42:- autoload(library(lists),[flatten/2]). 43:- autoload(library(prolog_code), [pi_head/2]). 44
45:- if(exists_source(library(pldoc/man_index))). 46:- autoload(library(pldoc/man_index), [man_object_property/2]). 47:- endif. 48
69
76
77explain(Item) :-
78 explain(Item, Explanation),
79 print_message(information, explain(Explanation)),
80 fail.
81explain(_).
82
83 86
92
93explain(Var, [isa(Var, 'unbound variable')]) :-
94 var(Var),
95 !.
96explain(I, [isa(I, 'an integer')]) :-
97 integer(I),
98 !.
99explain(F, [isa(F, 'a floating point number')]) :-
100 float(F),
101 !.
102explain(Q, [isa(Q, 'a rational (Q) number')]) :-
103 rational(Q),
104 !.
105explain(S, [isa(S, 'a string')]) :-
106 string(S),
107 !.
108explain([], [isa([], 'a special constant denoting an empty list')]) :-
109 !.
110explain(A, [isa(A, 'an atom')]) :-
111 atom(A).
112explain(A, Explanation) :-
113 atom(A),
114 current_op(Pri, F, A),
115 op_type(F, Type),
116 Explanation = [ isa(A, 'a ~w (~w) operator of priority ~d'-[Type, F, Pri]) ].
117explain(A, Explanation) :-
118 atom(A),
119 !,
120 explain_atom(A, Explanation).
121explain([H|T], Explanation) :-
122 List = [H|T],
123 is_list(T),
124 !,
125 length(List, L),
126 ( Explanation = [ isa(List, 'a proper list with ~d elements'-[L]) ]
127 ; maplist(printable, List),
128 Explanation = [ indent, 'Text is "~s"'-[List] ]
129 ).
130explain(List, Explanation) :-
131 List = [_|_],
132 !,
133 length(List, L),
134 !,
135 Explanation = [isa(List, 'is a not-closed list with ~d elements'-[L])].
136explain(Name//NTArity, Explanation) :-
137 atom(Name),
138 integer(NTArity),
139 NTArity >= 0,
140 !,
141 Arity is NTArity + 2,
142 explain(Name/Arity, Explanation).
143explain(Name/Arity, Explanation) :-
144 atom(Name),
145 integer(Arity),
146 Arity >= 0,
147 !,
148 functor(Head, Name, Arity),
149 known_predicate(Module:Head),
150 ( Module == system
151 -> true
152 ; \+ predicate_property(Module:Head, imported_from(_))
153 ),
154 explain_predicate(Module:Head, Explanation).
155explain(Module:Name/Arity, Explanation) :-
156 atom(Module), atom(Name), integer(Arity),
157 !,
158 functor(Head, Name, Arity),
159 explain_predicate(Module:Head, Explanation).
160explain(Module:Head, Explanation) :-
161 callable(Head),
162 !,
163 explain_predicate(Module:Head, Explanation).
164explain(Dict, Explanation) :-
165 is_dict(Dict, Tag),
166 !,
167 Explanation = [isa(Dict, 'a dict with tag ~q'-[Tag]) ].
168explain(Term, Explanation) :-
169 compound(Term),
170 compound_name_arity(Term, _Name, Arity),
171 numbervars(Term, 0, _, [singletons(true)]),
172 Explanation = [isa(Term, 'is a compound term with arity ~D'-[Arity])].
173explain(Term, Explanation) :-
174 explain_functor(Term, Explanation).
175
181
182known_predicate(M:Head) :-
183 var(M),
184 current_predicate(_, M2:Head),
185 ( predicate_property(M2:Head, imported_from(M))
186 -> true
187 ; M = M2
188 ),
189 !.
190known_predicate(Pred) :-
191 predicate_property(Pred, undefined).
192known_predicate(_:Head) :-
193 functor(Head, Name, Arity),
194 '$in_library'(Name, Arity, _Path).
195
196op_type(X, prefix) :-
197 atom_chars(X, [f, _]).
198op_type(X, infix) :-
199 atom_chars(X, [_, f, _]).
200op_type(X, postfix) :-
201 atom_chars(X, [_, f]).
202
203printable(C) :-
204 integer(C),
205 code_type(C, graph).
206
207
208 211
212explain_atom(A, Explanation) :-
213 referenced(A, Explanation).
214explain_atom(A, Explanation) :-
215 current_predicate(A, Module:Head),
216 ( Module == system
217 -> true
218 ; \+ predicate_property(Module:Head, imported_from(_))
219 ),
220 explain_predicate(Module:Head, Explanation).
221explain_atom(A, Explanation) :-
222 predicate_property(Module:Head, undefined),
223 functor(Head, A, _),
224 explain_predicate(Module:Head, Explanation).
225
226
227 230
231explain_functor(Head, Explanation) :-
232 referenced(Head, Explanation).
233explain_functor(Head, Explanation) :-
234 current_predicate(_, Module:Head),
235 \+ predicate_property(Module:Head, imported_from(_)),
236 explain_predicate(Module:Head, Explanation).
237explain_functor(Head, Explanation) :-
238 predicate_property(M:Head, undefined),
239 ( functor(Head, N, A),
240 Explanation = [ pi(M:N/A), 'is an undefined predicate' ]
241 ; referenced(M:Head, Explanation)
242 ).
243
244
245 248
249lproperty(built_in, [' built-in']).
250lproperty(dynamic, [' dynamic']).
251lproperty(multifile, [' multifile']).
252lproperty(transparent, [' meta']).
253
254tproperty(Pred, [' imported from module ', module(Module)]) :-
255 predicate_property(Pred, imported(Module)).
256tproperty(Pred, [' defined in ', url(File:Line)]) :-
257 predicate_property(Pred, file(File)),
258 predicate_property(Pred, line_count(Line)).
259tproperty(Pred, [' that can be autoloaded']) :-
260 predicate_property(Pred, autoload).
261
263
264explain_predicate(Pred, Explanation) :-
265 Pred = Module:Head,
266 functor(Head, Name, Arity),
267 ( predicate_property(Pred, non_terminal)
268 -> What = 'non-terminal'
269 ; What = 'predicate'
270 ),
271 ( predicate_property(Pred, undefined)
272 -> Explanation = [ pi(Module:Name/Arity),
273 ansi([bold,fg(default)], ' is an undefined ~w', [What])
274 ]
275 ; ( var(Module)
276 -> U0 = [ pi(Name/Arity),
277 ansi([bold,fg(default)], ' is a', [])
278 ]
279 ; U0 = [ pi(Module:Name/Arity),
280 ansi([bold,fg(default)], ' is a', [])
281 ]
282 ),
283 findall(Utter, (lproperty(Prop, Utter),
284 predicate_property(Pred, Prop)),
285 U1),
286 U2 = [ansi([bold,fg(default)], ' ~w', [What]) ],
287 findall(Utter, tproperty(Pred, Utter),
288 U3),
289 flatten([U0, U1, U2, U3], Explanation)
290 ).
291:- if(current_predicate(man_object_property/2)). 292explain_predicate(Pred, Explanation) :-
293 Pred = _Module:Head,
294 functor(Head, Name, Arity),
295 man_object_property(Name/Arity, summary(Summary)),
296 source_file(Pred, File),
297 current_prolog_flag(home, Home),
298 sub_atom(File, 0, _, _, Home),
299 Explanation = [indent, 'Summary: "~w"'-[Summary] ].
300:- endif. 301explain_predicate(Pred, Explanation) :-
302 referenced(Pred, Explanation).
303
304 307
308referenced(Term, Explanation) :-
309 current_predicate(_, Module:Head),
310 ( predicate_property(Module:Head, built_in)
311 -> current_prolog_flag(access_level, system)
312 ; true
313 ),
314 \+ predicate_property(Module:Head, imported_from(_)),
315 Module:Head \= help_index:predicate(_,_,_,_,_),
316 nth_clause(Module:Head, N, Ref),
317 '$xr_member'(Ref, Term),
318 utter_referenced(Module:Head, N, Ref,
319 'Referenced', Explanation).
320referenced(_:Head, Explanation) :-
321 current_predicate(_, Module:Head),
322 ( predicate_property(Module:Head, built_in)
323 -> current_prolog_flag(access_level, system)
324 ; true
325 ),
326 \+ predicate_property(Module:Head, imported_from(_)),
327 nth_clause(Module:Head, N, Ref),
328 '$xr_member'(Ref, Head),
329 utter_referenced(Module:Head, N, Ref,
330 'Possibly referenced', Explanation).
331
332utter_referenced(_Module:class(_,_,_,_,_,_), _, _, _, _) :-
333 current_prolog_flag(xpce, true),
334 !,
335 fail.
336utter_referenced(_Module:lazy_send_method(_,_,_), _, _, _, _) :-
337 current_prolog_flag(xpce, true),
338 !,
339 fail.
340utter_referenced(_Module:lazy_get_method(_,_,_), _, _, _, _) :-
341 current_prolog_flag(xpce, true),
342 !,
343 fail.
344utter_referenced(From, _, _, _, _) :-
345 hide_reference(From),
346 !,
347 fail.
348utter_referenced(pce_xref:defined(_,_,_), _, _, _, _) :-
349 !,
350 fail.
351utter_referenced(pce_xref:called(_,_,_), _, _, _, _) :-
352 !,
353 fail.
354utter_referenced(pce_principal:send_implementation(_, _, _),
355 _, Ref, Text, Explanation) :-
356 current_prolog_flag(xpce, true),
357 !,
358 xpce_method_id(Ref, Id),
359 Explanation = [indent, '~w from ~w'-[Text, Id]].
360utter_referenced(pce_principal:get_implementation(Id, _, _, _),
361 _, Ref, Text, Explanation) :-
362 current_prolog_flag(xpce, true),
363 !,
364 xpce_method_id(Ref, Id),
365 Explanation = [indent, '~w from ~w'-[Text, Id]].
366utter_referenced(Head, N, Ref, Text, Explanation) :-
367 clause_property(Ref, file(File)),
368 clause_property(Ref, line_count(Line)),
369 !,
370 pi_head(PI, Head),
371 Explanation = [ indent,
372 '~w from ~d-th clause of '-[Text, N],
373 pi(PI), ' at ', url(File:Line)
374 ].
375utter_referenced(Head, N, _Ref, Text, Explanation) :-
376 pi_head(PI, Head),
377 Explanation = [ indent,
378 '~w from ~d-th clause of '-[Text, N],
379 pi(PI)
380 ].
381
382xpce_method_id(Ref, Id) :-
383 clause(Head, _Body, Ref),
384 strip_module(Head, _, H),
385 arg(1, H, Id).
386
387hide_reference(pce_xref:exported(_,_)).
388hide_reference(pce_xref:defined(_,_,_)).
389hide_reference(pce_xref:called(_,_,_)).
390hide_reference(prolog_xref:called(_,_,_,_,_)).
391hide_reference(prolog_xref:pred_mode(_,_,_)).
392hide_reference(prolog_xref:exported(_,_)).
393hide_reference(prolog_xref:dynamic(_,_,_)).
394hide_reference(prolog_xref:imported(_,_,_)).
395hide_reference(prolog_xref:pred_comment(_,_,_,_)).
396hide_reference(_:'$mode'(_,_)).
397hide_reference(_:'$pldoc'(_,_,_,_)).
398hide_reference(prolog_manual_index:man_index(_,_,_,_,_)).
399
400
401 404
405:- multifile
406 prolog:message//1. 407
408prolog:message(explain(Explanation)) -->
409 report(Explanation).
410
411report(Explanation) -->
412 { string(Explanation),
413 !,
414 split_string(Explanation, "\n", "", Lines)
415 },
416 lines(Lines).
417report(Explanation) -->
418 { is_list(Explanation) },
419 report_list(Explanation).
420
421lines([]) -->
422 [].
423lines([H]) -->
424 !,
425 [ '~s'-[H] ].
426lines([H|T]) -->
427 [ '~s'-[H], nl ],
428 lines(T).
429
430report_list([]) -->
431 [].
432report_list([H|T]) -->
433 report1(H),
434 report_list(T).
435
436report1(indent) -->
437 !,
438 [ '~t~6|'-[] ].
439report1(String) -->
440 { atomic(String) },
441 [ '~w'-[String] ].
442report1(Fmt-Args) -->
443 !,
444 [ Fmt-Args ].
445report1(url(Location)) -->
446 [ url(Location) ].
447report1(url(URL, Label)) -->
448 [ url(URL, Label) ].
449report1(pi(PI)) -->
450 { pi_nt(PI, NT) },
451 [ ansi(code, '~q', [NT]) ].
452report1(ansi(Style, Fmt, Args)) -->
453 [ ansi(Style, Fmt, Args) ].
454report1(isa(Obj, Fmt-Args)) -->
455 !,
456 [ ansi(code, '~p', [Obj]),
457 ansi([bold,fg(default)], ' is ', []),
458 ansi([bold,fg(default)], Fmt, Args)
459 ].
460report1(isa(Obj, Descr)) -->
461 [ ansi(code, '~p', [Obj]),
462 ansi([bold,fg(default)], ' is ~w', [Descr])
463 ].
464
465pi_nt(Module:Name/Arity, NT),
466 atom(Module), atom(Name), integer(Arity),
467 Arity >= 2,
468 functor(Head, Name, Arity),
469 predicate_property(Module:Head, non_terminal) =>
470 Arity2 is Arity - 2,
471 NT = Module:Name//Arity2.
472pi_nt(PI, NT) =>
473 NT = PI