1 % (c) 2009-2017 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(units, [plugin_info/1]).
6
7 plugin_info([
8 % The name of the Plugin
9 name = 'Unit Analysis'
10 % Author is not used yet
11 ,author = 'Sebastian Krings'
12 % marks the plugin as unstable or stable
13 ,status = stable
14 % A list of supported file extensions together with a file description
15 % (They are not used here because the plugin is used for an already loaded
16 % file, see plugin_for_modes)
17 % ,file_extensions = [ ('Plugin demonstration spec', ['.xtl2', '.PPP']) ]
18 % The predicate that is used to load a file. The first argument is the
19 % filename, the second the file extension (like '.mch').
20 %,load_file = load_spec/1
21 % The transition skeleton initialises a transition variable in such
22 % way that only a part of all transitions match it. It is used to
23 % separate time outs and maximum number of computations for several
24 % transition classes (like different operations).
25 %,transition_skeleton = trans_skel/1
26 % The predicate that computes a transition in the state-space, its
27 % arguments are SourceState,Operation,DestinationState,ListOfOperationInfos.
28 ,transition = do_transition/4
29 % The predicate that computes an initial state, its arguments are
30 % Operation,DestinationState,ListOfOperationInfos.
31 ,initialisation = do_initialisation/3
32 % A predicate that returns a property for a given state, its arguments
33 % are State,Property.
34 ,state_property = find_state_property/2
35 % A predicate that receives a list of expressions,predicates and
36 % predicates on transitions as input (list of codes) and returns
37 % the parsed expressions.
38 % Its arguments are ListOfExpressionStrings,ListOfPredicateStrings,
39 % ListOfTransitionPredicateStrings,ListOfExpressions,ListOfPredicates,
40 % ListOfTransitionPredicates.
41 %,parser = parse_spec/6
42 % A predicate that computes a value of an expression (returned by
43 % the parser above) in a state. Its arguments are Expression,State
44 %,compute_expression = compute/2
45 % A predicate that evaluates a predicate (returned by the parser above)
46 % in a state. Its arguments are Predicate,State
47 %,evaluate_predicate = eval/2
48 % A predicate that evaluates a predicate (returned by the parser above)
49 % for a transition. Its arguments are Predicate,Transition
50 %,evaluate_transition = evaltrans/2
51 % A predicate that checks if a state is initialised. Its argument is a State
52 %,is_initialised_state = is_init/1
53 % Pretty-print a transition, the first argument is the transition, the
54 % second -the result- is a list of codes.
55 ,prettyprint_transition = pp_trans/2
56 % Pretty-print a property, the first argument is the property, the
57 % second -the result- is a list of codes.
58 ,prettyprint_property = pp_prop/2
59 ,prettyprint_value = pp_value/2
60 % Syntax colouring is defined by specifying a list of rules, each
61 % containing of a regular expression and a colour class
62 %,syntax_colouring = [pair('/\\*', '\\*/',syntax_comment),
63 % expression('(?=\\m)(start|trans|prop)(?=\\M)', syntax_keyword),
64 % expression('(?=\\m)(true|fail|atomic|nonvar|var|functor|op|is|ground|member|append|length)(?=\\M)|=', syntax_type),
65 % expression(':-|!|-->|;|\\.', syntax_assignment),
66 % expression('%(.*)', syntax_comment)]
67 ,plugin_for_modes = [b]
68 ,plugin_init=plugin_init/0
69 ,new_transition_notification=notify_new_transition/4
70 ,check_invariant=do_check_invariant/2
71 ,preferences=preference/4
72 ,output=output/3
73 ,internal_representation=internal_representation/1
74 ]).
75
76 :- public do_transition/4, do_initialisation/3, find_state_property/2,pp_trans/2, pp_prop/2, pp_value/2.
77 :- public plugin_init/0,notify_new_transition/4,do_check_invariant/2,preference/4,output/3,internal_representation/1.
78
79 :- use_module(library(samsort)).
80 :- use_module(library(lists)).
81
82 :- use_module(probsrc(tools)).
83 :- use_module(probsrc(specfile)).
84 :- use_module(probsrc(state_space)).
85 :- use_module(probsrc(bmachine)).
86 :- use_module(probsrc(preferences)).
87 :- use_module(probsrc(bmachine)).
88 :- use_module(probsrc(bmachine_structure)).
89 :- use_module(probsrc(pragmas)).
90 :- use_module(probsrc(coverage_tools_annotations),['$NOT_COVERED'/1]).
91 :- use_module(probsrc(error_manager)).
92
93 :- use_module(units_interpreter).
94 :- use_module(units_interpreter_helpers).
95 :- use_module(units_prettyprint).
96 :- use_module(units_tools).
97 :- use_module(units_alias).
98 :- use_module(units_domain).
99
100 :- use_module(probsrc(module_information)).
101 :- module_info(group,plugin_units).
102 :- module_info(description,'Units Plugin: This is an experimental plugin that analyses physical units stored in pragmas.').
103
104 :- dynamic iterations/1, static_result/2, units_error_context/1, tcltk_replay_sequence/1.
105
106 preference(fixpoint_on_load, true, 'Perform Static Fix-Point Check on Startup',bool).
107 preference(fixpoint_halt_on_error, true, 'Halt Fix-Point Search on Error',bool).
108 preference(fixpoint_max_iterations, 100, 'Max. Number of Iterations in Fix-Point Check', nat1).
109
110 output('Static Analysis Result', X, [tcltk,cli]) :-
111 (static_result(FixPointState,UnboundInConstraint)
112 -> pp_fixpoint_result(FixPointState,UnboundInConstraint,Output),
113 atom_codes(Output,X)
114 ; X = "no result yet"),
115 '$NOT_COVERED'('GUI code not used in automatic tests').
116
117 output('Grounded Result State', X, [eclipse]) :-
118 findall(Error,get_error(incorrect_unit_definition,Error),ListOfErrors),
119 ListOfErrors \= [], !,
120 % we have something to report. skip the rest
121 reset_errors,
122 X = error(ListOfErrors).
123
124 output('Grounded Result State', state(X), [eclipse]) :-
125 (static_result(FixPointState,_UnboundInConstraint)
126 -> maplist(pp(eventb),FixPointState,X)
127 ; X = []).
128
129 plugin_init :-
130 units_interpreter_helpers:retractall(cached_global_type(_,_)),
131 retractall(tcltk_replay_sequence(_)),
132 reset_tcltk_replay_sequence,
133 state_space:state_space_initialise,
134 (preference(plugin(units,fixpoint_on_load),true)
135 -> static_pre_check
136 ; true),
137 (replay_tcltk_sequence ; write('Could not replay Tcl/Tk Operation Sequence!'), nl).
138
139 static_pre_check :-
140 % pre check can only be performed on an initialised machine
141 bmachine:bmachine_is_precompiled,
142 statistics(walltime,[Start|_]),
143 retractall(iterations(_)),
144 assert(iterations(0)),
145 do_initialisation(Operation, State, [initialisation]),
146 append_to_tcltk_replay_sequence(Operation),
147 to_fixpoint(State,FixPointState),
148 retractall(static_result(_,_)),
149 extract_unbound_in_constraint(FixPointState,Unbound),
150 assert(static_result(FixPointState,Unbound)),
151 insert_result_in_machine,
152 iterations(It),
153 statistics(walltime, [End|_]),
154 Time is End - Start,
155 format('Fixpoint algorithm stopped after ~w iterations and ~w milliseconds ~n', [It, Time]).
156 static_pre_check :- write('Pre-Check failed!\n'), '$NOT_COVERED'('This should not happen').
157
158 extract_unbound_in_constraint([],[]).
159 extract_unbound_in_constraint([bind(Var,Val)|T], [Var|RT]) :-
160 involved_in_constraint(Val),
161 extract_unbound_in_constraint(T,RT).
162 extract_unbound_in_constraint([bind(_Var,_Val)|T], RT) :-
163 extract_unbound_in_constraint(T,RT).
164
165 to_fixpoint(State,FixPointState) :-
166 retract(iterations(LastIt)), preference(plugin(units,fixpoint_max_iterations),MaxIt),
167 LastIt < MaxIt
168 -> CurIt is LastIt + 1, assert(iterations(CurIt)), to_fixpoint2(State,FixPointState)
169 ; State=FixPointState, add_warning(units, 'Warning: Fixpoint algorithm stopped, because iteration limit was reached\n').
170
171 to_fixpoint2(concrete_constants(State),FixPointState) :-
172 !, do_transition(concrete_constants(State),Op,PossibleFixPointState,_Op2),
173 append_to_tcltk_replay_sequence(Op),
174 to_fixpoint(PossibleFixPointState,FixPointState).
175 to_fixpoint2(State,FixPointState) :-
176 findall(Operation, do_transition(State,_,_,[op(Operation)]), ListOfOperations),
177 copy_term(State,StoredState),
178 all_operations(ListOfOperations,State,PossibleFixPointState),
179 (
180 another_iteration(StoredState,PossibleFixPointState)
181 -> to_fixpoint(PossibleFixPointState,FixPointState)
182 ; FixPointState = PossibleFixPointState
183 ).
184
185 another_iteration(StoredState,PossibleFixPointState) :-
186 \+(halt_on_error), state_was_updated(StoredState,PossibleFixPointState).
187
188 halt_on_error :-
189 preference(plugin(units,fixpoint_halt_on_error),true),
190 state_error_exists.
191
192
193 all_operations([],State,State).
194 all_operations([Operation|T],InState,OutState) :-
195 copy_term(InState,StoredInState),
196 (do_transition(InState,OpSpec,IntermediateState,[op(Operation)])), !,
197 (state_was_updated(StoredInState,IntermediateState)
198 -> append_to_tcltk_replay_sequence(OpSpec)
199 ; true),%format('Operation ~w did not update~nInState:~n~w~nIntermediateState:~n~w~n', [OpSpec,StoredInState,IntermediateState])),
200 (halt_on_error
201 -> OutState=IntermediateState
202 ; all_operations(T,IntermediateState,OutState)).
203 all_operations([_Operation|T],InState,OutState) :-
204 all_operations(T,InState,OutState),
205 (halt_on_error
206 -> OutState=InState
207 ; all_operations(T,InState,OutState)),
208 add_error(units_all_operations, 'operation could not be evaluated'),
209 '$NOT_COVERED'('This should not happen').
210
211 do_initialisation(OpSetup, concrete_constants(ConstantsState), [initialisation]) :-
212 bmachine:bmachine_is_precompiled,
213 b_machine_has_constants_or_properties, !,
214 set_units_error_context(operation('SETUP_CONSTANTS',ConstantsState)),
215 init_user_defined_alias,
216 b_get_machine_constants(Constants),
217 units_build_initial_state(Constants,ConstantsState),
218 b_get_properties_from_machine(Properties),
219 units_check_boolean_expression(Properties,ConstantsState,[]),
220 ((get_preference(show_initialisation_arguments,true),extract_value_list(ConstantsState,Vals))
221 -> safe_univ(OpSetup,['$setup_constants'|Vals])
222 ; (OpSetup = '$setup_constants')
223 ).
224
225 do_initialisation(Operation,StateOut,[initialisation]) :-
226 bmachine:bmachine_is_precompiled,
227 \+ b_machine_has_constants_or_properties, !,
228 set_units_error_context(operation('$initialise_machine',StateOut)),
229 init_user_defined_alias,
230 b_get_machine_variables(Variables),
231 units_build_initial_state(Variables,VariablesState),
232 b_get_initialisation_from_machine(Init,_OType),
233 units_execute_statement(Init,[],VariablesState,StateOut,_Path),
234 specfile:create_initialisation_operation(StateOut,Operation).
235
236 do_transition(concrete_constants(StateIn),Operation,StateOut,[op(Operation)]) :-
237 bmachine:bmachine_is_precompiled, !,
238 set_units_error_context(operation('$initialise_machine',StateIn)),
239 b_get_machine_variables(Variables),
240 units_build_initial_state(Variables,VariablesState),
241 append(StateIn,VariablesState,State),
242 b_get_initialisation_from_machine(Init,_OType),
243 units_execute_statement(Init,[],State,UnsortedStateOut,_Path),
244 samsort(UnsortedStateOut,StateOut),
245 specfile:create_initialisation_operation(StateOut,Operation).
246
247 do_transition(State,OpSpec,NewState,[op(Operation)]) :-
248 bmachine:bmachine_is_precompiled,
249 b_top_level_operation(Operation),
250 set_units_error_context(operation(Operation,State)),
251 units_find_transition(Operation, State, NewState, Paras, Results),
252 (Paras \= []
253 -> OpSpec1 =.. [Operation|Paras]
254 ; OpSpec1 = Operation
255 ),
256 (Results \= []
257 -> OpSpec = '-->'(OpSpec1,Results)
258 ; OpSpec = OpSpec1
259 ),
260 execute_invariant(NewState).
261
262 notify_new_transition(FromID,TransID,DestID,_Exists) :-
263 %write(notify_new_transition(FromID,TransID,DestID,Exists)),nl,
264 state_space:transition(FromID,Action,TransID,DestID),
265 set_units_error_context(operation(Action,_)),
266 visited_expression(DestID,State),
267 check_state_add_error(DestID,State).
268
269 find_state_property(State,Property):-
270 member(bind(Var,Val),State),
271 Property = '='(Var,Val).
272
273 pp_trans(Transition,Pretty) :-
274 units_pp_trans(Transition,Pretty).
275
276 pp_prop(Property,Pretty) :-
277 units_pp_prop(Property,Pretty).
278
279 pp_value(Value,Pretty) :-
280 units_pp_value(Value,Pretty).
281
282 do_check_invariant(ID,_CurState) :-
283 %write(do_check_invariant(ID,CurState)), nl,
284 invariant_violated(ID), !.
285 do_check_invariant(ID,CurState) :-
286 set_units_error_context(checking_invariant),
287 state_corresponds_to_initialised_b_machine(CurState,CurBState),
288 execute_invariant(CurBState),
289 set_invariant_checked(ID).
290
291 execute_invariant(CurState) :-
292 set_units_error_context(checking_invariant),
293 b_get_properties_from_machine(Properties),
294 units_check_boolean_expression(Properties,CurState,[]),
295 b_get_invariant_from_machine(I),
296 units_check_boolean_expression(I,[],CurState),
297 set_units_error_context(checking_assertions),
298 b_get_static_assertions_from_machine(StaticAssertions),
299 units_check_list_of_boolean_expressions(StaticAssertions,[],CurState),
300 b_get_dynamic_assertions_from_machine(DynamicAssertions),
301 units_check_list_of_boolean_expressions(DynamicAssertions,[],CurState).
302
303 internal_representation(Rep) :-
304 full_b_machine(BMachine),
305 (static_result(_,_) -> true ; static_pre_check),
306 translate:translate_machine1(BMachine,(0,RepWithoutGlobals),(_,[])),
307 findall(alias(AliasName,Definition),
308 global_pragma(unit_alias,[AliasName|Definition]),
309 List),
310 global_pragma_output(List,OutputCodes),
311 append(OutputCodes,RepWithoutGlobals,Rep),
312 '$NOT_COVERED'('GUI code not used in automatic tests').
313
314 global_pragma_output([],[]) :- '$NOT_COVERED'('GUI code not used in automatic tests').
315 global_pragma_output([alias(AliasName,Definition)|T],Codes) :-
316 global_pragma_output(T,CodesTemp),
317 insert_spacer_and_join(Definition,DefinitionCodes),
318 ajoin(['/*@ unit_alias ', AliasName, ' ', DefinitionCodes, ' */'], CodePragma),
319 safe_atom_codes(CodePragma,CodePragmaCodes),
320 append(CodePragmaCodes,CodesTemp,Codes),
321 '$NOT_COVERED'('GUI code not used in automatic tests').
322
323 insert_result_in_machine :-
324 write('Pre-Check finished. Inserting results in machine'), nl,
325 static_result(Result,_),
326 full_b_machine(BMachine),
327 get_section(abstract_variables,BMachine,AbsVarsContent),
328 expand_variables(AbsVarsContent,ExpandedAbsVarsContent,Result),
329 write_section(abstract_variables,ExpandedAbsVarsContent,BMachine,BMachine2),
330 get_section(concrete_variables,BMachine2,ConcVarsContent),
331 expand_variables(ConcVarsContent,ExpandedConcVarsContent,Result),
332 write_section(concrete_variables,ExpandedConcVarsContent,BMachine2,BMachine3),
333 get_section(abstract_constants,BMachine3,AbsConstContent),
334 expand_variables(AbsConstContent,ExpandedAbsConstContent,Result),
335 write_section(abstract_constants,ExpandedAbsConstContent,BMachine3,BMachine4),
336 get_section(concrete_constants,BMachine4,ConcConstContent),
337 expand_variables(ConcConstContent,ExpandedConcConstContent,Result),
338 write_section(concrete_constants,ExpandedConcConstContent,BMachine4,BMachine5),
339 retractall(bmachine:machine(_,_)),
340 bmachine:assert_main_machine(BMachine5).
341
342 expand_variables([],[],_Result).
343 expand_variables([b(identifier(X),Type,Infos)|T],[b(identifier(X),Type,Infos2)|T2],Result) :-
344 member(bind(X,Val),Result), !,
345 (
346 member(unit(ExplicitUnit),Infos) -> atom_codes(Atom,ExplicitUnit), ajoin(['"',Atom,'"'],Output),
347 Infos2 = [output_pragma('unit',Output)|Infos] ;
348 pp(classic,Val,PP) -> ajoin(['"',PP,'"'],AtomPP),
349 Infos2 = [output_pragma('inferred_unit',AtomPP)|Infos] ;
350 otherwise -> Infos2 = Infos
351 ),
352 expand_variables(T,T2,Result).
353 expand_variables([b(identifier(X),Type,Infos)|T],[b(identifier(X),Type,Infos)|T2],Result) :-
354 format('Warning: identifier ~w not in result\n', [X]),
355 expand_variables(T,T2,Result),
356 '$NOT_COVERED'('This should not happen.').
357
358 set_units_error_context(Context) :-
359 retractall(units_error_context(_)),
360 assert(units_error_context(Context)).