1 :- module(prettyprinter, [pretty_print_error/1]).
2 :- use_module(plspec_logger).
3
4
5 pretty_print_error(fail(postcondition_violated(matched_pre(Pre),
6 violated_post(Post),
7 value(Val)))) :- !,
8 log(error, 'A postcondition was violated!', []),
9 log(error, 'The matched precondition was "~w".', [Pre]),
10 log(error, 'However, the postcondition "~w" does not hold.', [Post]),
11 translate_term_into_atom_with_max_depth(Val,ValLimited),
12 log(error, 'The offending value was: ~p.', [ValLimited]),
13 (debug_argument(Post,'$root',[Val]) -> true ; true).
14 pretty_print_error(fail(prespec_violated(specs(PreSpecs), values(Vals),
15 location(Functor)))) :- !,
16 log(error, 'No precondition was matched in ~w.', [Functor]),
17 log(error, 'Specified preconditions were: ~w.', [PreSpecs]),
18 translate_term_into_atom_with_max_depth(Vals,ValsLimited),
19 log(error, 'However, none of these is matched by: ~p.', [ValsLimited]),
20 (debug_arguments(PreSpecs,Functor,Vals) -> true ; true).
21 pretty_print_error(fail(spec_violated(spec(T), value(V), location(Location)))) :- !,
22 log(error, 'An invariant was violated in ~w.', [Location]),
23 log(error, 'The spec was: ~w.', [T]),
24 log(error, 'However, the value was bound to: ~w.', [V]).
25 pretty_print_error(fail(spec_not_found(spec(Spec)))) :- !,
26 %% TODO: not all failures include a location
27 log(error, 'Spec "~w" was not found.', [Spec]).
28 pretty_print_error(fail(spec_not_found(spec(Spec), location(Location)))) :- !,
29 log(error, '~nA spec for ~w was not found.', [Location]),
30 log(error, 'Spec "~w" was not found.', [Spec]).
31 pretty_print_error(X) :-
32 log(error, 'plspec raised an error term that is unhandled.', []),
33 log(error, '~p.', [X]).
34
35 :- use_module(validator,[valid/2,spec_indirection/2]).
36 :- use_module(library(lists),[include/3]).
37
38
39 % debug a single argument and a single specification
40 debug_argument(SpecDef,Position,Arg) :-
41 spec_indirection(SpecDef,SpecRHS),
42 debug_argument(SpecRHS,Position,Arg).
43 debug_argument(one_of(List),Position,Arg) :- !,
44 include(prettyprinter:spec_top_level_match(Arg),List,Matches),
45 (Matches==[]
46 -> get_functor(Arg,FA,NA),
47 log(error,'Value at ~w with functor ~w/~w does not match any case of ~w.',[Position,FA,NA,List])
48 ; Matches = [OneMatch]
49 -> debug_argument(OneMatch,Position,Arg)
50 ; fail).
51 debug_argument(compound(Spec),Position,Arg) :- !, functor(Spec,F,N),
52 get_functor(Arg,FA,NA),
53 ( F=FA,N=NA
54 -> Spec =.. [F|Specs], Arg =.. [F|Args],
55 l_debug_arguments(Specs,F/N,1,Args)
56 ; log(error,'Value at ~w with functor ~w/~w does not match functor ~w/~w of ~w.',[Position,FA,NA,F,N,Spec])
57 ).
58 debug_argument(list(Spec),Position,Arg) :- !,
59 ( Arg = [] -> fail
60 ; Arg = [H|T] ->
61 (debug_argument(Spec,Position,H) -> true
62 ; debug_argument(list(Spec),list(Position),T)
63 )
64 ; get_functor(Arg,FA,NA),
65 log(error,'Value at ~w with functor ~w/~w ist not a list.',[Position,FA,NA])
66 ).
67 debug_argument(atom(X),Position,Arg) :- !,
68 Arg \== X,
69 get_functor(Arg,FA,NA),
70 log(error,'Value at ~w with functor ~w/~w does not match atom ~w.',[Position,FA,NA,X]).
71 debug_argument(ground,Position,Arg) :- !,
72 term_variables(Arg,Vars), Vars \==[],
73 get_functor(Arg,FA,NA),
74 log(error,'Value at ~w with functor ~w/~w does have variables ~w and is not ground.',[Position,FA,NA,Vars]).
75 debug_argument(Basic,Position,Arg) :- basic_type(Basic), !,
76 \+ valid(Basic, Arg),
77 get_functor(Arg,FA,NA),
78 log(error,'Value at ~w with functor ~w/~w ist not ~w.',[Position,FA,NA,Basic]).
79
80
81 % basic plspec types:
82 basic_type(atom).
83 basic_type(atomic).
84 basic_type(float).
85 basic_type(integer).
86 basic_type(nonvar).
87 basic_type(number).
88 basic_type(var).
89
90 % check at the top-level whether a spec potentially matches an argument
91 spec_top_level_match(Arg,R) :- var(Arg),!,R==var.
92 spec_top_level_match(X,atom(X)) :- !.
93 spec_top_level_match(Arg,compound(Spec)) :- functor(Spec,F,N), functor(Arg,F,N).
94 spec_top_level_match(X,T) :- basic_type(T), valid(T,X),!.
95
96
97 % debug a list of argument and a list of specification alternatives
98 debug_arguments([SingleSpec],Position,Args) :-
99 % currently we only support debugging a single list
100 l_debug_arguments(SingleSpec,Position,1,Args).
101
102 % debug a list of specs and a list of (supposedly) matching arguments
103 l_debug_arguments([Spec1|TSpec],Position,ArgNr,[Arg1|TArgs]) :-
104 (valid(Spec1, Arg1)
105 -> A1 is ArgNr+1,
106 l_debug_arguments(TSpec,Position,A1,TArgs)
107 ; get_functor(Arg1,FA,NA),
108 log(error,'Argument ~w of ~w with functor ~w/~w does not match spec ~w.',[ArgNr,Position,FA,NA,Spec1]),
109 \+ basic_type(Spec1), % otherwise we will just repeat the error message
110 debug_argument(Spec1,Position,Arg1)
111 ).
112 % TO DO: treat things like one_of pattern matches
113
114 % a safe version of functor/3 which also works with variables
115 get_functor(X,F,N) :- var(X),!,F='$VAR',N = -1.
116 get_functor(X,F,N) :- functor(X,F,N).
117
118 :- use_module(library(codesio), [write_term_to_codes/3]).
119 translate_term_into_atom_with_max_depth(Term,Atom) :-
120 translate_term_into_atom_with_max_depth(Term,5,Atom).
121 translate_term_into_atom_with_max_depth(Term,_,Atom) :- atomic(Term),!,Atom=Term.
122 translate_term_into_atom_with_max_depth(Term,Limit,Atom) :-
123 write_term_to_codes(Term,Temp,[quoted(true),numbervars(true),max_depth(Limit)]),
124 atom_codes(Atom,Temp).