irrelevance.pl 8.84 KB
Newer Older
Siebers, Michael's avatar
Siebers, Michael committed
1
:- ensure_loaded(requests).
2
:- ensure_loaded(error_handling).
3
:- ensure_loaded(type_defs).
4
5
6
7
:- use_module(src/explanations).

:- multifile user:irrelevant/1.

8
9
10
%! do_handle_irrelevant_file(+Parameters:dict, -AbsPath:atom) is semidet.
%
% True if the item associated with the absolute path given in Parameters
Siebers, Michael's avatar
Siebers, Michael committed
11
% is irrelevant. The absolute path is accessed using the dict key `abs_path`.
12
% An error is thrown if the dict does not contain this key, if its value
13
14
% is not an atom representing an absolute file path, or if the dict contains 
% any other keys.
15
% 
Siebers, Michael's avatar
Siebers, Michael committed
16
% @throws parse_error(Reason, Location) if Parameters cannot be parsed
17
18
19
20
% @error api_error(_, parameter_error) if the format of Parameters is erroneous,
%                                      e.g if it is unbound, no dict, or if any
%                                      value read is unbound.
% @error api_error(_, implementation_error) if checking the irrelevance itself
Siebers, Michael's avatar
Siebers, Michael committed
21
%                                           throws an exception.
22
do_handle_irrelevant_file(Parameters, AbsPath) :-
23
    abs_path_type(AbsPathType),
24
    parse_parameters(Parameters, 
25
                     dict([abs_path:AbsPathType]),
26
                     Parsed),
27
    AbsPath=Parsed.abs_path,
28
29
    catch(user:irrelevant(AbsPath),
          E,
30
          api_error(implementation_error,
31
                    [call(user:irrelevant(AbsPath)), cause(E)])
32
    ).
33
34
35



36
37
38
39
40
%! do_handle_explain(++Parameters:dict, --Result:list, --More:atom) is det
%
% Generates textual explanations for the irrelevance of a file. Parameters may
% contain the following keys:
%
Siebers, Michael's avatar
Siebers, Michael committed
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
%   $ abs_path (required, atom):
%   The absolute path of the file which's irrelevance shall be explained
%
%   $ limit (optional, integer, default=5):
%   The maximal number of explanations to create.
%
% The Result is a list of explanation(AbsPath, Message, Details, References) terms.
% The list might be empty. In these terms 
%  
%   $ AbsPath: is bound to the absolute path of the item which's irrelevance is explained.  
%   $ Message: is a list which's elements are either strings or a term reference(DescribingString, ReferenceID).
%   $ DescribingString: a string
%   $ ReferenceID: an integer
%   $ Details: is a list of =Message=-type lists.
%   $ References: is a list of ref(ReferenceID, ListOfEntities) terms
%   $ ListOfEntities: a list of entity(NameOfEntity,NameOfProperty) terms
%   $ NameOfEntity: the absolute path of a file or directory
%   $ NameOfProperty: an atom describing the property of the entity which is referenced
%
% If there are more explanations available than there are in Result, More is bound to 'true'. Otherwise, More is bound to 'false'.
61
62
63
64
% 
% @throws not_irrelevant(AbsPath) If the provided absolut path denotes a file 
%         which is not irrelevant. Then, by definition, no explanation may be 
%         created.
65
% @throws parse_error(Reason, Location) if Parameters cannot be parsed
66
67
% @error api_error(_, implementation_error) if generating the explanation itself
%        throws an exception
Siebers, Michael's avatar
Siebers, Michael committed
68
69
70
% @error api_error(_, parameter_error) if Parameters is unbound, if 
%                                       any value read from Parameters is
%                                       unbound, or Result or More are bound
71
do_handle_explain(Parameters, Result, More) :-
72
    abs_path_type(AbsPathType),
73
    parse_parameters(Parameters, 
74
                     dict([abs_path:AbsPathType, limit:positive_integer=5]),
75
                     Parsed),
76
77
    (   var(Result)
    ->  true
78
79
80
    ;   api_error(parameter_error, [
                error(uninstantiation_error(Result)),
                location("second parameter of predicate") ])
81
82
83
    ),
    (   var(More)
    ->  true
84
85
86
    ;   api_error(parameter_error, [
                error(uninstantiation_error(More)),
                location("third parameter of predicate") ])
87
88
    ),
    catch(
89
        do_handle_explain_(Parsed.abs_path, Parsed.limit, Result, More),
90
91
92
        E,
        (   E=fails(irrelevant(Path))
        ->  throw(not_irrelevant(Path))
93
        ;   api_error(implementation_error, [
94
95
                            call(do_handle_explain_(Parsed.abs_path, 
                                    Parsed.limit, Result, More)),
96
97
98
99
                            error(E)
            ])
        )
    ).
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
do_handle_explain_(AbsPath, Limit, Result, More) :-
    findnsols(Limit,
              explanation(AbsPath,Message, Details, References),
              explanations:explain(irrelevant(AbsPath), Message, Details, References),
              Result),
    deterministic(LastAnswer),
    (   LastAnswer==true  % other responses are not explicitly documented
    ->  More=false
    ;   More=true
    ), !.

        
%! explanation_to_json(++Explanation, --JSON) is det
%
% Transforms an explanation to a term suitable to be transformed into a JSON
% string. The sub-transformations of the explanation message and details are
% carried out by message_to_json/2. The references are transformed by
% reference_to_json/2.
%
% @see do_handle_explain/3 for the expected format of Explanation
% @error instantiation_error if Explanation or any part of it is unbound
% @error type_error(Type,Part) if Explanation or any Part of it has not the 
%        expected type
% @error uninstantiation_error(Part) if (part of) JSON is instantiated and 
%        cannot be unified with the correct answer.
explanation_to_json(explanation(AbsPath,Message,Details,References),
                            JSONDict) :-
    text_to_string(AbsPath,AbsPathStr),
    message_to_json(Message, MessageJSON),
    must_be(list,Details),
    maplist(message_to_json,Details, DetailsJSON),
    reference_to_json(References, ReferencesJSON),
    (   JSONDict = explanation{
                               abs_path: AbsPathStr,
                               reasoning: MessageJSON,
                               reasoning_details: DetailsJSON,
                               references: ReferencesJSON
                   }
    -> true
    ; uninstantiation_error(JSONDict)
    ), !.
explanation_to_json(Explanation,_JSON) :-
    type_error(explanation(_,_,_,_),Explanation).             

145
   
146
    
Siebers, Michael's avatar
Siebers, Michael committed
147
%! message_to_json(++Message:list, --JSON) is det.
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
%
% Transforms an explanation (detailed) message to a term suitable to be
% transformed into a JSON string.
%
% @see do_handle_explain/3 for the expected format of Message
% @error instantiation_error if Message or any part of it is unbound
% @error type_error(Type,Part) if Message or any Part of it has not the expected
%        type
% @error uninstantiation_error(Part) if (part of) JSON is instantiated and 
%        cannot be unified with the correct answer.
message_to_json(Message, JSON) :-
    must_be(list, Message),
    maplist(message_to_json_, Message, JSONTrue),
    (   JSON=JSONTrue
    -> true
    ; uninstantiation_error(JSON)
    ).
    
message_to_json_(reference(M,Ref), JSON) :-
    text_to_string(M, MStr),
    must_be(integer, Ref),
    dict_create(J, message, [text=MStr, ref_id=Ref]),
    (   JSON = J
    ->  true
    ;   uninstantiation_error(JSON)
    ),
    !.
message_to_json_(M, JSON) :-
    text_to_string(M, MStr),
    dict_create(J, message, [text=MStr]),
    (   JSON = J
    ->  true
    ;   uninstantiation_error(JSON)
    ).

Siebers, Michael's avatar
Siebers, Michael committed
183
%! reference_to_json(++References:list, --JSON) is det.
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
%
% Transforms explanation references to a term suitable to be
% transformed into a JSON string.
%
% @see do_handle_explain/3 for the expected format of References
% @error instantiation_error if References or any part of it is unbound
% @error type_error(Type,Part) if References or any Part of it has not the expected type
% @error domain_error(non_empty_list,[]) if any reference has an empty list of entities.
% @error uninstantiation_error(Part) if (part of) JSON is instantiated and cannot be 
%        unified with the correct answer.
reference_to_json(References, JSON) :-
    must_be(list, References),
    maplist(reference_to_json_, References, JSONTrue),
    (   JSON=JSONTrue
    -> true
    ; uninstantiation_error(JSON)
    ).
    
reference_to_json_(ref(RefId,ListOfEntities), JSONDict) :-
    must_be(integer,RefId),
    must_be(list, ListOfEntities),
    maplist(entity_to_json, ListOfEntities, JSONEntities), 
    (   JSONDict = reference{id: RefId, referenced_entities: JSONEntities}
    ->  true
    ;   uninstantiation_error(JSONDict)
    ),
    !.
reference_to_json_(Reference,_JSON) :-
    type_error(ref(_,_), Reference).

214
215
216
217
218
219
220
%! entity_to_json(Entity, -JSON:dict) is det.
%
% Transforms an entity specification into a dict. Entity is a term
%
%   * entity(++Path, ++Property)
%     an entity specification references the property Property of an entity 
%     which is given by its absolute path
221
222
223
224
225
226
227
228
229
230
231
232
entity_to_json(entity(Path, Property), 
                            entity{abs_path: PathStr, property: PropertyStr}) :-
    text_to_string(Path, PathStr),
    text_to_string(Property, PropertyStr),
    !.
entity_to_json(Entity,JSON) :-
    (   var(JSON)
    ->  type_error(entity(_,_), Entity)
    ;   uninstantiation_error(JSON)
    ).