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