1 % (c) 2009-2019 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(evalstores,[
6 evalstore_reset/0
7 ,evalstore_create_type_env_by_state/2
8 ,evalstore_create_store_by_state/2
9 ,evalstore_eval_formula/4
10 ,evalstore_bindings/2
11 ]).
12
13 :- use_module(module_information,[module_info/2]).
14 :- module_info(group,misc).
15 :- module_info(description,'This module provides methods to construct stores dynamically and evaluate expressions in them, possibly used only by ProB1 for Rodin.').
16
17 :- use_module(library(lists)).
18 :- use_module(library(samsort),[samsort/3]).
19 :- use_module(tools_meta,[safe_time_out_or_virtual_time_out/3]). %:- use_module(library(timeout),[time_out/3]).
20
21 :- use_module(state_space, [visited_expression/2]).
22 :- use_module(specfile, [state_corresponds_to_set_up_constants/2
23 ,state_corresponds_to_initialised_b_machine/1]).
24 :- use_module(bsyntaxtree, [create_texpr/4
25 ,get_texpr_type/2]).
26 :- use_module(b_global_sets, [is_b_global_constant/3
27 ,b_global_set/1]).
28 :- use_module(bmachine, [full_b_machine/1
29 ,b_get_machine_constants/1
30 ,b_get_machine_variables/1]).
31 :- use_module(bmachine_construction,[type_open_formula/8]).
32 :- use_module(btypechecker, [env_empty/1
33 ,add_identifiers_to_environment/3
34 ,env_lookup_type/3
35 ,env_identifiers/2]).
36 :- use_module(store, [empty_state/1
37 ,normalise_store/2
38 ,merge_updates/3]).
39 :- use_module(kernel_waitflags,[init_wait_flags/1
40 ,ground_wait_flags/1]).
41 :- use_module(b_interpreter, [set_up_typed_localstate/6
42 ,b_test_boolean_expression/4
43 ,b_compute_expression/5]).
44 :- use_module(b_enumerate, [b_tighter_enumerate_values/2]).
45 :- use_module(error_manager, [get_all_errors_and_clear/1
46 ,reset_errors/0]).
47
48 :- use_module(extension('user_signal/user_signal'),[protect_from_user_interrupt_det/1]).
49 :- use_module(extension('probhash/probhash'),[hash_term/2]).
50
51 % Each node represents a variable binding like a state in the state space
52 % The main difference to a state in the state space is that we usually have an evolution
53 % of nodes by adding successively new variables.
54
55 % A node is encoded with terms that describe combinations / references to other states
56 % (see expand_node/2 for details).
57 %
58
59 :- dynamic stored_evalstores/3. % stored_evalstores(EvalstoreId,Store,TypeEnvId)
60 :- dynamic evalstore_hashes/2. % evalstore_hashes(Hash,EvalstoreId)
61 :- dynamic next_evalstore_id/1. % next_evalstore_id(EvalstoreId)
62
63 :- dynamic stored_type_environment/2. % stored_type_environment(TypeEnvId,TypeEnv)
64 :- dynamic type_environment_hashes/2. % type_environment_hashes(Hash,TypeEnvId)
65 :- dynamic next_type_environment_id/1. % next_type_environment_id(TypeEnvId)
66 %:- dynamic standard_type_env/2. % standard_type_env(Description,TypeEnvId)
67
68 % remove all existing nodes
69 evalstore_reset :-
70 protect_from_user_interrupt_det(reset_evalstores2).
71 reset_evalstores2 :-
72 retractall(stored_evalstores(_,_,_)),
73 retractall(evalstore_hashes(_,_)),
74 retractall(next_evalstore_id(_)),
75 assert(next_evalstore_id(1)),
76 retractall(stored_type_environment(_,_)),
77 retractall(next_type_environment_id(_)),
78 retractall(type_environment_hashes(_,_)),
79 assert(next_type_environment_id(1)).
80
81
82 store_type_environment(Env,Id) :-
83 hash_type_env(Env,Hash),
84 ( type_environment_hashes(Hash,ExistingId) ->
85 Id = ExistingId
86 ; otherwise ->
87 assert_type_env(Env,Hash,Id)).
88 assert_type_env(Env,Hash,Id) :-
89 protect_from_user_interrupt_det(assert_type_env2(Env,Hash,Id1)),
90 Id1=Id.
91 assert_type_env2(Env,Hash,Id) :-
92 retract(next_type_environment_id(Id)),
93 Next is Id+1,
94 assert(next_type_environment_id(Next)),
95 assert(stored_type_environment(Id,Env)),
96 assert(type_environment_hashes(Hash,Id)).
97
98 create_new_node(NodeExpr,TypeEnvId,EvalstoreId) :-
99 expand_node(NodeExpr,Store),
100 hash_node(Store,TypeEnvId,Hash),
101 ( evalstore_hashes(Hash,Id) ->
102 EvalstoreId = Id
103 ; otherwise ->
104 assert_new_node(NodeExpr,Hash,TypeEnvId,EvalstoreId)).
105
106 hash_node(Store,TypeEnvId,Hash) :-
107 hash_term(Store/TypeEnvId,Hash).
108
109 assert_new_node(Node,Hash,TypeEnvId,Id) :-
110 % the assert should be protected from interrupts to prevent an inconsistent database
111 protect_from_user_interrupt_det(assert_new_node2(Node,Hash,TypeEnvId,Id2)),
112 Id=Id2.
113 assert_new_node2(Node,Hash,TypeEnvId,Id) :-
114 retract(next_evalstore_id(Id)),
115 Next is Id+1,
116 assert(next_evalstore_id(Next)),
117 assert(evalstore_hashes(Hash,Id)),
118 assert(stored_evalstores(Id,Node,TypeEnvId)).
119
120 % expand_node(NodeExpr,Store):
121 % get the full store that the node represents
122 expand_node(Node,Store) :-
123 expand_node2(Node,UStore),
124 samsort(cmp_bind,UStore,Store).
125 expand_node2(empty,Store) :-
126 empty_state(Store).
127 expand_node2(state(StateId),Store) :- % refers to a state in the state space
128 ( StateId=root -> expand_node2(empty,Store)
129 ; otherwise ->
130 visited_expression(StateId,State),
131 state_corresponds_to_set_up_constants(State,Store)).
132 expand_node2(store(Store),Store). % <- directly defines a store
133 expand_node2(update(Orig,New),Store) :- % <- updates the values of a store by another one,
134 expand_node2(Orig,OrigStore), % the new store overrides old values
135 expand_node2(New,NewStore),
136 merge_updates(OrigStore,NewStore,Store).
137
138 cmp_bind(bind(A,_),bind(B,_)) :- A @< B.
139
140 % Generate a hash of the normalised type environment
141 hash_type_env(Env,Hash) :-
142 env_identifiers(Env,Ids), % Ids is sorted
143 maplist(lookup_for_type(Env),Ids,Types),
144 hash_term(Types,Hash).
145 lookup_for_type(Env,Id,type(Id,Type)) :-
146 env_lookup_type(Id,Env,Type).
147
148 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
149 % Type Environments
150 extract_type_environment(State,Env) :-
151 % by starting with a list of sorted identifiers, we try to make
152 % sure that two environments with the same identifiers and types
153 % are treated as equal.
154 extract_type_environment2(State,UIds),
155 create_normalised_type_environment(UIds,Env).
156 create_normalised_type_environment(UIds,Env) :-
157 sort(UIds,Ids),
158 env_empty(Empty),
159 add_identifiers_to_environment(Ids,Empty,Env).
160
161 extract_type_environment2(root,Ids) :-
162 % the root node has already some identifiers in the type environment:
163 % deferred sets, enumerated sets and their elements
164 !,global_identifiers(Ids,[]).
165 extract_type_environment2(State,Ids) :-
166 state_corresponds_to_initialised_b_machine(State),!,
167 global_identifiers(Ids,I1),
168 constant_identifiers(I1,I2),
169 variable_identifiers(I2,[]).
170 extract_type_environment2(_State,Ids) :-
171 global_identifiers(Ids,I1),
172 constant_identifiers(I1,[]).
173
174 global_identifiers -->
175 % the enumerated set elements:
176 findall(TId,
177 (is_b_global_constant(G,_,N),create_texpr(identifier(N),global(G),[],TId))),
178 % the deferred/enumerated sets:
179 findall(TId,
180 (b_global_set(G),create_texpr(identifier(G),set(global(G)),[],TId))).
181 constant_identifiers(In,Out) :-
182 b_get_machine_constants(Csts),
183 append(Csts,Out,In).
184 variable_identifiers(In,Out) :-
185 b_get_machine_variables(Vars),
186 append(Vars,Out,In).
187
188 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
189 % evaluation
190 eval_in_evalstore(TypedFormula,StoreExpr,NewIdentifiers,Timeout,Result) :-
191 reset_errors,
192 expand_node(StoreExpr,Store),
193 get_texpr_type(TypedFormula,Type),
194 empty_state(Empty),
195 set_up_typed_localstate(NewIdentifiers,_NewValues,TypedVals,Empty,LocalStore,positive),
196 init_wait_flags(WF),
197 b_tighter_enumerate_values(TypedVals,WF),
198 time_out_eval(Timeout,Type,TypedFormula,LocalStore,Store,WF,Result1,TOResult),
199 ( TOResult \== success ->
200 Result = timeout
201 ; error_occurred(Result1,Msg) ->
202 Result = error(Msg)
203 ; Result1 = ok(V,ResultStore) ->
204 update_store(StoreExpr,ResultStore,NewStore),
205 Result = ok(V,NewStore)
206 ).
207 update_store(OldStore,AdditionalStore,NewStore) :-
208 empty_state(AdditionalStore),!,OldStore=NewStore.
209 update_store(OldStore,AdditionalStore,update(OldStore,store(AdditionalStore))).
210
211 time_out_eval(none,Type,TypedFormula,LocalStore,Store,WF,Result,TOResult) :-
212 !,evaluate_formula(Type,TypedFormula,LocalStore,Store,WF,Result1),
213 Result=Result1,TOResult=success.
214 time_out_eval(Timeout,Type,TypedFormula,LocalStore,Store,WF,Result,TOResult) :-
215 safe_time_out_or_virtual_time_out( evaluate_formula(Type,TypedFormula,LocalStore,Store,WF,Result),
216 Timeout,
217 TOResult),!.
218
219 evaluate_formula(pred,F,LS,S,WF,ok(pred(V),ResultStore)) :- !,
220 ( b_test_boolean_expression(F,LS,S,WF), ground_wait_flags(WF) ->
221 normalise_store(LS,ResultStore), V=true
222 ; otherwise ->
223 empty_state(ResultStore), V=false).
224 evaluate_formula(subst,_F,_LS,_S_,_WF,error('Substitutions are not yet supported')) :- !.
225 evaluate_formula(_Type,F,LS,S,WF,ok(value(Value),ResultStore)) :-
226 b_compute_expression(F,LS,S,Value,WF),!,
227 empty_state(ResultStore).
228
229 :- use_module(library(codesio),[write_to_codes/2]).
230 error_occurred(error(ErrorMsg),ErrorMsg) :- !.
231 error_occurred(_,ErrorMsg) :-
232 get_all_errors_and_clear([Error|_]),
233 write_to_codes(Error,ErrorCodes),
234 limit_error(ErrorCodes,ErrorCodes2),
235 atom_codes(ErrorMsg,ErrorCodes2).
236
237 % copied from bvisual2:
238 limit_error(ErrorCodes,ErrorCodes) :-
239 length(ErrorCodes,L),L < 200,!.
240 limit_error(In,Out) :-
241 prefix_length(In,Start,200),
242 append(Start,"...",Out).
243
244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
245 % type checking
246 evalstore_typecheck(Raw,EnvId,Result) :-
247 stored_type_environment(EnvId,Env),
248 full_b_machine(Machine),
249 AllowNewIds=false,
250 type_open_formula(Raw,[env(Env)],AllowNewIds,Machine,_Type,NewIdentifiers,TExpr,Errors),
251 ( Errors = [] ->
252 evalstore_typecheck_update_typeenv(NewIdentifiers,Env,EnvId,NewEnvId),
253 Result=ok(TExpr,NewIdentifiers,NewEnvId)
254 ; otherwise ->
255 Result=errors(Errors)).
256 evalstore_typecheck_update_typeenv([],_Env,EnvId,NewEnvId) :-
257 !, EnvId = NewEnvId.
258 evalstore_typecheck_update_typeenv(NewIdentifiers,Env,_EnvId,NewEnvId) :-
259 add_identifiers_to_environment(NewIdentifiers,Env,NewEnv),
260 store_type_environment(NewEnv,NewEnvId).
261
262
263 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
264 % entry points
265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
266 % type environments
267
268 evalstore_create_type_env_by_state(StateId,EnvId) :-
269 ( StateId=root -> State=root
270 ; otherwise -> visited_expression(StateId,State)),
271 extract_type_environment(State,Env),
272 store_type_environment(Env,EnvId).
273
274 evalstore_create_store_by_state(StateId,EvalstoreId) :-
275 evalstore_create_type_env_by_state(StateId,EnvId),
276 create_new_node(state(StateId),EnvId,EvalstoreId).
277
278 evalstore_eval_formula(EvalstoreId,Raw,Timeout,Result) :-
279 stored_evalstores(EvalstoreId,StoreExpr,EnvId),
280 evalstore_typecheck(Raw,EnvId,TypeResult),
281 evalstore_eval_formula2(TypeResult,StoreExpr,Timeout,Result).
282 evalstore_eval_formula2(errors(Errors),_StoreExpr,_Timeout,errors(Errors)).
283 evalstore_eval_formula2(ok(TExpr,NewIdentifiers,NewEnvId),StoreExpr,Timeout,Result) :-
284 eval_in_evalstore(TExpr,StoreExpr,NewIdentifiers,Timeout,EvalResult),
285 evalstore_eval_formula3(EvalResult,NewIdentifiers,NewEnvId,Result).
286 evalstore_eval_formula3(error(Msg),_Ids,_EnvId,errors([Msg])).
287 evalstore_eval_formula3(timeout,_Ids,_EnvId,timeout).
288 evalstore_eval_formula3(ok(Value,NewStore),Ids,EnvId,ok(Value,Ids,StoreId)) :-
289 create_new_node(NewStore,EnvId,StoreId).
290
291 evalstore_bindings(EvalstoreId,Binding) :-
292 stored_evalstores(EvalstoreId,StoreExpr,_),
293 expand_node(StoreExpr,Binding).
294
295 :- use_module(eventhandling,[register_event_listener/3]).
296 :- register_event_listener(specification_initialised,evalstore_reset,
297 'Initialise module evalstore').