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

:- multifile user:irrelevant/1.

7
8
9
%! 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
10
% is irrelevant. The absolute path is accessed using the dict key `abs_path`.
11
% An error is thrown if the dict does not contain this key, if its value
Siebers, Michael's avatar
Siebers, Michael committed
12
% is not an atom or if the dict contains any other keys.
13
% 
Siebers, Michael's avatar
Siebers, Michael committed
14
% @throws parse_error(Reason, Location) if Parameters cannot be parsed
15
16
17
18
% @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
19
%                                           throws an exception.
20
do_handle_irrelevant_file(Parameters,AbsPath) :-
21
    abs_path_type(AbsPathType),
22
    parse_parameters(Parameters, 
23
                     dict([abs_path:AbsPathType]),
24
                     Parsed),
25
    AbsPath=Parsed.abs_path,
26
27
    catch(user:irrelevant(AbsPath),
          E,
28
          api_error(implementation_error,
29
                    [call(user:irrelevant(AbsPath)), cause(E)])
30
    ).
31
32
33



34
35
36
37
38
%! 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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
%   $ 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'.
59
60
61
62
% 
% @throws not_irrelevant(AbsPath) If the provided absolut path denotes a file 
%         which is not irrelevant. Then, by definition, no explanation may be 
%         created.
63
% @throws parse_error(Reason, Location) if Parameters cannot be parsed
64
65
% @error api_error(_, implementation_error) if generating the explanation itself
%        throws an exception
Siebers, Michael's avatar
Siebers, Michael committed
66
67
68
% @error api_error(_, parameter_error) if Parameters is unbound, if 
%                                       any value read from Parameters is
%                                       unbound, or Result or More are bound
69
do_handle_explain(Parameters, Result, More) :-
70
    parse_parameters(Parameters, 
71
                     dict([abs_path:atom, limit:positive_integer=5]),
72
                     Parsed),
73
74
    (   var(Result)
    ->  true
75
76
77
    ;   api_error(parameter_error, [
                error(uninstantiation_error(Result)),
                location("second parameter of predicate") ])
78
79
80
    ),
    (   var(More)
    ->  true
81
82
83
    ;   api_error(parameter_error, [
                error(uninstantiation_error(More)),
                location("third parameter of predicate") ])
84
85
    ),
    catch(
86
        do_handle_explain_(Parsed.abs_path, Parsed.limit, Result, More),
87
88
89
        E,
        (   E=fails(irrelevant(Path))
        ->  throw(not_irrelevant(Path))
90
        ;   api_error(implementation_error, [
91
92
                            call(do_handle_explain_(Parsed.abs_path, 
                                    Parsed.limit, Result, More)),
93
94
95
96
                            error(E)
            ])
        )
    ).
97

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

142
   
143
    
Siebers, Michael's avatar
Siebers, Michael committed
144
%! message_to_json(++Message:list, --JSON) is det.
145
146
147
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
%
% 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
180
%! reference_to_json(++References:list, --JSON) is det.
181
182
183
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
%
% 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).

211
212
213
214
215
216
217
%! 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
218
219
220
221
222
223
224
225
226
227
228
229
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)
    ).