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). |