| 1 | % (c) 2009-2024 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_translate,[pp_ltl_formula/2, pp_formulas/2, pp_ap/2, pp_tp/2, get_ltl_sub_formulas/3]). | |
| 6 | ||
| 7 | /* SICSTUS Libraries */ | |
| 8 | :- use_module(library(lists)). | |
| 9 | %% :- use_module(library(codesio)). | |
| 10 | %% :- use_module(library(terms)). | |
| 11 | ||
| 12 | /* ProB Libraries */ | |
| 13 | %% :- use_module(probsrc(error_manager)). | |
| 14 | %% :- use_module(probsrc(debug)). | |
| 15 | :- use_module(probsrc(self_check)). | |
| 16 | :- use_module(probsrc(specfile),[csp_with_bz_mode/0]). | |
| 17 | :- use_module(probsrc(tools),[ajoin/2]). | |
| 18 | :- use_module(probsrc(translate), [translate_bexpression/2,translate_event/2]). | |
| 19 | :- use_module(probsrc(state_space),[transition/4]). | |
| 20 | :- use_module(library(codesio),[write_to_codes/2]). | |
| 21 | ||
| 22 | :- use_module(probsrc(module_information),[module_info/2]). | |
| 23 | :- module_info(group,ltl). | |
| 24 | :- module_info(description,'This module provides utility predicates for pretty-printing LTL and CTL formulae.'). | |
| 25 | ||
| 26 | pp_ltl_formula(Formula,Text) :- | |
| 27 | pp_ltl(Formula,Atoms,[]), | |
| 28 | ajoin(Atoms,Text). | |
| 29 | ||
| 30 | /* Pretty printer predicates */ | |
| 31 | :- public pp_op/2. | |
| 32 | pp_op(Id,PP) :- | |
| 33 | transition(_Src,Action,Id,_Dst),!, | |
| 34 | translate_event(Action,PP). | |
| 35 | ||
| 36 | pp_formulas([],[]). | |
| 37 | pp_formulas([ApTp|Rest],[PP|PPrest]) :- | |
| 38 | (pp_formula(ApTp,PP) -> true ; PP = '???'), | |
| 39 | pp_formulas(Rest,PPrest). | |
| 40 | pp_formula(ap(AP),ap(PP)) :- | |
| 41 | pp_ap(AP,PP). | |
| 42 | pp_formula(action(AP),tp(PP)) :- | |
| 43 | pp_tp(AP,PP). | |
| 44 | ||
| 45 | pp_ap(bpred(B),PP) :- !, | |
| 46 | translate_bexpression(B,PP1), | |
| 47 | ajoin(['{',PP1,'}'],PP). | |
| 48 | pp_ap(xtl_predicate_check(P),PP) :- atom(P),!, | |
| 49 | ajoin(['{',P,'}'],PP). | |
| 50 | pp_ap(enabled(TP),PP) :- !, | |
| 51 | pp_tp2(TP,TPP), | |
| 52 | ajoin(['e(',TPP,')'],PP). | |
| 53 | pp_ap(available(TP),PP) :- !, % TO DO: check_ap currently has no rule for Av(.) / available Operator !! | |
| 54 | % Was introduced for David Williams. | |
| 55 | % Only relevant for CSP and rewritten to [tau] U (e(tau) & not [tau]) or (e(a) & not e(tau))). | |
| 56 | % This is done in the Prolog module with the C interface for LTL. | |
| 57 | pp_tp2(TP,TPP), | |
| 58 | ajoin(['Av(',TPP,')'],PP). | |
| 59 | pp_ap(weak_fair(TP),PP) :- !, | |
| 60 | pp_tp2(TP,TPP), | |
| 61 | ajoin(['WF(',TPP,')'],PP). | |
| 62 | pp_ap(strong_fair(TP),PP) :- !, | |
| 63 | pp_tp2(TP,TPP), | |
| 64 | ajoin(['SF(',TPP,')'],PP). | |
| 65 | pp_ap(dlk(L),PP) :- !, | |
| 66 | maplist(pp_tp2,L,S), | |
| 67 | tools: ajoin_with_sep(S,',',SText), | |
| 68 | ajoin(['deadlock(',SText,')'],PP). | |
| 69 | pp_ap(ctrl(L),PP) :- !, | |
| 70 | maplist(pp_tp2,L,S), | |
| 71 | tools: ajoin_with_sep(S,',',SText), | |
| 72 | ajoin(['controller(',SText,')'],PP). | |
| 73 | pp_ap(det(L),PP) :- !, | |
| 74 | maplist(pp_tp2,L,S), | |
| 75 | tools: ajoin_with_sep(S,',',SText), | |
| 76 | ajoin(['deterministic(',SText,')'],PP). | |
| 77 | pp_ap(Expr,PP) :- | |
| 78 | write_to_codes(Expr,Codes), | |
| 79 | atom_codes(PP,Codes). | |
| 80 | ||
| 81 | pp_tp(change_expr(Comparator,TE),PP) :- | |
| 82 | pp_comp(Comparator,PPC), !, | |
| 83 | translate_bexpression(TE,TPP), | |
| 84 | ajoin([PPC,'({',TPP,'})'],PP). | |
| 85 | pp_tp(before_after(TE),PP) :- | |
| 86 | !, | |
| 87 | translate_bexpression(TE,TPP), | |
| 88 | ajoin(['BA({',TPP,'})'],PP). | |
| 89 | pp_tp(T,PP) :- | |
| 90 | pp_tp2(T,PP1), | |
| 91 | ajoin(['[',PP1,']'],PP). | |
| 92 | pp_tp2(operation_call(Action),OpCall) :- | |
| 93 | translate_action(Action,PP),!, ajoin(['calls(',PP,')'],OpCall). | |
| 94 | pp_tp2(btrans(event(Name)),Name) :- !. | |
| 95 | pp_tp2(btrans(event(Name,Predicate,_)),PP) :- !, | |
| 96 | translate_bexpression(Predicate,PP1), | |
| 97 | ajoin([Name,' | ',PP1],PP). | |
| 98 | pp_tp2(csp(tau,_Arity,[]),PP) :- !, PP = tau. | |
| 99 | pp_tp2(csp(tick,_Arity,[]),PP) :- !, PP = tick. | |
| 100 | pp_tp2(xtl(Op,0,[]),PP) :- !, PP = Op. | |
| 101 | pp_tp2(xtl(Op,_Arity,PatternArgs),PP) :- !, Term =.. [Op|PatternArgs], pp_tp2(Term,PP). | |
| 102 | pp_tp2(Action,PP) :- | |
| 103 | translate_action(Action,PP),!. | |
| 104 | pp_tp2(F,AF) :- | |
| 105 | (translate: translate_event(F,AF) -> true; AF='???'). | |
| 106 | ||
| 107 | pp_comp(eq,unchanged). | |
| 108 | pp_comp(neq,changed). | |
| 109 | pp_comp(lt,increasing). | |
| 110 | pp_comp(gt,decreasing). | |
| 111 | ||
| 112 | translate_action(bop(Name,_NumberArgs,_NumberResults,ArgPatterns,_ResPatterns),Transition) :- !, | |
| 113 | maplist(get_arg,ArgPatterns,ArgPatterns1), | |
| 114 | maplist(translate_bexpression,ArgPatterns1,PPArgPatterns), | |
| 115 | Op =.. [Name|PPArgPatterns], | |
| 116 | Transition = Op. | |
| 117 | /* ( NumberResults == 0 -> | |
| 118 | Transition = Op | |
| 119 | ; | |
| 120 | maplist(translate:translate_bexpression,ResPatterns,PPResPatterns), | |
| 121 | Transition = '-->'(Op,PPResPatterns) | |
| 122 | ). | |
| 123 | */ | |
| 124 | translate_action(csp(Channel,_Arity,Args),PP) :- | |
| 125 | (csp_with_bz_mode -> | |
| 126 | % in this mode we need to remove 'CSP:' | |
| 127 | translate: translate_event2(io(Args,Channel,_SPan),[_|S],[]) | |
| 128 | ; | |
| 129 | translate: translate_event2(io(Args,Channel,_SPan),S,[]) | |
| 130 | ), ajoin(S,PP). | |
| 131 | ||
| 132 | get_arg(N/Arg,Arg) :- nonvar(N),!. | |
| 133 | get_arg(Arg,Arg). | |
| 134 | ||
| 135 | /* Tests for pp_ltl_formula/2 */ | |
| 136 | ||
| 137 | :- assert_must_succeed((pp_ltl_formula(implies(globally(finally(until(true,false))),true),Text), | |
| 138 | Text = '(G (F ((true U false))) => true)')). | |
| 139 | ||
| 140 | :- assert_must_succeed((pp_ltl_formula(historically(not(release(yesterday(sink),next(deadlock)))),Text), | |
| 141 | Text = 'H (not((Y (sink) R X (deadlock))))')). | |
| 142 | ||
| 143 | :- assert_must_succeed(( | |
| 144 | pp_ltl_formula(implies( | |
| 145 | ap(enabled(btrans(event('A')))), | |
| 146 | or(weakuntil( | |
| 147 | ap(bpred(equal(identifier(op('x')),integer(1)))), | |
| 148 | ap(bpred(equal(identifier(op('x')),integer(2)))) ), | |
| 149 | finally(action(btrans(event('A')))))),Text), | |
| 150 | Text = '(e(A) => (({x = 1} W {x = 2}) or F ([A])))')). | |
| 151 | ||
| 152 | :- assert_must_succeed(( | |
| 153 | pp_ltl_formula(implies( | |
| 154 | current, | |
| 155 | and(once(ap(bpred(equal(identifier(op('x')),integer(1))))), | |
| 156 | action(btrans(event('A',equal(identifier(op('x')),integer(2)),_))))),Text), | |
| 157 | Text = '(current => (O ({x = 1}) & [A | x = 2]))')). | |
| 158 | ||
| 159 | :- assert_must_succeed(( | |
| 160 | pp_ltl_formula(fairnessimplication( | |
| 161 | and(strongassumptions(and(ap(strong_fair(btrans(event('A')))), | |
| 162 | ap(strong_fair(btrans(event('B')))))), | |
| 163 | weakassumptions(or(ap(weak_fair(btrans(event('A')))), | |
| 164 | ap(weak_fair(btrans(event('B'))))))), | |
| 165 | true),Text), | |
| 166 | Text = '(((SF(A) & SF(B))) & (WF(A) or WF(B)) => true)')). | |
| 167 | ||
| 168 | :- assert_must_succeed(( | |
| 169 | pp_ltl_formula(fairnessimplication( | |
| 170 | and(weakassumptions(and(ap(weak_fair(btrans(event('A')))), | |
| 171 | ap(weak_fair(btrans(event('B')))))), | |
| 172 | strongassumptions(or(ap(strong_fair(btrans(event('A')))), | |
| 173 | ap(strong_fair(btrans(event('B'))))))), | |
| 174 | true),Text), | |
| 175 | Text = '(((WF(A) & WF(B))) & (SF(A) or SF(B)) => true)')). | |
| 176 | ||
| 177 | :- assert_must_succeed(( | |
| 178 | pp_ltl_formula(globally(finally(ap(available(btrans(event('A')))))),Text), | |
| 179 | Text = 'G (F (Av(A)))')). | |
| 180 | ||
| 181 | /* Simple Pretty Printer for LTL/CTL Formulas */ | |
| 182 | ||
| 183 | pp_ltl(true,['true'|S],S). | |
| 184 | pp_ltl(false,['false'|S],S). | |
| 185 | pp_ltl(deadlock,['deadlock'|S],S). | |
| 186 | pp_ltl(sink,['sink'|S],S). | |
| 187 | pp_ltl(goal,['goal'|S],S). | |
| 188 | pp_ltl(state_error,['error'|S],S). | |
| 189 | pp_ltl(det_output,['det_output'|S],S). | |
| 190 | pp_ltl(non_det,['non_det'|S],S). | |
| 191 | pp_ltl(current,['current'|S],S). | |
| 192 | pp_ltl(globally(F),['G ('| S],T) :- !, pp_ltl(F,S,[')'|T]). | |
| 193 | pp_ltl(finally(F),['F ('| S],T) :- !, pp_ltl(F,S,[')'|T]). | |
| 194 | pp_ltl(next(F),['X ('|S],T) :- !, pp_ltl(F,S,[')'|T]). | |
| 195 | pp_ltl(yesterday(F),['Y ('|S],T) :- !, pp_ltl(F,S,[')'|T]). | |
| 196 | pp_ltl(since(F,G),['('|S],T) :- !, | |
| 197 | pp_ltl(F,S,[' S '| S1]), | |
| 198 | pp_ltl(G,S1,[')'|T]). | |
| 199 | pp_ltl(once(F),['O ('|S],T) :- !, pp_ltl(F,S,[')'|T]). | |
| 200 | pp_ltl(historically(F),['H ('|S],T) :- !, pp_ltl(F,S,[')'|T]). | |
| 201 | pp_ltl(until(F,G),['('|S],T) :- !, | |
| 202 | pp_ltl(F,S,[' U '| S1]), | |
| 203 | pp_ltl(G,S1,[')'|T]). | |
| 204 | pp_ltl(weakuntil(F,G),['('|S],T) :- !, | |
| 205 | pp_ltl(F,S,[' W '| S1]), | |
| 206 | pp_ltl(G,S1,[')'|T]). | |
| 207 | pp_ltl(release(F,G),['('|S],T) :- !, | |
| 208 | pp_ltl(F,S,[' R '| S1]), | |
| 209 | pp_ltl(G,S1,[')'|T]). | |
| 210 | pp_ltl(implies(F,G),['('|S],T) :- !, | |
| 211 | pp_ltl(F,S,[' => '| S1]), | |
| 212 | pp_ltl(G,S1,[')'|T]). | |
| 213 | pp_ltl(equivalent(F,G),['('|S],T) :- !, | |
| 214 | pp_ltl(F,S,[' <=> '| S1]), | |
| 215 | pp_ltl(G,S1,[')'|T]). | |
| 216 | pp_ltl(fairnessimplication(FairAssump,Formula),['('|S],T) :- !, | |
| 217 | pp_ltl_fairness(FairAssump,S,[' => '| S1]), | |
| 218 | pp_ltl(Formula,S1,[')'|T]). | |
| 219 | pp_ltl(or(F,G),['('|S],T) :- !, | |
| 220 | pp_ltl(F,S,[' or '| S1]), | |
| 221 | pp_ltl(G,S1,[')'|T]). | |
| 222 | pp_ltl(and(F,G),['('|S],T) :- !, | |
| 223 | pp_ltl(F,S,[' & '| S1]), | |
| 224 | pp_ltl(G,S1,[')'|T]). | |
| 225 | pp_ltl(not(F),['not('| S],T) :- !, pp_ltl(F,S,[')'|T]). | |
| 226 | pp_ltl(ap(AP),[Pred|S],S) :- !, pp_ap(AP,Pred). | |
| 227 | pp_ltl(action(A),[Action|S],S) :- !, pp_tp(A,Action). | |
| 228 | ||
| 229 | /* Pretty Print CTL Part */ | |
| 230 | pp_ltl(ena(F),['EX ('|S],T) :- !, pp_ltl(F,S,[')'|T]). | |
| 231 | pp_ltl(en(F), ['EX ('|S],T) :- !, pp_ltl(F,S,[')'|T]). | |
| 232 | pp_ltl(an(F), ['AX ('|S],T) :- !, pp_ltl(F,S,[')'|T]). | |
| 233 | pp_ltl(eg(F), ['EG ('|S],T) :- !, pp_ltl(F,S,[')'|T]). | |
| 234 | pp_ltl(ef(F), ['EF ('|S],T) :- !, pp_ltl(F,S,[')'|T]). | |
| 235 | pp_ltl(ag(F), ['AG ('|S],T) :- !, pp_ltl(F,S,[')'|T]). | |
| 236 | pp_ltl(af(F), ['AF ('|S],T) :- !, pp_ltl(F,S,[')'|T]). | |
| 237 | pp_ltl(eu(F,G), ['( E '|S],T) :- !, | |
| 238 | pp_ltl(F,S,[' U '|S1]), | |
| 239 | pp_ltl(G,S1,[')'|T]). | |
| 240 | ||
| 241 | /* Pretty Print Fairness Assumptions */ | |
| 242 | pp_ltl_fairness(strongassumptions(F),S1,T) :- !, | |
| 243 | (F=all -> | |
| 244 | S1 = ['SEF'|S], T = S | |
| 245 | ; | |
| 246 | S1 = ['('|S], pp_ltl_fairness1(F,S,[')'|T]) | |
| 247 | ). | |
| 248 | pp_ltl_fairness(weakassumptions(F),S1,T) :- !, | |
| 249 | (F=all -> | |
| 250 | S1 = ['WEF'|S], T = S | |
| 251 | ; | |
| 252 | S1 = ['('|S], pp_ltl_fairness1(F,S,[')'|T]) | |
| 253 | ). | |
| 254 | pp_ltl_fairness(and(F,G),S,T) :- !, | |
| 255 | pp_ltl_fairness(F,S,[' & '|S1]), | |
| 256 | pp_ltl_fairness(G,S1,T). | |
| 257 | ||
| 258 | pp_ltl_fairness1(and(F,G),['('|S],T) :- !, | |
| 259 | pp_ltl_fairness1(F,S,[' & '|S1]), | |
| 260 | pp_ltl_fairness1(G,S1,[')'|T]). | |
| 261 | pp_ltl_fairness1(or(F,G),S,T) :- !, | |
| 262 | pp_ltl_fairness1(F,S,[' or '|S1]), | |
| 263 | pp_ltl_fairness1(G,S1,T). | |
| 264 | pp_ltl_fairness1(ap(AP),[Pred|S],S) :- !, | |
| 265 | pp_ap(AP,Pred). | |
| 266 | ||
| 267 | ||
| 268 | ||
| 269 | ||
| 270 | % TODO: extend and possibly use in pp_ltl above | |
| 271 | get_ltl_sub_formulas(and(A,B),'&',[A,B]). | |
| 272 | get_ltl_sub_formulas(or(A,B),'or',[A,B]). | |
| 273 | get_ltl_sub_formulas(implies(A,B),'=>',[A,B]). | |
| 274 | get_ltl_sub_formulas(equivalent(A,B),'<=>',[A,B]). | |
| 275 | get_ltl_sub_formulas(until(A,B),'U',[A,B]). | |
| 276 | get_ltl_sub_formulas(weakuntil(A,B),'W',[A,B]). | |
| 277 | get_ltl_sub_formulas(release(A,B),'R',[A,B]). | |
| 278 | get_ltl_sub_formulas(since(A,B),'S',[A,B]). | |
| 279 | get_ltl_sub_formulas(globally(A),'G',[A]). | |
| 280 | get_ltl_sub_formulas(finally(A),'F',[A]). | |
| 281 | get_ltl_sub_formulas(not(A),'not',[A]). | |
| 282 | get_ltl_sub_formulas(next(A),'X',[A]). | |
| 283 | get_ltl_sub_formulas(yesterday(A),'Y',[A]). | |
| 284 | get_ltl_sub_formulas(historically(A),'H',[A]). | |
| 285 | get_ltl_sub_formulas(once(A),'O',[A]). |