1 % (c) 2009-2025 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(ltl_tools,[temporal_parser/3,temporal_parser_l/3,
6 parse_ltlfile/2,check_csp_event/4,
7 parse_and_pp_ltl_formula/2,
8 typecheck_temporal_formula/3,
9 preprocess_formula/2,
10 preprocess_tp/2]).
11
12 /* SICSTUS Libraries */
13 :- use_module(library(lists)).
14
15 /* ProB Libraries */
16 % B
17 :- use_module(probsrc(bsyntaxtree), [predicate_identifiers/2, get_texpr_type/2]).
18 :- use_module(probsrc(bmachine),[b_type_expression/5, b_get_machine_operation/4, get_primed_machine_variables/1]).
19 % CSP
20 :- use_module(probcspsrc(haskell_csp),[channel/2, channel_type_list/2,parse_single_csp_expression/3]).
21 % utility modules
22 :- use_module(probsrc(error_manager)).
23 :- use_module(probsrc(debug)).
24 :- use_module(probsrc(self_check)).
25 :- use_module(probsrc(tools),[split_atom/3, string_concatenate/3]).
26 :- use_module(probsrc(tools_files),[read_file_codes/2]).
27 :- use_module(probsrc(preferences),[get_preference/2]).
28 % state space modules
29 :- use_module(probsrc(state_space),[current_state_id/1]).
30 :- use_module(probsrc(specfile),[animation_mode/1,csp_mode/0, csp_with_bz_mode/0, b_or_z_mode/0, xtl_mode/0]).
31 % other
32 :- use_module(probsrc(parsercall),[call_ltl_parser/3, parse_expression/2,parse_predicate/2]).
33 /* Promela Tools */
34 %:- use_module(probpromela(promela_tools),[parse_promela_strings/4]).
35 /* ProB LTL modules */
36 :- use_module(probltlsrc(ltl_translate)).
37
38 :- use_module(probsrc(module_information),[module_info/2]).
39 :- module_info(group,ltl).
40 :- module_info(description,'This module provides utility predicates for the LTL and CTL model checkers.').
41
42 :- set_prolog_flag(double_quotes, codes).
43
44 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
45 % parse an LTL or CTL formula
46 % Mode: ltl or ctl or pctl
47 temporal_parser(Formula,Mode,Result) :-
48 temporal_parser_l([Formula],Mode,[Result]).
49
50 temporal_parser_l(Formulas,Mode,Result) :-
51 % call the Java parser for LTL
52 call_ltl_parser(Formulas,Mode,Res1),
53 % check if everything was fine
54 check_syntax_result_l(Res1,Parsed,ParseFlag,Mode), ParseFlag=ok,
55 % typecheck atomic propositions and such things
56 %tools_printing:nested_print_term(Parsed,10),
57 typecheck_temporal_formula_l(Parsed,Result,TypeFlag), TypeFlag=ok.
58
59 check_syntax_result_l([],[],_,_).
60 check_syntax_result_l([R|Rrest],[F|Frest],Res,Mode) :-
61 check_syntax_result(R,F,Res,Mode),
62 check_syntax_result_l(Rrest,Frest,Res,Mode).
63 check_syntax_result(ltl(F),F,_,_Mode).
64 check_syntax_result(syntax_error(E),_,error,Mode) :-
65 add_error(Mode,'Syntax error: ',E).
66
67 typecheck_temporal_formula_l([],[],_).
68 typecheck_temporal_formula_l([F|Frest],[R|Rrest],Res) :-
69 typecheck_temporal_formula(F,R,Res),
70 typecheck_temporal_formula_l(Frest,Rrest,Res).
71 typecheck_temporal_formula(ap(AP),ap(Typed),Res) :-
72 !,typecheck_ap(AP,Typed,Res).
73 typecheck_temporal_formula(action(TP),action(Typed),Res) :-
74 !,typecheck_tp(TP,Typed,Res).
75 typecheck_temporal_formula(ena(TP,F),ena(Typed,R),Res) :- % used in CTL only
76 !,typecheck_tp(TP,Typed,Res),
77 typecheck_temporal_formula(F,R,Res).
78 %typecheck_temporal_formula(forall(ID,LHSAP,RHS),forall(ID,LR,RR),Res) :-
79 % !, % TO DO: LHSAP is a B Predicate, add ID to the typing environment for LHS and RHS
80 % typecheck_temporal_formula(LHSAP,LR),
81 % typecheck_temporal_formula(RHS,RR).
82 typecheck_temporal_formula(F,R,Res) :-
83 F =.. [Functor|InArgs],
84 same_length(InArgs,OutArgs),
85 R =.. [Functor|OutArgs],
86 typecheck_temporal_formula_l(InArgs,OutArgs,Res).
87
88 typecheck_ap(BasicAP,AP,_Res) :- is_basic_ap(BasicAP),!,AP=BasicAP.
89 typecheck_ap(enabled(TP),enabled(R),Res) :-
90 !,typecheck_tp(TP,R,Res).
91 typecheck_ap(available(TP),available(R),Res) :-
92 !,typecheck_tp(TP,R,Res).
93 typecheck_ap(weak_fair(TP),weak_fair(R),Res) :-
94 !,typecheck_tp(TP,R,Res).
95 typecheck_ap(strong_fair(TP),strong_fair(R),Res) :-
96 !,typecheck_tp(TP,R,Res).
97 typecheck_ap(dlk(TPList),dlk(R),Res) :-
98 !,typecheck_tp_l(TPList,R,Res).
99 typecheck_ap(ctrl(TPList),ctrl(R),Res) :-
100 !,typecheck_tp_l(TPList,R,Res).
101 typecheck_ap(det(TPList),det(R),Res) :-
102 !,typecheck_tp_l(TPList,R,Res).
103 typecheck_ap(bpred(Raw),bpred(Typed),Res) :- !,
104 type_check_raw(Raw,pred,Typed,Res).
105 typecheck_ap(unparsed_pred(Atom),xtl_predicate_check(Atom),ResultFlag) :- !,
106 (animation_mode(xtl) -> true ; ResultFlag=error).
107 typecheck_ap(F,R,Res) :-
108 add_internal_error('Illegal LTL atomic proposition:',typecheck_ap(F,R,Res)),fail.
109
110 typecheck_expr(bexpr(E),TE,Type,Res) :- !,
111 type_check_raw(E,Type,TE,Res),
112 (Type=pred -> add_error(ltl,'Expression expected, but was predicate:',TE), Res=error
113 ; true).
114 typecheck_expr(F,R,T,Res) :-
115 add_internal_error('Illegal LTL atomic expression:',typecheck_expr(F,R,T,Res)),fail.
116
117
118 %typecheck_pred(bpred(E),TE,Type,Res) :- !,
119 % type_check_raw(E,Type,TE,Res),
120 % (Type=pred -> true
121 % ; add_error(ltl,'Predicate expected, but was expression:',TE), Res=error
122 % ).
123 %typecheck_pred(F,R,T,Res) :-
124 % add_internal_error('Illegal LTL atomic predicate:',typecheck_pred(F,R,T,Res)),fail.
125
126
127 type_check_raw(Raw,Type,Typed,Res) :-
128 b_type_expression_for_ltl(Raw,Type,Typed0,Errors),
129 add_all_perrors(Errors,[],typecheck_atomic_property),
130 (no_real_perror_occurred(Errors)
131 -> inline_prob_deferred_set_elements_into_bexpr(Typed0,Typed)
132 ; Typed=Typed0, Res=error).
133
134 is_basic_ap(deadlock).
135 is_basic_ap(det_output).
136 is_basic_ap(non_det).
137 is_basic_ap(sink).
138 is_basic_ap(goal).
139 is_basic_ap(state_error).
140 is_basic_ap(current).
141 is_basic_ap(stateid(_)).
142
143 :- use_module(probsrc(specfile),[eventb_mode/0]).
144 :- use_module(probsrc(b_global_sets),[inline_prob_deferred_set_elements_into_bexpr/2]).
145
146 typecheck_tp(unparsed_trans(Op),xtl(Opname,Arity,Args),ResultFlag) :-
147 animation_mode(xtl), !,
148 ( compound(Op) -> functor(Op,Opname,Arity), Op =.. [_|Args]
149 ; split_atom(Op,['.'],[Opname|Args]) ->
150 length(Args,Arity)
151 ;
152 add_error(ltl,'Failed to parse operation specification'),
153 ResultFlag = error).
154 typecheck_tp(unparsed_trans(Opname),csp(Channel,Arity,ResArgs),ResultFlag) :-
155 csp_mode,!,
156 ( Opname = tick
157 -> Channel=Opname, Arity=0, ResArgs=[] % should we set Arity ??
158 ; Opname = tau
159 -> Channel=Opname, Arity=0, ResArgs=[] % should we set Arity ??
160 ; (split_atom(Opname,['.','!'],[Channel|Args]), channel(Channel,_))
161 ->
162 (channel_type_list(Channel, TypeList),length(TypeList,Arity),length(Args,Arity)
163 -> (Arity = 0
164 -> ResArgs = []
165 ; (\+memberchk('_',Args)
166 % we want to call the CSP-M parser only once when there is no '_' symbol in Args
167 -> mergeArgsToDotTuple([Channel|Args],DotTupleEvent),
168 parse_single_csp_expression(ltl,DotTupleEvent,ParsedEvent),
169 splitToSingleArgs(ParsedEvent,[Channel|ResArgs],Arity)
170 ; l_parse_single_csp_expression_undescore(ltl,Args,ResArgs)
171 )
172 )
173 ; add_error(ltl,'Number of arguments not correct for: ', Opname),
174 ResultFlag=error, ResArgs=[], Arity = 0
175 )
176 ;
177 add_error(ltl,'Channel does not exist: ',Opname),
178 ResultFlag = error, ResArgs=[], Arity = 0
179 ).
180 typecheck_tp(operation_call(OpCall),operation_call(Result),ResultFlag) :- !,
181 typecheck_tp(OpCall,Result,ResultFlag),
182 (eventb_mode -> add_warning(ltl,'The operation_call transition predicate only makes sense for classical B')
183 ; get_preference(eventtrace,true) -> true
184 ; add_warning(ltl,'The operation_call transition predicate can only be checked when STORE_DETAILED_TRANSITION_INFOS preference is TRUE')
185 % TODO: warn if top-level operation that is not called anywhere
186 ).
187 typecheck_tp(bop(oppattern(_Pos,Name,Args)),Result,ResultFlag) :- !,
188 ( b_or_z_mode, b_get_machine_operation(Name,Results,Parameters,_) ->
189 Result = bop(Name,NumberArgs,NumberResults,ArgPatterns,ResPatterns),
190 length(Parameters,NumberArgs), length(Results,NumberResults),
191 % Pattern matching for results is not yet supported
192 ResPatterns = [],
193 length(Args,PatNumberArgs),
194 ( Args=[] ->
195 ArgPatterns = [],
196 ResPatterns = []
197 ; PatNumberArgs=NumberArgs ->
198 create_b_op_patterns(Args,Parameters,ArgPatterns,ResultFlag)
199 ; ajoin(['Expected ',NumberArgs,' arguments instead of ',PatNumberArgs,' for: '],Msg),
200 add_error(ltl,Msg, Name),
201 ResultFlag=error)
202 ; (csp_mode ; xtl_mode), Args=[] ->
203 debug_println(9,unparsed_trans(Name)),
204 typecheck_tp(unparsed_trans(Name),Result,ResultFlag)
205 ; unknown_operation_fuzzy_msg(Name,Msg) -> add_error(ltl,Msg, Name),ResultFlag=error
206 ; % fuzzy matching and provide better error message, especially since sometimes operation names need to be prefixed
207 add_error(ltl,'Unknown operation in LTL formula:', Name),ResultFlag=error).
208 typecheck_tp(btrans(event(Name)),btrans(event(Name)),_ResultFlag) :- !.
209 typecheck_tp(btrans(event(Name,RawPredicate)),btrans(event(Name,TPredicate,Poststate)),ResultFlag) :- !,
210 ( b_get_machine_operation(Name,_,_Parameters,_) ->
211 ( bmachine:type_with_errors(RawPredicate,[prepost,operation(Name)],pred,TPredicate) ->
212 find_poststate_access(TPredicate,Poststate)
213 ; ResultFlag=error)
214 ; unknown_operation_fuzzy_msg(Name,Msg) -> add_error(ltl,Msg, Name),ResultFlag=error
215 ; add_error(ltl,'Unknown operation in LTL formula:', Name),ResultFlag=error).
216 typecheck_tp(before_after(bpred(RawPredicate)),before_after(TPred),ResultFlag) :- !,
217 get_primed_machine_variables(PV),
218 (get_preference(symmetry_mode,off) -> Scope = [prob_ids(visible),identifier(PV),variables]
219 ; Scope = [identifier(PV),variables]
220 ),
221 % in classical B x$0 refers to the value before a substitution
222 ( bmachine:type_with_errors(RawPredicate,Scope,pred,TPred0)
223 -> % see prepost becomes_such b_parse_machine_operation_pre_post_predicate
224 inline_prob_deferred_set_elements_into_bexpr(TPred0,TPred) % TODO: we could compute the $0 variables
225 ; ResultFlag=error).
226 typecheck_tp(change_expr(Comparator,Expr),change_expr(Comparator,TypedExpr),Res) :-
227 !, typecheck_expr(Expr,TypedExpr,_Type,Res).
228 typecheck_tp(F,R,Res) :-
229 add_internal_error('Illegal LTL transition predicate:',typecheck_tp(F,R,Res)),fail.
230
231 typecheck_tp_l([],[],_Res).
232 typecheck_tp_l([TP|TPs],[TTP|TTPs],Res) :-
233 typecheck_tp(TP,TTP,ResultFlag),
234 (ResultFlag == error -> Res = error ; Res=Res1),
235 typecheck_tp_l(TPs,TTPs,Res1).
236
237 :- use_module(probsrc(tools_matching), [get_possible_operation_matches_msg/2]).
238 % fuzzy matching and provide better error message, especially since sometimes operation names need to be prefixed
239 % TODO: this also returns subsidiary operations; which is what we want for operation_call but not for other preds
240 unknown_operation_fuzzy_msg(OpName,Msg) :- b_or_z_mode,
241 get_possible_operation_matches_msg(OpName,FMsg),
242 ajoin(['Unknown operation in LTL formula (did you mean ',FMsg,' ?) :'], Msg).
243
244 :- use_module(probsrc(bsyntaxtree),[syntaxtraversion/6]).
245 find_poststate_access(TExpr,Poststate) :-
246 find_poststate_access2(TExpr,P,[]),
247 sort(P,Poststate).
248 find_poststate_access2(TExpr) -->
249 {syntaxtraversion(TExpr,Expr,_,Infos,Subs,_)},
250 find_poststate_access3(Expr,Infos),
251 find_poststate_access_l(Subs).
252 find_poststate_access3(identifier(Id),Infos) -->
253 {memberchk(poststate,Infos),!,atom_concat(Orig,'\'',Id)},
254 [poststate(Orig,Id)].
255 find_poststate_access3(_,_) --> [].
256 find_poststate_access_l([]) --> !.
257 find_poststate_access_l([TExpr|Rest]) -->
258 find_poststate_access2(TExpr),
259 find_poststate_access_l(Rest).
260
261 /* Parsing CSP expressions where '_' will be not parsed */
262 parse_single_csp_expression_undescore(Context,Expr,Res) :-
263 (Expr=='_' -> Res = Expr; parse_single_csp_expression(Context,Expr,Res)).
264
265 l_parse_single_csp_expression_undescore(Context,LExpr,LRes) :-
266 maplist(parse_single_csp_expression_undescore(Context),LExpr,LRes).
267 /*******************************************************/
268
269 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
270 % do some preprocessing on a formula:
271 % - insert current state id
272 % - rewrite CSP declarations in some cases
273
274 preprocess_formula(In,Out) :-
275 (preprocess_formula2(In,Out) -> true ; add_error(ltl,'preprocessing formula failed: ',In),fail).
276
277 :- assert_must_succeed(preprocess_formula2(globally(next(false)),false)).
278 :- assert_must_succeed(preprocess_formula2(globally(next(next(true))),globally(true))).
279 :- assert_must_succeed(preprocess_formula2(finally(next(true)),true)).
280 :- assert_must_succeed(preprocess_formula2(finally(next(next(false))),finally(false))).
281
282 % base cases
283 preprocess_formula2(ap(A),ap(B)) :- !,
284 (A=bpred(Pred) ->
285 predicate_identifiers(Pred,Ids),
286 debug_println(predicate_identifiers(A,Ids))
287 ; true),
288 preprocess_ap(A,B).
289 preprocess_formula2(action(A),action(B)) :- !,preprocess_tp(A,B).
290 preprocess_formula2(ena(SpecA,FA),action(SpecB,FB)) :-
291 !,preprocess_tp(SpecA,SpecB),preprocess_formula2(FA,FB).
292 % (X true = true) and (X false = false)
293 preprocess_formula2(next(F),PNextF) :-
294 !,preprocess_formula2(F,PF),
295 (PF = true -> PNextF = true
296 ;PF = false -> PNextF = false
297 ; PNextF = next(PF)).
298 % idempotency laws
299 preprocess_formula2(globally(globally(F)),globally(PF)) :-
300 !,preprocess_formula2(F,PF).
301 preprocess_formula2(finally(finally(F)),finally(PF)) :-
302 !,preprocess_formula2(F,PF).
303 preprocess_formula2(until(F,until(F,G)),until(PF,PG)) :-
304 !,preprocess_formula2(F,PF),
305 preprocess_formula2(G,PG).
306 preprocess_formula2(until(until(F,G),G),until(PF,PG)) :-
307 !,preprocess_formula2(F,PF),
308 preprocess_formula2(G,PG).
309 % absorption laws
310 preprocess_formula2(finally(globally(finally(F))),globally(finally(PF))) :-
311 !,preprocess_formula2(F,PF).
312 preprocess_formula2(globally(finally(globally(F))),finally(globally(PF))) :-
313 !,preprocess_formula2(F,PF).
314 % F (f U g) == F g
315 preprocess_formula2(finally(until(_F,G)),finally(PG)) :-
316 !,preprocess_formula2(G,PG).
317 % G (f R g) == G g
318 preprocess_formula2(globally(release(_F,G)),globally(PG)) :-
319 !,preprocess_formula2(G,PG).
320 % F true = true
321 preprocess_formula2(finally(F),PFinallyF) :-
322 !,preprocess_formula2(F,PF),
323 (PF = true -> PFinallyF = true ; PFinallyF = finally(PF)).
324 % G false = false
325 preprocess_formula2(globally(F),PGloballyF) :-
326 !,preprocess_formula2(F,PF),
327 (PF = false -> PGloballyF = false ; PGloballyF = globally(PF)).
328 % otherwise
329 preprocess_formula2(Term,NewTerm) :-
330 functor(Term,Functor,Arity),
331 functor(NewTerm,Functor,Arity),
332 preprocess_formula_args(Arity,Term,NewTerm).
333
334 preprocess_formula_args(0,_,_) :- !.
335 preprocess_formula_args(N,Term,NewTerm) :-
336 arg(N,Term,Arg),
337 arg(N,NewTerm,NewArg),
338 preprocess_formula2(Arg,NewArg),
339 N2 is N-1,
340 preprocess_formula_args(N2,Term,NewTerm).
341
342 preprocess_ap(current,stateid(StateId)) :- !,
343 current_state_id(StateId).
344 preprocess_ap(enabled(A),enabled(B)) :- !,
345 preprocess_tp(A,B).
346 preprocess_ap(AP,AP).
347
348 preprocess_tp(csp(Channel,Arity,CspArgs),
349 bop(Channel,NumberArgs,NumberResults,ArgPatterns,ResPatterns)) :-
350 csp_with_bz_mode,
351 b_get_machine_operation(Channel,Results,Parameters,_Body),
352 !,
353 % check if more arguments are specified that allowed
354 length(Parameters,NumberArgs),
355 length(Results,NumberResults),
356 MaxNumberAllowed is NumberArgs + NumberResults,
357 ( Arity > MaxNumberAllowed ->
358 add_error(ltl,'Too many arguments for LTL/CTL channel: ', Channel),fail
359 ; Arity =< NumberResults ->
360 ParamArgs = CspArgs,ResArgs = []
361 ;
362 MinArgs is min(NumberArgs,Arity),
363 append_length(ParamArgs,ResArgs,CspArgs,MinArgs)),
364 csp_arguments(ParamArgs,1,Parameters,ArgPatterns,Exprs1),
365 csp_arguments(ResArgs,1,Results,ResPatterns,Exprs2),
366 append(Exprs1,Exprs2,Exprs),
367 parse_preds_exprs(Exprs).
368 preprocess_tp(TP,TP).
369
370 csp_arguments([],_Index,_Parameters,[],[]).
371 csp_arguments([P|ParamArgs],Index,[Param|ParamRest],ArgPatterns,Exprs) :-
372 ( is_csp_placeholder(P) ->
373 ArgPatterns = ArgPatternsRest,
374 Exprs = ExprsRest
375 ;
376 ArgPatterns = [Index/ArgPattern|ArgPatternsRest],
377 Exprs = [Expr|ExprsRest],
378 csp_argument(P,Param,ArgPattern,Expr)),
379 I2 is Index+1,
380 csp_arguments(ParamArgs,I2,ParamRest,ArgPatternsRest,ExprsRest).
381 csp_argument(Argument,Parameter,ParsedExpr,expr(ToParse,Type,ParsedExpr)) :-
382 get_texpr_type(Parameter,Type),
383 atom_codes(Argument,ToParse).
384
385 is_csp_placeholder('_').
386 is_csp_placeholder('?').
387
388 % currently not called: nor is unparsed_ap called
389 %translate_preds(In,Out) :-
390 % unparsed_ap_l(In,Out,Translation,[]),
391 % parse_preds_exprs(Translation).
392
393 parse_preds_exprs([]) :- !. % nothing to parse
394 parse_preds_exprs(Translation) :-
395 split_preds(Translation,PredStrings,Preds,ExprStrings,Exprs),
396 animation_mode(AnimMode),
397 parse_preds_exprs2(AnimMode,PredStrings,Preds,ExprStrings,Exprs).
398
399 % default: parse the predicates and expressions as B
400 parse_preds_exprs2(_,PredStrings,Preds,ExprStringsWithTypes,Exprs) :-
401 annotate_ap_type(Preds,bpred,BPreds),
402 split_exprs_types(ExprStringsWithTypes,ExprStrings,Types),
403 maplist(parse_expression,ExprStrings,UExprs),
404 maplist(parse_predicate,PredStrings,UPreds),
405 type_exprs(UExprs,Types,Exprs,EErrors),
406 type_preds(UPreds,BPreds,PErrors),
407 append(EErrors,PErrors,Errors),
408 add_all_perrors(Errors,[],type_error).
409
410 split_exprs_types([],[],[]).
411 split_exprs_types([Expr/Type|ETRest],[Expr|ERest],[Type|TRest]) :-
412 split_exprs_types(ETRest,ERest,TRest).
413
414 type_exprs([],[],[],[]).
415 type_exprs([Expr|Erest],[Type|TypeRest],[Typed|Trest],Errors) :-
416 b_type_expression_for_ltl(Expr,Type,Typed,LErrors),
417 append(LErrors,ErrorRest,Errors),
418 type_exprs(Erest,TypeRest,Trest,ErrorRest).
419 type_preds([],[],[]).
420 type_preds([Pred|Prest],[Typed|Trest],Errors) :-
421 b_type_expression_for_ltl(Pred,pred,Typed,LErrors),
422 append(LErrors,ErrorRest,Errors),
423 type_preds(Prest,Trest,ErrorRest).
424
425 annotate_ap_type([],_,[]).
426 annotate_ap_type([AP|APrest],Type,[R|Rrest]) :-
427 functor(AP,Type,1),arg(1,AP,R),
428 annotate_ap_type(APrest,Type,Rrest).
429
430 b_type_expression_for_ltl(Expr,Type,Typed,LErrors) :-
431 (get_preference(symmetry_mode,off)
432 -> Scope = [prob_ids(visible),variables]
433 ; Scope = [variables] % we have to be careful with symmetry, TODO: better feedback
434 ),
435 % we would need to call add_prob_deferred_set_elements_to_store(State1,State2,visible)
436 % should we also add external_library(all_available_libraries)
437 b_type_expression(Expr,Scope,Type,Typed,LErrors).
438
439
440 split_preds([],[],[],[],[]).
441 split_preds([pred(A,B)|Rest],[A|ARest],[B|BRest],X,Y) :- !,
442 split_preds(Rest,ARest,BRest,X,Y).
443 split_preds([expr(A,T,B)|Rest],X,Y,[A/T|ARest],[B|BRest]) :-
444 split_preds(Rest,X,Y,ARest,BRest).
445
446 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
447 % parse a file with several LTL formulas:
448
449 % The format in Rodin and the format generated by the Tcl/Tk LTL viewer are different
450 % Rodin has formulas on one line, with comments starting with #
451 % Tcl/Tk generates sections with square brackets; within a section there is a single formula
452
453 parse_ltlfile(Filename, Formulas) :-
454 (parse_ltlfile2(Filename,Formulas) -> true ; add_error(ltl, 'Error while parsing the LTL formula file')).
455 parse_ltlfile2(Filename, Formulas) :-
456 read_file_codes(Filename,Codes),
457 reset_line_counter,
458 parse_lines(Lines,Codes,[]),
459 get_line_counter(LineCount), debug_format(19,'Read ~w lines from file ~w~n',[LineCount,Filename]),
460 parse_sections(Raws,Lines,[]),
461 maplist(parse_file_formula(Filename),Raws,Formulas).
462
463
464 :- use_module(probsrc(tools_strings),[ajoin/2]).
465 parse_file_formula(Filename,file_formula(StartLine,Name,Raw),formula(Name,Res)) :- !,
466 debug_format(19,'Parsing formula starting at line ~w in ~w~n',[StartLine,Filename]),
467 (temporal_parser(Raw,ltl,Parsed)
468 -> Res=Parsed
469 ; ajoin(['Error parsing LTL formula ',Name,' starting at line ',StartLine,' in ',Filename],Msg),
470 add_error(ltl_tools,Msg,Raw),
471 Res = false).
472 parse_file_formula(_,X,Res) :-
473 add_internal_error('Illegal formula term:',X),!, Res=false.
474
475
476 /*
477 parse_file2([]) --> [].
478 parse_file2([F|Rest]) -->
479 parse_section(F),
480 parse_file2(Rest). */
481
482 parse_sections(Res) --> [comment(_)],!, parse_sections(Res).
483 parse_sections([file_formula(StartLine,Name,F)|Rest]) -->
484 [header(_,Name)],!,
485 parse_section_multi_line_formulas("",FCodes,StartLine),
486 { atom_codes(F,FCodes) },
487 parse_sections(Rest).
488 parse_sections([file_formula(StartLine,FormulaName,F)|Rest]) -->
489 % no header section, this is Rodin Format and we expect one formula per line
490 [formula_line(StartLine,FCodes)],
491 !,
492 { atom_codes(F,FCodes), ajoin(['ltl-formula-on-line-',StartLine],FormulaName) },
493 parse_sections(Rest).
494 parse_sections([]) --> [].
495
496
497 parse_section_multi_line_formulas(Prev,F,Line) --> [comment(_)],!, parse_section_multi_line_formulas(Prev,F,Line).
498 parse_section_multi_line_formulas(Prev,F,StartLine) -->
499 [formula_line(StartLine,Snippet)],!,
500 {(Prev==[] -> F2=Snippet
501 ; append([Prev,"\n",Snippet],F2)
502 )},
503 parse_section_multi_line_formulas(F2,F,_).
504 parse_section_multi_line_formulas(F,F,-1) --> [].
505
506 parse_lines([]) --> [].
507 parse_lines([L|Rest]) -->
508 parse_line(L), %{get_line_counter(Line),print(parsed_line(Line,L)),nl},
509 parse_lines(Rest).
510
511 parse_line(Line) --> pl_spaces,!, parse_line(Line).
512 parse_line(comment(C)) -->
513 "#",!, % comment lines start with #
514 pl_until_newline(C).
515 parse_line(comment("")) -->
516 pl_newline,!.
517 parse_line(header(Line,Title)) -->
518 pl_title(Title), !,
519 {get_line_counter(Line)},
520 pl_opt_spaces, pl_newline, !.
521 parse_line(formula_line(Line,[C|F])) -->
522 % non empty formulas
523 [C], {get_line_counter(Line)},
524 pl_until_newline(F).
525
526 pl_title(Title) -->
527 "[", pl_opt_spaces, pl_title2(T), {atom_codes(Title,T)}.
528 pl_title2([]) --> pl_opt_spaces,"]",!.
529 pl_title2([]) --> pl_newline,!,{fail}. % maybe an exception?
530 pl_title2([C|Rest]) --> [C],pl_title2(Rest).
531
532 pl_spaces --> " ",!,pl_spaces.
533 pl_spaces --> "\t",!,pl_spaces.
534
535 pl_opt_spaces --> " ",!,pl_opt_spaces.
536 pl_opt_spaces --> "\t",!,pl_opt_spaces.
537 pl_opt_spaces --> [].
538
539 pl_until_newline([]) --> pl_newline,!.
540 pl_until_newline([C|Rest]) --> [C],!,pl_until_newline(Rest).
541 pl_until_newline([]) --> []. % end of string/file
542
543 pl_newline --> "\n\r",!, {inc_line_counter}.
544 pl_newline --> "\n",!,{inc_line_counter}.
545 pl_newline --> "\r",{inc_line_counter}.
546
547 inc_line_counter :- get_line_counter(L), L1 is L+1, bb_put(ltl_line_counter,L1).
548 get_line_counter(Val) :- (bb_get(ltl_line_counter,V) -> V=Val ; Val = -1).
549 reset_line_counter :- bb_put(ltl_line_counter,1).
550
551
552 create_b_op_patterns(Args,Parameters,ArgPatterns,Res) :-
553 create_b_op_patterns2(Args,Parameters,ArgPatterns,1,Res).
554 create_b_op_patterns2([],[],[],_,_).
555 create_b_op_patterns2([Arg|ARest],[Param|ParamRest],Patterns,Index,Res) :-
556 create_b_op_pattern(Arg,Param,Pattern,Res),
557 (var(Pattern) -> Patterns = PRest ; Patterns = [Index/Pattern|PRest]),
558 Index2 is Index+1,
559 create_b_op_patterns2(ARest,ParamRest,PRest,Index2,Res).
560 create_b_op_pattern(undef(_Pos),_,_,_).
561 create_b_op_pattern(def(_Pos,Raw),TParam,Typed,Res) :-
562 get_texpr_type(TParam,Type),
563 b_type_expression_for_ltl(Raw,Type,Typed,Errors),
564 add_all_perrors(Errors,[],typecheck_operation_argument),
565 (no_real_perror_occurred(Errors) -> true ; Res=error).
566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
567
568
569 parse_and_pp_ltl_formula(FormulaAsAtom,Text) :-
570 temporal_parser(FormulaAsAtom,ltl,Ltl),
571 ( pp_ltl_formula_classicb(Ltl,Text)
572 -> true
573 ; add_error_fail(ltl, 'Failed to pretty print LTL formula: ', Ltl)
574 ).
575
576 :- use_module(probsrc(specfile),[unset_animation_minor_modes/1,reset_animation_minor_modes/1]).
577 :- use_module(probsrc(translate),[set_unicode_mode/0, unset_unicode_mode/0]).
578 pp_ltl_formula_classicb(Ltl,Text) :-
579 unset_animation_minor_modes(L), % unset eventb mode, as we will later parse the LTL formula in classic mode!
580 % TO DO: improve and ensure things like finite(.) from Event-B work in atomic propositions
581 set_unicode_mode,
582 call_cleanup(pp_ltl_formula(Ltl,Text),
583 (reset_animation_minor_modes(L),unset_unicode_mode)).
584
585 %%%%%%%% Other predicates %%%%%%%%%%
586
587 :- assert_must_succeed((ltl_tools: mergeArgsToDotTuple([1,2,'<bob,alice>','{bob}'],Res), Res = '1.2.<bob,alice>.{bob}')).
588 :- assert_must_succeed((ltl_tools: mergeArgsToDotTuple(['(bob,alice)'],Res), Res = '(bob,alice)')).
589 :- assert_must_succeed(ltl_tools: mergeArgsToDotTuple([],'')).
590
591 mergeArgsToDotTuple(List,Res) :- mergeArgsToDotTuple(List,'',Res).
592
593 mergeArgsToDotTuple([],Str,Str).
594 mergeArgsToDotTuple([H|T],Str,Res) :-
595 (T\=[] -> string_concatenate(H,'.',HRes) ; HRes=H),
596 string_concatenate(Str,HRes,StrRes),mergeArgsToDotTuple(T,StrRes,Res).
597
598 splitToSingleArgs(dotTuple(List),List,Arity) :-
599 N is Arity +1, length(List,N),!.
600 splitToSingleArgs(X,[X],0) :- !.
601 splitToSingleArgs(ParsedEvent,_,N) :-
602 ajoin(['Expected arity ',N,' for channel: '],Msg),
603 add_internal_error(Msg,ParsedEvent).
604
605 :- assert_must_succeed((check_csp_event(tick,0,[],R), R=tick(_))).
606 :- assert_must_succeed((check_csp_event(tau,0,[],R), R=tau(_))).
607 :- assert_must_fail(check_csp_arguments([setValue([alice,bob]),tupleExp([s,t,u])], [setValue([alice,bob]),in(na_tuple([s,t]))],some_event)).
608 :- assert_must_succeed(check_csp_arguments([setValue([alice,bob]),tupleExp([s,t,u])], [setValue([alice,bob]),in(na_tuple([s,t,u]))],some_event)).
609 :- assert_must_succeed(check_csp_arguments([setValue([bob,alice]),tupleExp([s,t,u])], [setValue([alice,bob]),in(na_tuple([s,t,u]))],some_event)).
610 :- assert_must_succeed(check_csp_arguments([a,'_','_'],[in(a),in(int(10)),in(int(20))],true)).
611
612 %% Predicate check csp event
613 check_csp_event(tick,0,[],R) :- !, R=tick(_).
614 check_csp_event(tau,0,[],R) :- !, R=tau(_).
615 check_csp_event(Event,Arity,PatArgs,io(Vals,Event,_SrcSpan)) :- !,
616 (channel(Event,_) ->
617 ((channel_type_list(Event,List),length(List,Arity)) ->
618 check_csp_arguments(PatArgs,Vals,Event)
619 ; add_error(ltl,'Internal Error: Missing/extra arguments in CSP LTL/CTL pattern: ',Event:PatArgs),fail
620 )
621 ; add_internal_error('Internal Error: Channel not defined: ',Event/Arity)).
622
623 :- block check_csp_arguments(?,-,?).
624 check_csp_arguments([],[],_).
625 check_csp_arguments([],[H|T],Event) :-
626 print('* Missing arguments in CSP LTL/CTL pattern (use .?): '),print(Event),
627 print(' : '),print([H|T]),nl.
628 check_csp_arguments([H|T],[],Event) :-
629 add_error(ltl,'Extra arguments in CSP LTL/CTL pattern: ',Event:[H|T]),fail.
630 check_csp_arguments([H|T],[PatH|PatT],Event) :-
631 check_csp_arg(H,PatH),!,
632 check_csp_arguments(T,PatT,Event).
633
634 check_csp_arg(X,Y) :-
635 var(Y),!,
636 add_internal_error('Internal Error: Event argument was a variable, expected to be: ',X).
637 check_csp_arg('_',_Y). % underscore should match any argument
638 check_csp_arg(X,in(Y)) :-
639 check_csp_arg(X,Y).
640 check_csp_arg(X,out(Y)) :-
641 check_csp_arg(X,Y).
642
643 check_csp_arg(list(L),list(L1)) :- !,
644 l_check_csp_arg(L,L1,list).
645 check_csp_arg(setValue(L),setValue(L1)) :- !,
646 sort(L,R),
647 sort(L1,R1),
648 l_check_csp_arg(R,R1,setValue). % elements order is not important within set expressions
649 check_csp_arg(tupleExp(L),na_tuple(L1)) :- !,
650 l_check_csp_arg(L,L1,na_tuple).
651 check_csp_arg(X,Y) :- eq_csp_val(Y,X).
652
653 eq_csp_val(int(X),int(Y)) :- !, Y=X.
654 eq_csp_val(X,X).
655
656 l_check_csp_arg([],[],_):- !.
657 l_check_csp_arg([],[H|T],Functor) :-
658 add_error_fail(ltl,'Missing elements in CSP LTL pattern: ',Functor:[H|T]).
659 l_check_csp_arg([H|T],[H1|T1],Functor) :-
660 check_csp_arg(H,H1),!,
661 l_check_csp_arg(T,T1,Functor).