1 | % (c) 2017-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 | :- module(alloy2b, [load_alloy_ast_prolog_file/1, | |
5 | load_alloy_model/2, | |
6 | load_alloy_model/3, | |
7 | translate_alloy_model/3, | |
8 | get_command_names/1, | |
9 | get_skipped_command_names/1, | |
10 | get_translated_command_names/1, | |
11 | load_command_in_current_translation/1]). | |
12 | ||
13 | :- meta_predicate map_translate(3, -, -, -). | |
14 | ||
15 | :- use_module(library(lists)). | |
16 | :- use_module(library(file_systems)). | |
17 | :- use_module(library(sets), [subtract/3]). | |
18 | :- use_module(library(aggregate),[forall/2]). | |
19 | :- use_module(probsrc(debug)). | |
20 | :- use_module(probsrc(bmachine)). | |
21 | :- use_module(probsrc(translate)). | |
22 | :- use_module(probsrc(preferences), [get_preference/2,temporary_set_preference/2]). | |
23 | :- use_module(probsrc(error_manager)). | |
24 | :- use_module(probsrc(bmachine)). | |
25 | :- use_module(probsrc(bsyntaxtree)). | |
26 | :- use_module(probsrc(specfile), [set_animation_minor_mode/1]). | |
27 | :- use_module(probsrc(tools_strings), [atom_prefix/2, atom_suffix/2, ajoin/2]). | |
28 | :- use_module(probsrc(tools_positions), [row_col_to_position/3, is_valid_position/1]). | |
29 | ||
30 | :- set_prolog_flag(double_quotes, codes). | |
31 | ||
32 | :- dynamic natural_type/1, integer_type/1, sig_field_id/2, enumerated_set/1, singleton_set/1, total_function/2, | |
33 | ordered_signature/1, extending_signatures/1, is_sub_signature_of/3, is_seq/1, | |
34 | maxseq/1, artificial_parent_type/3, abstract_sig/1, definition_without_param/1, | |
35 | skipped_command/2, translated_command/2, model_path/1, fact/1, assertion/1. | |
36 | :- volatile natural_type/1, integer_type/1, sig_field_id/2, enumerated_set/1, singleton_set/1, total_function/2, | |
37 | ordered_signature/1, extending_signatures/1, is_sub_signature_of/3, is_seq/1, | |
38 | maxseq/1, artificial_parent_type/3, abstract_sig/1, definition_without_param/1, | |
39 | skipped_command/2, translated_command/2, model_path/1, fact/1, assertion/1. | |
40 | ||
41 | default_upper_bound_scope(3). | |
42 | default_max_seq(7). | |
43 | set_max_seq(Max) :- number(Max), Max \== -1, !, assertz(maxseq(Max)). | |
44 | set_max_seq(_) :- default_max_seq(D), assertz(maxseq(D)). | |
45 | ||
46 | % An automated translation from Alloy to classical B. | |
47 | % The Alloy abstract syntax tree is translated to an untyped B AST as supported by ProB. | |
48 | % Afterwards, the untyped AST can be typechecked and loaded by ProB. | |
49 | % The used positions are from the Alloy parser and thus refer to the Alloy model. | |
50 | % Usage: | |
51 | % load_alloy_model/2: input is an Alloy model represented as a Prolog term as provided by the Kotlin translation; the model is translated to an untyped B AST and loaded by ProB | |
52 | % translate_alloy_model/3: input is an Alloy model represented as a Prolog term as provided by the Kotlin translation, output is an untyped B AST | |
53 | % Run tests: | |
54 | % - launch ProB Tcl/Tk or ProB CLI | |
55 | % - 'use_module(probsrc('alloy2b/alloy2b')).' | |
56 | % - 'error_manager:reset_errors , plunit:run_tests(full_machine_translation).'. | |
57 | % - run a single test: 'error_manager:reset_errors , plunit:run_tests(full_machine_translation:directctrl).' | |
58 | % - 'bmachine:full_b_machine(L) , translate:print_machine(L)'. | |
59 | % Further tests are integrated in testsrc(testcases). | |
60 | % | |
61 | % KNOWN ISSUES / TO DOs | |
62 | % ---------------------- | |
63 | % - nested quantifiers: the usage of facts for singleton_set is error prone, and cannot deal with | |
64 | % nested quantifiers with variable clashes, in particular when in the inner quantifier the | |
65 | % identifier is not a singleton set | |
66 | % - nested multiplicity annotations for non-binary relations (Joshua: is this possible in Alloy?) | |
67 | % - variable capture due to use of DEFINITIONS | |
68 | % - modulo currently only works for positive numbers | |
69 | ||
70 | load_alloy_ast_prolog_file(File) :- | |
71 | \+ file_exists(File, read), | |
72 | !, | |
73 | add_error(load_alloy_ast_prolog_file, 'Alloy AST file has not been created:', [File]), | |
74 | fail. | |
75 | load_alloy_ast_prolog_file(File) :- | |
76 | load_alloy_ast_prolog_file_for_command(_, File). | |
77 | ||
78 | %% load_alloy_ast_prolog_file_for_command(?CmdName, +File). | |
79 | % First command is chosen as main command if CmdName is a variable. | |
80 | load_alloy_ast_prolog_file_for_command(CmdName, File) :- | |
81 | formatsilent('Opening Alloy AST file: ~w~n', [File]), | |
82 | open(File, read, Stream), | |
83 | safe_read_term(Stream, AlloyTerm), | |
84 | close(Stream), | |
85 | retractall(model_path(_)), | |
86 | asserta(model_path(File)), | |
87 | load_alloy_model(CmdName, AlloyTerm, 'alloytranslation'). | |
88 | ||
89 | %% load_command_in_current_translation(+CmdName). | |
90 | % If the selected command is currently not translated, reload the machine and translate the command. | |
91 | % Otherwise, there is nothing to do. | |
92 | % Note that we do not translate commands with different scopes at once. | |
93 | load_command_in_current_translation(CmdName) :- | |
94 | ( skipped_command(_, CmdName) | |
95 | -> model_path(File), | |
96 | load_alloy_ast_prolog_file_for_command(CmdName, File) | |
97 | ; true | |
98 | ). | |
99 | ||
100 | safe_read_term(Stream, Term) :- | |
101 | catch(read(Stream, Term), error(E,_), ( | |
102 | add_internal_error('Exception while reading Prolog term from stream:', E), | |
103 | fail | |
104 | )). | |
105 | ||
106 | assert_integer_type_from_preference :- | |
107 | get_preference(alloy_use_implementable_integers, Pref), | |
108 | retractall(integer_type(_)), | |
109 | retractall(natural_type(_)), | |
110 | ( Pref == true | |
111 | -> asserta(integer_type('INT')), | |
112 | asserta(natural_type('NAT')) | |
113 | ; asserta(integer_type('INTEGER')), | |
114 | asserta(natural_type('NATURAL')) | |
115 | ). | |
116 | ||
117 | % TO DO: fix translation of Marc's machines | |
118 | ||
119 | % TO DO: do we need to translate the 'expect' keyword for commands? is it inlined by the alloy parser? | |
120 | ||
121 | load_alloy_model(AlloyTerm, AlloyFile) :- | |
122 | % use the first run or check command by default for models that use sequences, otherwise all commands are translated | |
123 | load_alloy_model(_FirstRunOrCheck, AlloyTerm, AlloyFile). | |
124 | ||
125 | load_alloy_model(CurrentCommand, AlloyTerm, AlloyFile) :- | |
126 | assert_integer_type_from_preference, | |
127 | reset_errors, | |
128 | debug_format(19, 'Translating AST to B.~n', []), | |
129 | retract_state, | |
130 | % tools_printing:nested_print_term(AlloyTerm,10), | |
131 | translate_alloy_model(CurrentCommand, AlloyTerm, UntypedBAst), | |
132 | !, | |
133 | UntypedBAst = machine(generated(none,AbstractMachine)), % like @generated pragma | |
134 | set_animation_minor_mode(alloy), | |
135 | b_load_machine_from_term(AlloyFile, complete_machine('alloytranslation',[AbstractMachine],[AlloyFile])). | |
136 | load_alloy_model(CurrentCommand, _, _) :- | |
137 | \+ integer(CurrentCommand), | |
138 | add_error(load_alloy_model, 'Current command has to be an integer:', CurrentCommand), | |
139 | fail. | |
140 | load_alloy_model(_, AlloyTerm, _) :- | |
141 | add_error(load_alloy_model, 'Translating Alloy AST failed:', AlloyTerm), | |
142 | fail. | |
143 | ||
144 | process_options(Options) :- | |
145 | ( \+member(sequences:_, Options) | |
146 | ; \+member(parent_types:_, Options) | |
147 | ), | |
148 | !, | |
149 | add_error(alloy2b, 'Alloy2B parser failed to analyze types or check for sequences (be sure Alloy2B parser library is up-to-date).',[]), | |
150 | fail. | |
151 | process_options(Options) :- | |
152 | process_options_safe(Options). | |
153 | ||
154 | process_options_safe(Options) :- | |
155 | select(parent_types:SetOfTypeSets, Options, RestOptions), | |
156 | !, | |
157 | assert_artificial_parent_types(SetOfTypeSets), | |
158 | process_options_safe(RestOptions). | |
159 | process_options_safe(_). | |
160 | ||
161 | % Given a list of list of signature names. We need to introduce a parent type in B for each list of signatures. | |
162 | assert_artificial_parent_types(SetOfTypeSets) :- | |
163 | assert_artificial_parent_types(0, SetOfTypeSets). | |
164 | ||
165 | assert_artificial_parent_types(_, []). | |
166 | assert_artificial_parent_types(C, [SetOfTypes|T]) :- | |
167 | gen_identifier(C, "_universe", ParentId), | |
168 | gen_identifier(C, "_Universe", PParentId), | |
169 | C1 is C + 1, | |
170 | assert_parent_id_for_types(SetOfTypes, ParentId, PParentId), | |
171 | assert_artificial_parent_types(C1, T). | |
172 | ||
173 | assert_parent_id_for_types([], _, _). | |
174 | assert_parent_id_for_types([SignatureName|T], ParentId, PParentId) :- | |
175 | asserta(artificial_parent_type(SignatureName,ParentId,PParentId)), | |
176 | assert_parent_id_for_types(T, ParentId, PParentId). | |
177 | ||
178 | % Log the current module to define unique field names. | |
179 | :- dynamic module_in_scope_but_root/2, current_module_in_scope/1. | |
180 | :- volatile module_in_scope_but_root/2, current_module_in_scope/1. | |
181 | ||
182 | % Translate an Alloy model and all its imports (excluding some utility libraries we provide manual implementations for). | |
183 | translate_alloy_model(CurrentCommand, alloy(RootModule,Models), UntypedBAst) :- | |
184 | retractall(translate:atelierb_mode(_)), | |
185 | select(alloy_model((RootModule,Alias),Facts,Assertions,Commands,Functions,Sigs,OSigNames,Opts), Models, RestModels), | |
186 | !, | |
187 | catch( | |
188 | translate_alloy_models(CurrentCommand, alloy_model((RootModule,Alias),Facts,Assertions,Commands,Functions,Sigs,OSigNames,Opts), RestModels, UntypedBAst), | |
189 | Exc, | |
190 | (add_internal_error('Exception while translating Alloy model:',Exc),fail)). | |
191 | ||
192 | translate_alloy_models(CurrentCommand, alloy_model((RootModule,RootAlias),Facts,Assertions,Commands,Functions,Signatures,OSigNames,Options), RestModels, UntypedBAst) :- | |
193 | empty_machine_acc(MAcc), | |
194 | forall( | |
195 | member(alloy_model((ModuleName,Alias),_,_,_,_,_,_,_),RestModels), | |
196 | asserta(module_in_scope_but_root(ModuleName,Alias)) | |
197 | ), | |
198 | ( ground(CurrentCommand) | |
199 | -> NOptions = [is_root_module,translate_command:CurrentCommand|Options] | |
200 | ; NOptions = [is_root_module|Options] | |
201 | ), | |
202 | translate_alloy_models_acc(MAcc, | |
203 | alloy_model((RootModule,RootAlias),Facts,Assertions,Commands,Functions,Signatures,OSigNames,NOptions), | |
204 | RestModels, UntypedBAst). | |
205 | ||
206 | translate_alloy_models_acc(MAcc, AlloyModel, RestModels, UntypedBAst) :- | |
207 | translate_single_alloy_model(AlloyModel, MAcc, NewMAcc), | |
208 | ( RestModels == [] | |
209 | -> debug_println(19, build_machine_ast), | |
210 | build_machine_ast(NewMAcc, UntypedBAst), | |
211 | assert_atelierb_mode, | |
212 | % tools_printing:nested_print_term(UntypedBAst,20), | |
213 | ( debug_mode(off) -> true | |
214 | ; nl, | |
215 | ( translate:print_raw_machine_terms(UntypedBAst) | |
216 | ; debug_println(19, 'translate:print_raw_machine_terms/1 failed.')), | |
217 | nl | |
218 | ), ! | |
219 | ; RestModels = [ImportedModel|T], | |
220 | translate_alloy_models_acc(NewMAcc, ImportedModel, T, UntypedBAst)). | |
221 | ||
222 | skip_module(ModuleName) :- | |
223 | supported_default_module(ModuleName); unsupported_module(ModuleName). | |
224 | ||
225 | assert_atelierb_mode :- | |
226 | get_preference(alloy_translation_for_proof, true), | |
227 | !, | |
228 | temporary_set_preference(translate_print_typing_infos, true), | |
229 | translate:set_atelierb_mode(native). | |
230 | assert_atelierb_mode. | |
231 | ||
232 | % Alloy utility modules we provide manual implementations for. | |
233 | supported_default_module('util''boolean'). | |
234 | supported_default_module('util''integer'). | |
235 | supported_default_module('util''natural'). | |
236 | supported_default_module('util''ordering'). | |
237 | supported_default_module('util''relation'). | |
238 | supported_default_module('util''sequence'). | |
239 | ||
240 | unsupported_module('util''sequniv'). % uses univ type in function args; we probably do not need this module as it provides the same functions as util/sequence | |
241 | unsupported_module('util''seqrel'). % TO DO: ? | |
242 | unsupported_module('util''time'). % TO DO: ? | |
243 | % following modules are translated using the tool: util/graph, util/ternary | |
244 | ||
245 | % Give unique names for identifiers of imported modules (prepend module name). | |
246 | % Identifier names in the root module are not extended. | |
247 | assert_current_module_in_scope(ModuleName, Alias) :- | |
248 | module_in_scope_but_root(ModuleName, Alias), | |
249 | retractall(current_module_in_scope(_)), | |
250 | asserta(current_module_in_scope(ModuleName)). | |
251 | assert_current_module_in_scope(_, _). | |
252 | ||
253 | option_defines_cmd_for_root(Options, CmdName) :- | |
254 | member(is_root_module, Options), | |
255 | member(translate_command:CmdName, Options). | |
256 | ||
257 | get_command_by_name(Commands, CmdName, MainCommand, CommandId, RestCommands) :- | |
258 | get_command_by_name(Commands, CmdName, 0, 0, MainCommand, CommandId, [], RestCommands). | |
259 | ||
260 | get_command_by_name([Command|T], CmdName, Run, Check, MainCommand, CommandId, AccRest, RestCommands) :- | |
261 | functor(Command, Functor, _), | |
262 | ( Functor == check | |
263 | -> NewCheck is Check + 1, | |
264 | NewRun = Run, | |
265 | TempId = Check | |
266 | ; NewRun is Run + 1, | |
267 | NewCheck = Check, | |
268 | TempId = Run | |
269 | ), | |
270 | number_codes(TempId, CTempId), | |
271 | atom_codes(ATempId, CTempId), | |
272 | atom_concat(Functor, ATempId, TempCmdName), | |
273 | ( CmdName == TempCmdName | |
274 | -> MainCommand = Command, | |
275 | CommandId = TempId, | |
276 | append(AccRest, T, RestCommands) | |
277 | ; append(AccRest, [Command], NAccRest), % append to keep order | |
278 | get_command_by_name(T, CmdName, NewRun, NewCheck, MainCommand, CommandId, NAccRest, RestCommands) | |
279 | ). | |
280 | ||
281 | ||
282 | translate_single_alloy_model(alloy_model((ModuleName,_),_,_,_,_,_,_,_), MAcc, MAcc) :- | |
283 | skip_module(ModuleName), | |
284 | !. | |
285 | translate_single_alloy_model(alloy_model((ModuleName,Alias),facts(Facts),assertions(Assertions),commands(Commands),functions(Functions), | |
286 | signatures(Signatures),ordered_signatures(OSigNames),Options), MAcc, NewMAcc) :- | |
287 | % singleton sets are asserted at runtime using singleton_set/1 | |
288 | % accumulate all translations, afterwards we build the untyped machine ast | |
289 | assert_current_module_in_scope(ModuleName, Alias), | |
290 | process_options(Options), | |
291 | maplist(assert_ordered_sig_name, OSigNames), | |
292 | ( option_defines_cmd_for_root(Options, CmdName) | |
293 | -> % a specific command id is set to be translated | |
294 | get_command_by_name(Commands, CmdName, MainCommand, CommandId, _RestCommands) | |
295 | ; % select the first command to be translated | |
296 | ( Commands = [MainCommand|_RestCommands] | |
297 | -> CommandId = 0 | |
298 | ; add_internal_error('Could not find main command id: ',CommandId) | |
299 | ) | |
300 | ), | |
301 | % assertions and facts are inlined in run or check commands and not translated in the properties | |
302 | length(Commands,NrOfCommands), | |
303 | get_command_scopes(MainCommand, GS, ExactScopes, UpBoundScopes, BitWidth, CmdMaxSeq), | |
304 | formatsilent('Scopes for main command ~w (~w commands) : global=~w, exact=~w, bw=~w, maxseq=~w~n', | |
305 | [CommandId,NrOfCommands,GS,ExactScopes,BitWidth,CmdMaxSeq]), | |
306 | set_max_seq(CmdMaxSeq), % we need max seq scope for signature fields | |
307 | !, | |
308 | ||
309 | debug_println(19, translating_signatures), | |
310 | maplist(preprocess_signature, Signatures), % assertz some signature names as singleton | |
311 | ||
312 | define_artificial_parent_types_if_necessary(MAcc, MAcc1, GS, ExactScopes, UpBoundScopes), | |
313 | map_translate(translate_signature, MAcc1, Signatures, MAcc2), | |
314 | ||
315 | ( number(GS), GS > 0 | |
316 | -> preferences:temporary_set_preference(globalsets_fdrange, GS) % TO DO: improve | |
317 | ; true | |
318 | ), | |
319 | ( get_preference(alloy_scopeless_translation,true) % do not set any scopes (useful for proof assistants) | |
320 | -> MAcc3 = MAcc2 | |
321 | ; try_set_bitwidth(BitWidth), | |
322 | get_signature_names_from_machine_acc(MAcc2, SignatureNames), | |
323 | translate_scopes_for_command(MainCommand,SignatureNames,ScopePreds), | |
324 | extend_machine_with_conjuncts(properties, [ScopePreds], MAcc2, MAcc3) | |
325 | ), | |
326 | debug_println(19, translating_functions), | |
327 | map_translate(translate_function, MAcc3, Functions, MAcc4), | |
328 | debug_println(19, translating_facts), | |
329 | map_translate(translate_fact, MAcc4, Facts, MAcc5), | |
330 | debug_println(19, translating_assertions), | |
331 | map_translate(translate_assertion, MAcc5, Assertions, MAcc6), | |
332 | debug_println(19, translating_commands), | |
333 | translate_commands(CommandId, MainCommand, MAcc6, Commands, MAcc7), | |
334 | !, | |
335 | finalize_extending_signatures(MAcc7, NewMAcc). | |
336 | ||
337 | get_command_name(CmdFunctor, ID, CmdName) :- | |
338 | number_codes(ID, Codes), | |
339 | atom_codes(AID, Codes), | |
340 | atom_concat(CmdFunctor, AID, CmdName). | |
341 | ||
342 | calculate_max_parent_type_card(SigNames, GlobalScope, ExactScopes, UpBoundScopes, MaxCard) :- | |
343 | calculate_max_parent_type_card(SigNames, GlobalScope, ExactScopes, UpBoundScopes, 0, MaxCard). | |
344 | ||
345 | calculate_max_parent_type_card([], _, _, _, Acc, Acc). | |
346 | calculate_max_parent_type_card([SigName|T], GlobalScope, ExactScopes, UpBoundScopes, Acc, MaxCard) :- | |
347 | ( (member((SigName,Scope), ExactScopes); member((SigName,Scope), UpBoundScopes)) | |
348 | -> NAcc is Acc + Scope | |
349 | ; NAcc is Acc + GlobalScope | |
350 | ), | |
351 | calculate_max_parent_type_card(T, GlobalScope, ExactScopes, UpBoundScopes, NAcc, MaxCard). | |
352 | ||
353 | % Introduce parent types in B for Alloy signatures without a common parent type but univ that interact with each other. | |
354 | define_artificial_parent_types_if_necessary(MAcc, NewMAcc, GlobalScope, ExactScopes, UpBoundScopes) :- | |
355 | findall((ParentId,PParentId), artificial_parent_type(_, ParentId, PParentId), TParents), | |
356 | remove_dups(TParents, Parents), | |
357 | define_artificial_parent_types(MAcc, Parents, NewMAcc, GlobalScope, ExactScopes, UpBoundScopes). | |
358 | ||
359 | define_artificial_parent_types(MAcc, [], MAcc, _, _, _). | |
360 | define_artificial_parent_types(MAcc, [(ParentId,PParentId)|T], NewMAcc, GlobalScope, ExactScopes, UpBoundScopes) :- | |
361 | PParentId = identifier(_,PParentName), | |
362 | findall(SigName, artificial_parent_type(SigName,ParentId,_), DSigNames), | |
363 | DSigNames \== [], | |
364 | remove_dups(DSigNames, SigNames), | |
365 | maplist(translate_e_p, SigNames, TSigNames), | |
366 | % should be: _universe0 = S1 \/ .. \/ Sn | |
367 | % ProB currently does not set the deferred set size of P0 in cbc mode if it is defined within an operation's precondition | |
368 | % (ProB uses the preference 'deferred_setsize' which then might be a contradiction) | |
369 | % this equality is valid if it is translated in the properties and not as a precondition which is the case | |
370 | % if we only translate a single run or check command | |
371 | % _universe0 = S1 \/ .. \/ Sn | |
372 | join_untyped_ast_nodes(union, TSigNames, Union), | |
373 | ParentDomain = equal(none,ParentId,Union), | |
374 | % TO DO: use above code if this issue is solved, otherwise stick to this translation which is just less performant in case we do not constrain _P0 | |
375 | % if we constrain _P0, e.g., using id(_P0), this could be an error as _P0 possibly contains more elements than necessary | |
376 | % Note: if preference 'deferred_setsize' is smaller than n, this is still a contradiction | |
377 | % S1 \/ .. \/ Sn <: universe0 | |
378 | %join_untyped_ast_nodes(union, TSigNames, Union), | |
379 | %ParentDomain = subset(none, Union, ParentId), | |
380 | extend_machine_acc(properties, MAcc, ParentDomain, MAcc1), | |
381 | calculate_max_parent_type_card(SigNames, GlobalScope, ExactScopes, UpBoundScopes, MaxCard), | |
382 | % _universe0 <: _Universe0 & card(_Universe0) = MaxCard | |
383 | Subset = subset(none,ParentId,PParentId), | |
384 | CardEq = equal(none,card(none,PParentId),integer(none,MaxCard)), | |
385 | extend_machine_acc(properties, MAcc1, conjunct(none,Subset,CardEq), MAcc2), | |
386 | % S1 /\ S2 = {} & .. & Sn-1 /\ Sn = {} | |
387 | disjoint_set_of_signatures(TSigNames, DisjointConstraint), | |
388 | extend_machine_acc(properties, MAcc2, DisjointConstraint, MAcc3), | |
389 | extend_machine_acc(constants, MAcc3, ParentId, MAcc4), | |
390 | extend_machine_acc(sets, MAcc4, deferred_set(none,PParentName), MAcc5), | |
391 | define_artificial_parent_types(MAcc5, T, NewMAcc, GlobalScope, ExactScopes, UpBoundScopes). | |
392 | ||
393 | disjoint_set_of_signatures(TSigNames, DisjointConstraint) :- | |
394 | get_signature_pairs(TSigNames, SigPairs), | |
395 | disjoint_set_of_signatures_from_pairs(SigPairs, [], DisjointList), | |
396 | join_untyped_ast_nodes(conjunct, DisjointList, DisjointConstraint). | |
397 | ||
398 | disjoint_set_of_signatures_from_pairs([], Acc, Acc). | |
399 | disjoint_set_of_signatures_from_pairs([(S1,S2)|T], Acc, DisjointList) :- | |
400 | Disjoint = equal(none,intersection(none,S1,S2),empty_set(none)), | |
401 | disjoint_set_of_signatures_from_pairs(T, [Disjoint|Acc], DisjointList). | |
402 | ||
403 | get_signature_pairs(TSigNames, SigPairs) :- | |
404 | findall((S1,S2), (nth0(I1, TSigNames, S1), nth0(I2, TSigNames, S2), I1 < I2), SigPairs). | |
405 | ||
406 | ||
407 | map_translate(_, MAcc, [], MAcc). | |
408 | map_translate(TypePred, MAcc, [Part|T], Res) :- | |
409 | call(TypePred, MAcc, Part, NewMAcc), | |
410 | map_translate(TypePred, NewMAcc, T, Res). | |
411 | ||
412 | try_set_bitwidth(BitWidth) :- | |
413 | BitWidth = bitwidth(BW), | |
414 | number(BW), | |
415 | BW > 0, | |
416 | !, | |
417 | MaxInt is truncate(2**(BW-1))-1, | |
418 | MinInt is truncate(-(2**(BW-1))), | |
419 | preferences:temporary_set_preference(maxint, MaxInt), | |
420 | preferences:temporary_set_preference(minint, MinInt). | |
421 | try_set_bitwidth(_). | |
422 | ||
423 | % things that need to be done before translating the signatures, e.g., assertz singleton set info | |
424 | preprocess_signature(signature(Name,_Fields,_Facts,Options,_)) :- | |
425 | % assertz signatures for singleton checks | |
426 | assert_singleton_set(Options, Name), | |
427 | assert_abstract_sig(Options, Name), | |
428 | assert_enumerated_set(Options, Name). | |
429 | ||
430 | % translate signature declaration sig Name {Fields} | |
431 | translate_signature(MAcc, signature(Name,Fields,Facts,Options,pos(Col,Row)), NewMAcc) :- | |
432 | extend_machine_acc(signatures, MAcc, [Name], MAcc1), | |
433 | translate_signature_aux(Name, Options, pos(Col,Row), MAcc1, MAcc2), | |
434 | translate_signature_fields(Fields, Name, MAcc2, MAcc3), | |
435 | translate_e_p(Name, TName), | |
436 | map_translate(translate_signature_fact(TName), MAcc3, Facts, NewMAcc). | |
437 | ||
438 | translate_signature_aux(Name, Options, Pos, MAcc, NewMAcc) :- | |
439 | % ordered signatures are defined as distinct sets of integer when translating a command | |
440 | member(ordered, Options), | |
441 | !, | |
442 | define_ordered_signature_functions(Pos, MAcc, Name, MAcc1), | |
443 | extend_machine_acc(signatures, MAcc1, [Name], MAcc2), | |
444 | translate_e_p(Name, TID), | |
445 | integer_type(IntOrInteger), | |
446 | formatsilent('Ordered signature = ~w~n',[Name]), | |
447 | extend_machine_acc(properties, MAcc2, | |
448 | member(none,TID,pow_subset(none,integer_set(none,IntOrInteger))), MAcc3), | |
449 | define_sig_as_set_or_constant_aux(constants, MAcc3, Name, Options, Pos, NewMAcc). | |
450 | translate_signature_aux(Name, Options, Pos, MAcc, NewMAcc) :- | |
451 | define_sig_as_set_or_constant(MAcc, Name, Options, Pos, NewMAcc). | |
452 | ||
453 | define_ordered_signature_functions(POS, MAcc, Sig, NewMAcc) :- | |
454 | translate_pos(POS, BPOS), | |
455 | gen_identifier(0, "_c_", TIDX), | |
456 | TIDS = identifier(BPOS,s), | |
457 | translate_e_p(Sig, TSig), | |
458 | MemberX = member(BPOS,TIDX,TSig), | |
459 | % next_Sig(s) == IF succ[s] <: Sig THEN succ[s] ELSE {} END | |
460 | atom_concat(next_, Sig, NextName), | |
461 | SuccessorImg = image(BPOS,successor(BPOS),TIDS), | |
462 | PredecessorImg = image(BPOS,predecessor(BPOS),TIDS), | |
463 | extend_machine_acc(definitions, MAcc, | |
464 | expression_definition(BPOS,NextName,[TIDS],if_then_else(BPOS,subset(BPOS,SuccessorImg,TSig),SuccessorImg,empty_set(BPOS))), MAcc1), | |
465 | % nexts_Sig(s) == {x|x:Sig & min({x} \/ s) /= x} | |
466 | atom_concat(nexts_, Sig, NextsName), | |
467 | NextsBody = conjunct(BPOS,MemberX,not_equal(BPOS,min(BPOS,union(BPOS,set_extension(BPOS,[TIDX]),TIDS)),TIDX)), | |
468 | extend_machine_acc(definitions, MAcc1, | |
469 | expression_definition(BPOS,NextsName,[TIDS],comprehension_set(BPOS,[TIDX],NextsBody)), MAcc2), | |
470 | atom_concat(prev_, Sig, PrevName), | |
471 | % prev_Sig(s) == IF pred[s] <: Sig THEN pred[s] ELSE {} END | |
472 | extend_machine_acc(definitions, MAcc2, | |
473 | expression_definition(BPOS,PrevName,[TIDS],if_then_else(BPOS,subset(BPOS,PredecessorImg,TSig),PredecessorImg,empty_set(BPOS))), MAcc3), | |
474 | % prevs_Sig(s) == {x|x:Sig & max({x} \/ s) /= x} | |
475 | atom_concat(prevs_, Sig, PrevsName), | |
476 | PrevsBody = conjunct(BPOS,MemberX,not_equal(BPOS,max(BPOS,union(BPOS,set_extension(BPOS,[TIDX]),TIDS)),TIDX)), | |
477 | extend_machine_acc(definitions, MAcc3, | |
478 | expression_definition(BPOS,PrevsName,[TIDS],comprehension_set(BPOS,[TIDX],PrevsBody)), NewMAcc). | |
479 | ||
480 | translate_signature_fields(Fields, SignatureName) --> | |
481 | { translate_e_p(SignatureName, TSignatureName) }, | |
482 | translate_signature_fields_aux(Fields, SignatureName, TSignatureName). | |
483 | ||
484 | translate_signature_fields_aux([], _, _) --> | |
485 | []. | |
486 | translate_signature_fields_aux([Field|T], SignatureName, TSignatureName) --> | |
487 | translate_signature_field(SignatureName, TSignatureName, Field), | |
488 | translate_signature_fields_aux(T, SignatureName, TSignatureName). | |
489 | ||
490 | translate_signature_field(SignatureName, TSignatureName, Field, MAcc, NewMAcc) :- | |
491 | translate_signature_field_aux(SignatureName, TSignatureName, Field, TField, MAcc, MAcc1), | |
492 | extend_machine_acc(properties, MAcc1, TField, NewMAcc). | |
493 | ||
494 | translate_signature_field_aux(SignatureName, TSignatureName, | |
495 | field(FieldID,Expr,_FieldType,Options,POS), TFieldPred, MAcc, NewMAcc) :- | |
496 | FieldID = identifier(FieldName,_,_), | |
497 | asserta(sig_field_id(FieldName, SignatureName)), | |
498 | translate_e_p(FieldID, TFieldID), | |
499 | extend_machine_acc(constants, MAcc, TFieldID, MAcc1), | |
500 | translate_pos(POS, BPOS), | |
501 | disjoint_field_declaration(TSignatureName, BPOS, Options, MAcc1, TFieldID, NewMAcc), | |
502 | translate_field_decl(BPOS, SignatureName, TSignatureName, Expr, FieldID, TFieldID, TFieldPred), | |
503 | !. | |
504 | ||
505 | translate_function_field(field(FieldName,Expr,type(_Type,_Arity),_Options,POS), TField) :- | |
506 | % (Functor = function -> assert_singleton_set(FieldName) ; true) , % NO LONGER NEEDED | |
507 | translate_e_p(FieldName, TTFieldName), | |
508 | strip_singleton_set_from_this(TTFieldName, TFieldName), % necessary for nested definition calls with 'this' | |
509 | translate_pos(POS, BPOS), | |
510 | fun_or_pred_decl_special_cases(Expr, TFieldName, BPOS, TField, _IsSingleton), | |
511 | !. | |
512 | % TO DO: !! what if IsSingleton is set !! | |
513 | disjoint_field_declaration(TSignatureName, TPos, Options, MAcc, TFieldName, NewMAcc) :- | |
514 | member(disj, Options), | |
515 | !, | |
516 | % all a, b: S | a != b implies no a.f & b.f | |
517 | IdA = identifier(TPos,a), | |
518 | IdB = identifier(TPos,b), | |
519 | ImplLhs = conjunct(TPos,conjunct(TPos,member(TPos,IdA,TSignatureName),member(TPos,IdB,TSignatureName)), | |
520 | not_equal(TPos,IdA,IdB)), | |
521 | ImplRhs = equal(TPos,intersection(TPos,image(TPos,TFieldName,set_extension(none,[IdA])), | |
522 | image(TPos,TFieldName,set_extension(none,[IdB]))),empty_set(TPos)), | |
523 | Disjoint = forall(TPos,[IdA,IdB],implication(TPos,ImplLhs,ImplRhs)), | |
524 | extend_machine_acc(properties, MAcc, Disjoint, NewMAcc). | |
525 | disjoint_field_declaration(_, _, _, MAcc, _, MAcc). | |
526 | ||
527 | ||
528 | is_disjoint_quantifier(Fields) :- | |
529 | Fields = [F|_], | |
530 | is_disjoint_quantifier_field(F). % assumption: all or no field is disjoint | |
531 | is_disjoint_quantifier_field(field(_FieldName,_Expr,type(_Type,_Arity),Options,_POS)) :- | |
532 | member(disj, Options). | |
533 | ||
534 | translate_quantifier_field(field(FieldName,Expr,Type,_Options,POS), TField, NName) :- | |
535 | translate_pos(POS, BPOS), | |
536 | fun_or_pred_decl_special_cases(Expr, TFieldName, BPOS, TField, IsSingleton), | |
537 | !, | |
538 | ( IsSingleton == singleton | |
539 | -> FieldName = identifier(Name,_,_), | |
540 | prepend_identifier_of_module_import(Name, Type, NName), | |
541 | assert_singleton_set(NName) | |
542 | ; NName = alloy2b_none | |
543 | ), | |
544 | translate_e_p(FieldName, TFieldName). | |
545 | ||
546 | translate_fact(MAcc, fact(Expr,_Pos), NewMAcc) :- | |
547 | translate_e_p(Expr, TExpr), | |
548 | asserta(fact(Expr)), | |
549 | extend_machine_acc(properties, MAcc, TExpr, NewMAcc). | |
550 | ||
551 | translate_assertion(MAcc, fact(Expr,_Pos), NewMAcc) :- | |
552 | translate_e_p(Expr, TExpr), | |
553 | asserta(assertion(Expr)), | |
554 | extend_machine_acc(assertions, MAcc, TExpr, NewMAcc). | |
555 | ||
556 | % Signature facts: Identifiers may refer to the signature and are joined with 'this'. | |
557 | % We then need a universal quantification using 'this'. | |
558 | translate_signature_fact(TSignatureName, MAcc, Expr, NewMAcc) :- | |
559 | alloy_expr_contains_join(Expr), | |
560 | translate_e_p(Expr, TExpr), % we quantify in such a way that 'this' is a singleton_set_id; 'this' is always considered a singleton set id | |
561 | ThisID = identifier(none,'this'), | |
562 | TImplication = implication(none,subset(none,set_extension(none,[ThisID]),TSignatureName),TExpr), | |
563 | FORALL = forall(none,[ThisID],TImplication), | |
564 | extend_machine_acc(properties, MAcc, FORALL, NewMAcc). | |
565 | translate_signature_fact(_TSignatureName, MAcc, Expr, NewMAcc) :- | |
566 | translate_e_p(Expr, TExpr), | |
567 | extend_machine_acc(properties, MAcc, TExpr, NewMAcc). | |
568 | ||
569 | % TO DO: write generic AST traversal predicate | |
570 | alloy_expr_contains_join(AndOr) :- | |
571 | AndOr =.. [Functor,ListOfAsts,_Pos], | |
572 | !, | |
573 | member(Functor, [and,or]), | |
574 | member(Ast, ListOfAsts), | |
575 | alloy_expr_contains_join(Ast), | |
576 | !. | |
577 | %alloy_expr_contains_join(identifier('this',_,_)). | |
578 | alloy_expr_contains_join(join(_,_,_Type,_Pos)). | |
579 | % alloy_expr_contains_join_with_this(join(Arg1,Arg2,_Type,_Pos)) :- | |
580 | % Arg1 = identifier('this',_,_) ; Arg2 = identifier('this',_,_). | |
581 | alloy_expr_contains_join(Expr) :- | |
582 | Expr =.. [_Functor,Arg1,Arg2,_Type,_Pos], | |
583 | !, | |
584 | ( alloy_expr_contains_join(Arg1) -> | |
585 | true | |
586 | ; alloy_expr_contains_join(Arg2) | |
587 | ). | |
588 | alloy_expr_contains_join(Expr) :- | |
589 | Expr =.. [_Functor,Arg,_Type,_Pos], | |
590 | alloy_expr_contains_join(Arg). | |
591 | alloy_expr_contains_join(Expr) :- | |
592 | Expr =.. [Functor,_Params,Fields,Body,_Type,_POS], | |
593 | member(Functor, [all,no,some,one,lone,comprehension]), | |
594 | ( alloy_expr_contains_join(Body) -> | |
595 | true | |
596 | ; member(F, Fields), | |
597 | alloy_expr_contains_join(F) -> | |
598 | true | |
599 | ). | |
600 | ||
601 | % translate fun NAME Decls { Body } or pred Name Decls { Body } | |
602 | translate_function(MAcc, FunctionOrPredicate, NewMAcc) :- | |
603 | FunctionOrPredicate =.. [Functor,Name,Params,Decls,Body,POS], | |
604 | alloy_to_b_operator(Functor, BFunctor), | |
605 | maplist(translate_e_p, Params, TParams), | |
606 | % identifier like 'this' are singleton sets, parameters have to be identifiers | |
607 | maplist(strip_singleton_set, TParams, TTParams), | |
608 | maplist(translate_function_field, Decls, TDecls), | |
609 | translate_e_p(Body, TBody), | |
610 | translate_pos(POS, BPOS), | |
611 | ( Functor == function | |
612 | -> | |
613 | ( TDecls == [] | |
614 | -> | |
615 | TResult = TBody % no need to create set comprehension | |
616 | ; % create {temp | TDecls & temp : TBody } | |
617 | append(TDecls, [member(none,identifier(none,'_fun_res_'),TBody)], BehaviorList), | |
618 | join_untyped_ast_nodes(conjunct, BehaviorList, Behavior), | |
619 | TResult = comprehension_set(BPOS,[identifier(none,'_fun_res_')],Behavior) | |
620 | ), | |
621 | UAst =.. [BFunctor,BPOS,Name,TTParams,TResult] | |
622 | ; append(TDecls, [TBody], BehaviorList), | |
623 | join_untyped_ast_nodes(conjunct, BehaviorList, Behavior), | |
624 | UAst =.. [BFunctor,BPOS,Name,TTParams,Behavior] | |
625 | ), | |
626 | ( TTParams == [] -> asserta(definition_without_param(Name)); true), | |
627 | extend_machine_acc(definitions, MAcc, UAst, NewMAcc). | |
628 | ||
629 | ||
630 | % ------------ | |
631 | ||
632 | % command utilities | |
633 | get_command_pos(Command,POS) :- arg(8,Command,POS). | |
634 | is_command_id(ID,Command) :- arg(7,Command,Idx), | |
635 | (Idx = index(I) -> ID=I | |
636 | ; functor(Command,F,Arity),add_internal_error('Not a command: ',F/Arity:Idx:Command),fail). | |
637 | ||
638 | get_command_scopes(Command, GlobalScope, ExactScopes, UpBoundScopes, BitWidth, MaxSeq) :- | |
639 | Command =.. [_Functor,_Body,global_scope(TGlobalScope), | |
640 | exact_scopes(ExactScopes),upper_bound_scopes(UpBoundScopes),BitWidth,MaxSeq|_], | |
641 | !, | |
642 | ( (TGlobalScope == -1, ExactScopes == [], UpBoundScopes == []) | |
643 | -> default_upper_bound_scope(DefaultScope), | |
644 | debug_format(19, 'Applying default global scope of ~w', [DefaultScope]), | |
645 | GlobalScope = DefaultScope | |
646 | ; GlobalScope = TGlobalScope | |
647 | ), | |
648 | debug_format(19, 'Command scopes: global=~w, exact=~w, upbound=~w,bitwidth=~w~n,maxseq=~w~n', | |
649 | [GlobalScope,ExactScopes,UpBoundScopes,BitWidth,MaxSeq]). | |
650 | get_command_scopes(Command, -1, [], [], none, none) :- | |
651 | add_error(alloy2b, 'Cannot process Alloy Command:', Command). | |
652 | ||
653 | % ------------ | |
654 | ||
655 | get_command_names(CmdNames) :- | |
656 | get_skipped_command_names(Skipped), | |
657 | get_translated_command_names(Translated), | |
658 | append(Skipped, Translated, CmdNames). | |
659 | ||
660 | get_skipped_command_names(SkippedCmds) :- | |
661 | findall(Name, skipped_command(_,Name), SkippedCmds). | |
662 | ||
663 | get_translated_command_names(SkippedCmds) :- | |
664 | findall(Name, translated_command(_,Name), SkippedCmds). | |
665 | ||
666 | %% translate_command(+MainId, +MainCommand, +MAcc, +Command, -NewMAcc). | |
667 | % Has a counter for run and check commands to have a coherent enumeration, which is important | |
668 | % for reloading machines for a specific command. | |
669 | translate_commands(MainId, MainCommand, MAcc, Commands, NewMAcc) :- | |
670 | translate_commands(MainId, MainCommand, MAcc, Commands, 0, 0, NewMAcc). | |
671 | ||
672 | %% translate_commands(+MainId, +MainCommand, +MAcc, +Commands, +AmountOfRun, +AmounfOfCheck, -NewMAcc). | |
673 | translate_commands(_, _, MAcc, [], _, _, MAcc). | |
674 | translate_commands(MainId, MainCommand, MAcc, [Command|T], Run, Check, NewMAcc) :- | |
675 | translate_command(MainId, MainCommand, MAcc, Command, Run, Check, NewRun, NewCheck, NewMAcc1), | |
676 | translate_commands(MainId, MainCommand, NewMAcc1, T, NewRun, NewCheck, NewMAcc). | |
677 | ||
678 | ||
679 | ||
680 | %% translate_command(+MainId, +MainCommand, +MAcc, +Command, +Run, +Check, -NewRun, -NewCheck, -NewMAcc). | |
681 | % Translates a command if it has the same scope as the main command. | |
682 | % Otherwise, a command is skipped for translation and not part of the resulting B machine. | |
683 | % Uses global states translated_command/2 and skipped_command/2. In both terms, the first argument is the overall command index. | |
684 | % Here, we count indices for run and check independently in order to have a naming that is useful for the user. | |
685 | translate_command(MainId, MainCommand, MAcc, Command, Run, Check, NewRun, NewCheck, NewMAcc) :- | |
686 | Command =.. [Functor,_,GlobalScope,ExactScopes,UpBoundScopes,BitWidth,MaxSeq,_,Pos], | |
687 | ( atom_concat(check, _, Functor) | |
688 | -> NewRun = Run, | |
689 | NewCheck is Check + 1, | |
690 | CommandId = Check | |
691 | ; NewCheck = Check, | |
692 | NewRun is Run + 1, | |
693 | CommandId = Run | |
694 | ), | |
695 | functor(MainCommand, MainFunctor, _), | |
696 | % skip if not main command and scopes do not match with main command scopes | |
697 | \+ (MainFunctor == Functor, CommandId == MainId), | |
698 | \+ MainCommand =.. [_,_,GlobalScope,ExactScopes,UpBoundScopes,BitWidth,MaxSeq,_,_], | |
699 | !, | |
700 | get_command_name(Functor, CommandId, CmdName), | |
701 | is_command_id(ID,Command), | |
702 | asserta(skipped_command(ID,CmdName)), | |
703 | ajoin(['Skipping Alloy ',Functor, ' command ',CommandId,', scopes incompatible with main command: '],Msg), | |
704 | translate_pos(Pos,BPos), | |
705 | add_message(alloy2b_command_skipped,Msg,[GlobalScope,ExactScopes,UpBoundScopes,BitWidth,MaxSeq],BPos), | |
706 | NewMAcc=MAcc. | |
707 | translate_command(_,_,MAcc, Command, Run, Check, NewRun, NewCheck, NewMAcc) :- | |
708 | Command =.. [Functor,Body|_], | |
709 | get_command_pos(Command,POS), | |
710 | % if a model does not use sequences or all commands have a common scope and we are not in proof mode, we translate all commands | |
711 | !, | |
712 | is_command_id(ID,Command), | |
713 | ( Functor = check | |
714 | -> NewRun = Run, | |
715 | NewCheck is Check + 1, | |
716 | CommandId = Check | |
717 | ; Functor = run, | |
718 | NewCheck = Check, | |
719 | NewRun is Run + 1, | |
720 | CommandId = Run | |
721 | ), | |
722 | get_command_name(Functor, CommandId, CmdName), | |
723 | asserta(translated_command(ID,CmdName)), | |
724 | translate_command_aux(MAcc, Functor, Body, POS, CommandId, NewMAcc). | |
725 | translate_command(_,_,MAcc, Command, _, _, _, _, MAcc) :- | |
726 | add_error(alloy2b,'Unknown Alloy command (be sure Alloy2B parser library is up-to-date):', Command). | |
727 | ||
728 | translate_command_aux(MAcc, Functor, Body, POS, CommandId, NewMAcc) :- | |
729 | debug_println(19, translating_command(Functor)), | |
730 | translate_pos(POS, BPOS), | |
731 | ( get_preference(alloy_translation_for_proof, true) | |
732 | -> remove_negations_of_check_command(POS, Functor, Body, NBody1), | |
733 | remove_facts_and_assertions_from_command(NBody1, NBody), | |
734 | translate_e_p(NBody, TBody), | |
735 | extend_machine_acc(assertions, MAcc, TBody, NewMAcc) | |
736 | ; remove_facts_and_assertions_from_command(Body, NBody), | |
737 | translate_e_p(NBody, TBody), | |
738 | number_codes(CommandId, CCommandId), | |
739 | atom_codes(ACommandId, CCommandId), | |
740 | atom_concat(Functor, ACommandId, OperationName), | |
741 | Operation = operation(BPOS,identifier(BPOS,OperationName),[],[],precondition(BPOS,TBody,skip(none))), | |
742 | extend_machine_acc(operations, MAcc, Operation, NewMAcc) | |
743 | ). | |
744 | ||
745 | remove_facts_and_assertions_from_list([], []). | |
746 | remove_facts_and_assertions_from_list([AlloyTerm|T], NT) :- | |
747 | (fact(AlloyTerm); assertion(AlloyTerm)), | |
748 | !, | |
749 | debug_println(19, remove_facts_and_assertions_from_command), | |
750 | remove_facts_and_assertions_from_list(T, NT). | |
751 | remove_facts_and_assertions_from_list([AlloyTerm|T], [AlloyTerm|NT]) :- | |
752 | remove_facts_and_assertions_from_list(T, NT). | |
753 | ||
754 | %% remove_facts_and_assertions_from_command(AlloyTerm, NAlloyTerm). | |
755 | % Facts and assertions are inlined in Alloy commands by Alloy's parser. | |
756 | % We here remove facts and assertions from a command's body since they are translated as machine properties. | |
757 | remove_facts_and_assertions_from_command(and(AlloyTerms,APOS), NAlloyConj) :- | |
758 | !, | |
759 | remove_facts_and_assertions_from_list(AlloyTerms, NAlloyTerms), | |
760 | ( NAlloyTerms = [SingleTerm] | |
761 | -> NAlloyConj = SingleTerm | |
762 | ; NAlloyConj = and(NAlloyTerms,APOS) | |
763 | ). | |
764 | remove_facts_and_assertions_from_command(AlloyTerm, AlloyTerm). | |
765 | ||
766 | % We need to remove the negation of assertions stated in a check command when | |
767 | % translating to the assertions to prove in AterlierB. | |
768 | remove_negations_of_check_command(POS,check, Body, NBody) :- | |
769 | Body= and(List,APos), | |
770 | remove_negations_of_check_command_list(POS, List, NList), | |
771 | NBody = and(NList,APos). | |
772 | remove_negations_of_check_command(_, run, Body, Body). | |
773 | ||
774 | remove_negations_of_check_command_list(_, [], []). | |
775 | remove_negations_of_check_command_list(pos(_, CommandCol), [AlloyTerm|T], NList) :- | |
776 | get_alloy_position(AlloyTerm, APOS), | |
777 | APOS = pos(_, Col), | |
778 | Col >= CommandCol, | |
779 | % filter those assertions stated in the command (do not remove negations of inlined assertions) | |
780 | !, | |
781 | remove_negation_alloy_term(AlloyTerm, NewAlloyTerm), | |
782 | remove_negations_of_check_command_list(pos(_, CommandCol), T, NT), | |
783 | NList = [NewAlloyTerm|NT]. | |
784 | remove_negations_of_check_command_list(POS, [AlloyTerm|T], [AlloyTerm|NT]) :- | |
785 | remove_negations_of_check_command_list(POS, T, NT). | |
786 | ||
787 | remove_negation_alloy_term(not(AlloyTerm,_,_), AlloyTerm) :- | |
788 | !. | |
789 | remove_negation_alloy_term(AlloyTerm, AlloyTerm). | |
790 | ||
791 | translate_scopes_for_command(Command,SignatureNames,ResPredicate) :- | |
792 | get_command_scopes(Command, GlobalScope, ExactScopes, UpBoundScopes, _BitWidth, _CmdMaxSeq), | |
793 | maxseq(MaxSeq), | |
794 | % global scope, exact scopes and bitwidth | |
795 | % We do not need to set the bitwidth since we have real integers in B. | |
796 | ResPredicate = conjunct(none,SeqScopePred,conjunct(none,TScopes,TGlobalScopes)), | |
797 | % translate exact and upper bound scopes first | |
798 | translate_exact_and_upper_bound_scopes(0, ExactScopes, UpBoundScopes, EUSignatureNames, TScopes, OrdSigCounter1), | |
799 | % if present, define the global scope for the remaining signatures | |
800 | translate_global_scope(OrdSigCounter1, GlobalScope, SignatureNames, EUSignatureNames, TGlobalScopes, _), | |
801 | translate_seq_scopes(MaxSeq, SeqScopePred). | |
802 | ||
803 | translate_seq_scopes(MaxSeq, ScopePred) :- | |
804 | findall((Seq,MaxSeq), is_seq(Seq), Seqs), | |
805 | maplist(generate_leq_scope_predicate, Seqs, ScopePreds), | |
806 | join_predicates(conjunct, ScopePreds, ScopePred). | |
807 | ||
808 | % no exact scopes defined | |
809 | translate_exact_and_upper_bound_scopes(OrdSigCounter, [], [], ExactSigNames, TGlobalScopes, OrdSigCounter) :- | |
810 | !, | |
811 | ExactSigNames = [], | |
812 | TGlobalScopes = truth(none). | |
813 | translate_exact_and_upper_bound_scopes(OrdSigCounter, ExactScopes, UpBoundScopes, SignatureNames, TScopes, NewOrdSigCounter) :- | |
814 | trans_exact_or_upbnd_scopes(upper, OrdSigCounter, UpBoundScopes, [], UpBoundSigNames, [], TempTUpScopes, OrdSigCnt1), | |
815 | trans_exact_or_upbnd_scopes(exact, OrdSigCnt1, ExactScopes, [], ExactSigNames, [], TempTExactScopes, NewOrdSigCounter), | |
816 | append(TempTUpScopes, TempTExactScopes, TempTScopes), | |
817 | append(ExactSigNames, UpBoundSigNames, SignatureNames), | |
818 | join_predicates(conjunct, TempTScopes, TScopes). | |
819 | ||
820 | trans_exact_or_upbnd_scopes(_, OrdSigCounter, [], NamesAcc, NamesAcc, TScopeAcc, TScopeAcc, OrdSigCounter). | |
821 | trans_exact_or_upbnd_scopes(ScopeType, OrdSigCounter, [(SigName,Scope)|T], NameAcc, ExactSigNames, TScopeAcc, TempTExactScopes, NewOrdSigCnt) :- | |
822 | is_ordered_signature(SigName), | |
823 | !, | |
824 | define_ordered_signature_as_integer_set(OrdSigCounter, SigName, Scope, TScope, Cnt1), | |
825 | trans_exact_or_upbnd_scopes(ScopeType, Cnt1, T, [SigName|NameAcc], ExactSigNames, [TScope|TScopeAcc], TempTExactScopes, NewOrdSigCnt). | |
826 | trans_exact_or_upbnd_scopes(exact, OrdSigCounter, [(SigName,Scope)|T], NameAcc, ExactSigNames, TScopeAcc, TempTExactScopes, NewOrdSigCnt) :- | |
827 | generate_exact_scope_predicate((SigName,Scope), TScope), | |
828 | trans_exact_or_upbnd_scopes(exact, OrdSigCounter, T, [SigName|NameAcc], ExactSigNames, [TScope|TScopeAcc], TempTExactScopes, NewOrdSigCnt). | |
829 | trans_exact_or_upbnd_scopes(upper, OrdSigCounter, [(SigName,Scope)|T], NameAcc, ExactSigNames, TScopeAcc, TempTExactScopes, NewOrdSigCnt) :- | |
830 | generate_leq_scope_predicate((SigName,Scope), TScope), | |
831 | trans_exact_or_upbnd_scopes(upper, OrdSigCounter, T, [SigName|NameAcc], ExactSigNames, [TScope|TScopeAcc], TempTExactScopes, NewOrdSigCnt). | |
832 | ||
833 | generate_exact_scope_predicate((SigName,Scope), TScopePred) :- | |
834 | translate_e_p(SigName, TSignatureName), | |
835 | % debug_format(19,'Exact scope for SignatureName=~w with scope ~w~n',[SigName,Scope]), | |
836 | TScopePred = equal(none,card(none,TSignatureName),integer(none,Scope)). | |
837 | generate_leq_scope_predicate((SigName,Scope), TScopePred) :- | |
838 | translate_e_p(SigName, TSignatureName), | |
839 | TScopePred = less_equal(none,card(none,TSignatureName),integer(none,Scope)). | |
840 | ||
841 | define_ordered_signatures_as_integer_set([], OrdSigCounter, _, [], OrdSigCounter). | |
842 | define_ordered_signatures_as_integer_set([TSignatureName|T], OrdSigCounter, Scope, [TScope|TScopes], NewOrdSigCounter) :- | |
843 | define_ordered_signature_as_integer_set(OrdSigCounter, TSignatureName, Scope, TScope, OrdSigCounter1), | |
844 | define_ordered_signatures_as_integer_set(T, OrdSigCounter1, Scope, TScopes, NewOrdSigCounter). | |
845 | ||
846 | define_ordered_signature_as_integer_set(OrdSigCounter, TSignatureName, Scope, TScope, NewOrdSigCounter) :- | |
847 | NewOrdSigCounter is OrdSigCounter+Scope, | |
848 | UpBound is NewOrdSigCounter-1, | |
849 | % TSignatureName = OrdSigCounter...UpBound | |
850 | TScope = equal(none,identifier(none,TSignatureName), | |
851 | interval(none,integer(none,OrdSigCounter),integer(none,UpBound))). | |
852 | ||
853 | % translate_global_scope(inputCounter,inputGlobalScope,inputSigNames,inputExactSigNames, OutPredicate,OutNewCounter) | |
854 | translate_global_scope(OrdSigCounter, -1, _, _, TGlobalScopes, OrdSigCounter) :- | |
855 | !, | |
856 | TGlobalScopes = truth(none). % no global scope defined | |
857 | translate_global_scope(OrdSigCounter, GlobalScope, SignatureNames, ExactSigNames, | |
858 | TGlobalScopes, NewOrdSigCounter) :- | |
859 | split_ordered_unordered_signatures(SignatureNames, ExactSigNames, OrderedSigNames, RestSignatureNames), | |
860 | translate_global_scope_aux(RestSignatureNames, GlobalScope, [], TempTGlobalScopes), | |
861 | join_predicates(conjunct, TempTGlobalScopes, TTGlobalScopes), | |
862 | % ordered signatures are a subset of integers, OrdSigCounter: from which number on can we assign objects | |
863 | define_ordered_signatures_as_integer_set(OrderedSigNames, OrdSigCounter, GlobalScope, TOrderedScopes, NewOrdSigCounter), | |
864 | join_predicates(conjunct, [TTGlobalScopes|TOrderedScopes], TGlobalScopes). | |
865 | ||
866 | translate_global_scope_aux([], _, Acc, Acc). | |
867 | translate_global_scope_aux([SignatureName|T], GlobalScope, Acc, TGlobalScopes) :- | |
868 | \+ abstract_sig(SignatureName), | |
869 | translate_e_p(SignatureName, TSignatureName), | |
870 | TScope = less_equal(none,card(none,TSignatureName),integer(none,GlobalScope)), | |
871 | translate_global_scope_aux(T, GlobalScope, [TScope|Acc], TGlobalScopes). | |
872 | translate_global_scope_aux([_|T], GlobalScope, Acc, TGlobalScopes) :- | |
873 | translate_global_scope_aux(T, GlobalScope, Acc, TGlobalScopes). | |
874 | ||
875 | split_ordered_unordered_signatures(SNames, ExactSNames, OSNames, Rest) :- | |
876 | split_ordered_unordered_signatures(SNames, [], [], TOSNames, TRest), | |
877 | % remove exact signatures which are defined separately | |
878 | subtract(TOSNames, ExactSNames, OSNames), | |
879 | subtract(TRest, ExactSNames, Rest). | |
880 | ||
881 | split_ordered_unordered_signatures([], OAcc, RAcc, OAcc, RAcc). | |
882 | split_ordered_unordered_signatures([SName|T], OAcc, RAcc, OSNames, Rest) :- | |
883 | is_ordered_signature(SName), | |
884 | \+ member(SName, OAcc), | |
885 | !, | |
886 | split_ordered_unordered_signatures(T, [SName|OAcc], RAcc, OSNames, Rest). | |
887 | split_ordered_unordered_signatures([SName|T], OAcc, RAcc, OSNames, Rest) :- | |
888 | \+ is_ordered_signature(SName), | |
889 | \+ member(SName, RAcc), | |
890 | !, | |
891 | split_ordered_unordered_signatures(T, OAcc, [SName|RAcc], OSNames, Rest). | |
892 | split_ordered_unordered_signatures([_|T], OAcc, RAcc, OSNames, Rest) :- | |
893 | split_ordered_unordered_signatures(T, OAcc, RAcc, OSNames, Rest). | |
894 | ||
895 | % signature in (subset) | |
896 | define_sig_as_set_or_constant(MAcc, Name, Options, POS, NewMAcc) :- | |
897 | memberchk(subset(Parents), Options), | |
898 | !, | |
899 | % maplist(assert_extending_signature(Name),Parents) , % subset signatures do not need to be distinct | |
900 | translate_pos(POS, BPOS), | |
901 | define_sig_as_set_or_constant_aux(constants, MAcc, Name, Options, BPOS, MAcc1), | |
902 | % TO DO: consider several parents -> we need the universe type | |
903 | Parents = [Parent|_], | |
904 | translate_e_p(Name, TName), | |
905 | translate_e_p(Parent, TParent), | |
906 | TNode = subset(BPOS,TName,TParent), | |
907 | extend_machine_acc(properties, MAcc1, TNode, NewMAcc). | |
908 | % signature extends | |
909 | define_sig_as_set_or_constant(MAcc, Name, Options, POS, NewMAcc) :- | |
910 | member(subsig(Parent), Options), | |
911 | !, | |
912 | ( member(one, Options) -> | |
913 | One = one | |
914 | ; One = set | |
915 | ), | |
916 | assert_extending_signature(Name, Parent, One), | |
917 | translate_pos(POS, BPOS), | |
918 | define_sig_as_set_or_constant_aux(constants, MAcc, Name, Options, BPOS, MAcc1), | |
919 | translate_e_p(Name, TName), | |
920 | translate_e_p(Parent, TParent), | |
921 | TNode = subset(BPOS,TName,TParent), | |
922 | extend_machine_acc(properties, MAcc1, TNode, NewMAcc). | |
923 | % default signature, but we introduced an artifical parent type, i.e., a deferred set in B | |
924 | define_sig_as_set_or_constant(MAcc, Name, Options, POS, NewMAcc) :- | |
925 | translate_pos(POS, BPOS), | |
926 | artificial_parent_type(Name, ParentId, _), | |
927 | !, | |
928 | translate_e_p(Name, TName), | |
929 | TNode = subset(BPOS,TName,ParentId), | |
930 | extend_machine_acc(properties, MAcc, TNode, MAcc1), | |
931 | define_sig_as_set_or_constant_aux(constants, MAcc1, Name, Options, BPOS, NewMAcc). | |
932 | % default signature | |
933 | define_sig_as_set_or_constant(MAcc, Name, Options, POS, NewMAcc) :- | |
934 | translate_pos(POS, BPOS), | |
935 | define_sig_as_set_or_constant_aux(sets, MAcc, Name, Options, BPOS, NewMAcc). | |
936 | ||
937 | define_sig_as_set_or_constant_aux(sets, MAcc, Name, Options, BPOS, NewMAcc) :- | |
938 | member(one, Options), | |
939 | !, | |
940 | atom_concat('_', Name, EnumElement), | |
941 | % TO DO: couldn't figure out untyped ast of enumerated set, define enumerated set S = {_S} instead of constant etc. | |
942 | extend_machine_acc(sets, MAcc, deferred_set(BPOS,Name), MAcc1), | |
943 | extend_machine_acc(constants, MAcc1, identifier(BPOS,EnumElement), MAcc2), | |
944 | extend_machine_acc(properties, MAcc2, equal(BPOS,identifier(BPOS,Name),set_extension(BPOS,[identifier(BPOS,EnumElement)])), NewMAcc). | |
945 | define_sig_as_set_or_constant_aux(sets, MAcc, Name, Options, BPOS, NewMAcc) :- | |
946 | member(some, Options), | |
947 | !, | |
948 | extend_machine_acc(sets, MAcc, deferred_set(BPOS,Name), MAcc1), | |
949 | extend_machine_acc(properties, MAcc1, greater_equal(BPOS,card(BPOS,identifier(BPOS,Name)),integer(BPOS,1)), NewMAcc). | |
950 | define_sig_as_set_or_constant_aux(sets, MAcc, Name, Options, BPOS, NewMAcc) :- | |
951 | member(lone, Options), | |
952 | !, | |
953 | extend_machine_acc(sets, MAcc, deferred_set(BPOS,Name), MAcc1), | |
954 | extend_machine_acc(properties, MAcc1, less_equal(BPOS,card(BPOS,identifier(BPOS,Name)),integer(BPOS,1)), NewMAcc). | |
955 | define_sig_as_set_or_constant_aux(sets, MAcc, Name, _Options, BPOS, NewMAcc) :- | |
956 | extend_machine_acc(sets, MAcc, deferred_set(BPOS,Name), NewMAcc). | |
957 | define_sig_as_set_or_constant_aux(constants, MAcc, Name, _Options, BPOS, NewMAcc) :- | |
958 | extend_machine_acc(constants, MAcc, identifier(BPOS,Name), NewMAcc). | |
959 | ||
960 | % -------- | |
961 | % Semantic translation rules: translate expression or predicate | |
962 | % -------- | |
963 | translate_e_p2(A, TA) :- %% functor(A,F,N),format('~n TRANSLATE: ~w/~w : ~w ~n',[F,N,A]), | |
964 | translate_quantifier_e(A, TA), | |
965 | !. | |
966 | translate_e_p2(A, TA) :- | |
967 | translate_cst_e_p(A, TA), | |
968 | !. | |
969 | translate_e_p2(A, TA) :- | |
970 | translate_unary_e_p(A, TA), | |
971 | !. | |
972 | translate_e_p2(A, TA) :- | |
973 | translate_binary_e_p(A, TA), | |
974 | !. | |
975 | translate_e_p2(A, TA) :- | |
976 | translate_if_then_else(A, TA), | |
977 | !. | |
978 | translate_e_p2(A, _) :- | |
979 | add_error(load_alloy_model, 'Translation failed for:', A), | |
980 | fail. | |
981 | ||
982 | translate_e_p(A, TA) :- | |
983 | translate_e_p2(A, TA), | |
984 | check_valid_raw_translation(A,TA). | |
985 | ||
986 | :- load_files(library(system), [when(compile_time), imports([environ/2])]). | |
987 | :- if(environ(prob_safe_mode,true)). | |
988 | check_valid_raw_translation(A,Raw) :- \+ ground(Raw), | |
989 | add_error(alloy2b,'Non-ground raw AST:',Raw,Raw), | |
990 | write(A),nl, trace, translate_e_p2(A,_), | |
991 | fail. | |
992 | :- endif. | |
993 | check_valid_raw_translation(_,_). | |
994 | % Translation in the context where we expect an integer | |
995 | % This is the E_{int} translation function in the article | |
996 | % The following rule is not necessary: untyped_b_ast_to_integer will peel off set extensions | |
997 | %translate_e_p_integer(identifier(ID,_Type,POS), identifier(BPOS,ID)) :- %Type=integer, ? | |
998 | % is_singleton_set(ID),!, | |
999 | % translate_pos(POS, BPOS). | |
1000 | translate_e_p_integer(integer(A,POS), integer(BPOS,A)) :- | |
1001 | !, | |
1002 | translate_pos(POS, BPOS). | |
1003 | translate_e_p_integer(A, BAstInt) :- | |
1004 | translate_e_p(A, BAst), | |
1005 | untyped_b_ast_to_integer(BAst, BAstInt). | |
1006 | ||
1007 | untyped_b_ast_to_integer(integer(BPOS,A), integer(BPOS,A)) :- | |
1008 | !. | |
1009 | untyped_b_ast_to_integer(set_extension(_,[Single]), Single) :- | |
1010 | !. | |
1011 | % Alloy sums the elements of sets of integers so we can use SIGMA which also strips singleton sets like the MU operator. | |
1012 | % If preference alloy_strict_integers is set, we only accept singleton sets as integers and throw a WD error otherwise. | |
1013 | untyped_b_ast_to_integer(BAst, BAstInt) :- | |
1014 | get_preference(alloy_strict_integers, Strict), | |
1015 | Strict == false, | |
1016 | !, | |
1017 | get_b_position(BAst, BPOS), | |
1018 | check_position(BPOS,untyped_b_ast_to_integer), | |
1019 | BAstInt = general_sum(BPOS,[identifier(BPOS,z)],member(BPOS,identifier(BPOS,z),BAst),identifier(BPOS,z)). | |
1020 | untyped_b_ast_to_integer(BAst, BAstInt) :- | |
1021 | get_b_position(BAst, BPOS), | |
1022 | check_position(BPOS,untyped_b_ast_to_integer), | |
1023 | BAstInt = mu(BPOS,BAst). | |
1024 | ||
1025 | ||
1026 | ||
1027 | % Translation in the context where we expect a singleton set. | |
1028 | % Otherwise, a well-definedness error is thrown. | |
1029 | % This is the E_{one} translation function in the article. | |
1030 | % Note that the arguments are already translated (using \semE{.} in the article). | |
1031 | translate_e_p_one(set_extension(_,[One]), Res) :- | |
1032 | !, | |
1033 | Res = One. | |
1034 | translate_e_p_one(BAst, mu(none,BAst)). | |
1035 | ||
1036 | % construct_choose(TSetA,external_function_call(none,'CHOOSE',[TSetA],string(none,'a member of X'),TypeParams,Decl)) :- | |
1037 | % % format of raw AST of CHOOSE copied from btype2 | |
1038 | % TypeParams = rewrite_protected([identifier(none,T)]), | |
1039 | % Decl = rewrite_protected(total_function(none,pow_subset(none,identifier(none,T)),identifier(none,T))). | |
1040 | translate_if_then_else(if_then_else(ConditionPred,TruthExpr,FalsityExpr,_Type,POS), TIfThenElse) :- | |
1041 | translate_e_p(ConditionPred, TConditionPred), | |
1042 | translate_e_p(TruthExpr, TTruthExpr), | |
1043 | translate_e_p(FalsityExpr, TFalsityExpr), | |
1044 | translate_pos(POS, BPOS), | |
1045 | get_type_and_arity_from_alloy_term(TruthExpr, Type, _), | |
1046 | ( ( Type = [untyped] | |
1047 | ; is_primitive_type(Type, _) | |
1048 | ) | |
1049 | -> | |
1050 | TIfThenElse = conjunct(BPOS, | |
1051 | implication(BPOS,TConditionPred,TTruthExpr), | |
1052 | implication(BPOS,negation(BPOS,TConditionPred),TFalsityExpr)) | |
1053 | ; TIfThenElse = if_then_else(BPOS,TConditionPred,TTruthExpr,TFalsityExpr) | |
1054 | ). | |
1055 | ||
1056 | translate_quantifier_e(Quantifier, TQuantifier) :- | |
1057 | Quantifier =.. [Functor,Params,Fields,Body,_Type,POS], | |
1058 | member(Functor, [all,no,some,one,lone,comprehension]), | |
1059 | maplist(translate_e_p, Params, TParams), | |
1060 | maplist(strip_singleton_set, TParams, TTParams), % strip singleton sets for parameter definition in the untyped B ast (only identifier/2 nodes allowed in there) | |
1061 | translate_pos(POS, BPOS), | |
1062 | maplist(translate_quantifier_field, Fields, TFieldsList, SingletonSetNames), | |
1063 | ( is_disjoint_quantifier(Fields) | |
1064 | -> | |
1065 | maplist(translate_e_p, SingletonSetNames, TSets), % TSets are the translated Fields | |
1066 | % encoding all_different by union(Set1,Set2,) | |
1067 | length(Fields, Len), | |
1068 | construct_all_diff(BPOS,Len,TSets, AllDiff), | |
1069 | join_untyped_ast_nodes(conjunct, [AllDiff|TFieldsList], TFields) | |
1070 | ; join_untyped_ast_nodes(conjunct, TFieldsList, TFields) | |
1071 | ), | |
1072 | translate_e_p(Body, TBody), | |
1073 | translate_quantifier_e_aux(BPOS, Functor, TTParams, TFields, TBody, TQuantifier), | |
1074 | % TO DO: retract singleton sets introduced in quantifiers on exit | |
1075 | maplist(retract_singleton_set(BPOS), SingletonSetNames). | |
1076 | ||
1077 | construct_all_diff(BPOS,2,[S1,S2],AllDiff) :- !, AllDiff = not_equal(BPOS,S1,S2). | |
1078 | construct_all_diff(BPOS,Len,TSets, AllDiff) :- | |
1079 | AllDiff = equal(BPOS,card(BPOS,general_union(BPOS,set_extension(BPOS,TSets))),integer(BPOS,Len)). | |
1080 | % encoding all_different by card(union({O1,O2,...,Ok}) = k | |
1081 | ||
1082 | ||
1083 | translate_quantifier_e_aux(Pos, all, TParams, TFields, TBody, forall(Pos,TParams,implication(Pos,TFields,TBody))). | |
1084 | translate_quantifier_e_aux(Pos, no, TParams, TFields, TBody, negation(Pos,exists(Pos,TParams,conjunct(Pos,TFields,TBody)))). | |
1085 | translate_quantifier_e_aux(Pos, some, TParams, TFields, TBody, exists(Pos,TParams,conjunct(Pos,TFields,TBody))). | |
1086 | translate_quantifier_e_aux(Pos, one, TParams, TFields, TBody, equal(Pos,card(Pos,comprehension_set(Pos,TParams,conjunct(Pos,TFields,TBody))),integer(Pos,1))). | |
1087 | translate_quantifier_e_aux(Pos, lone, TParams, TFields, TBody, less_equal(Pos,card(Pos,comprehension_set(Pos,TParams,conjunct(Pos,TFields,TBody))),integer(Pos,1))). | |
1088 | translate_quantifier_e_aux(Pos, comprehension, TParams, TFields, TBody, comprehension_set(Pos,TParams,conjunct(Pos,TFields,TBody))). | |
1089 | ||
1090 | % Translate Constants | |
1091 | translate_cst_e_p(iden(POS), event_b_identity(BPOS)) :- % has no Span Info ! | |
1092 | translate_pos(POS, BPOS). | |
1093 | translate_cst_e_p(identifier(Cst,_,POS), BConstruct) :- | |
1094 | alloy_constant_to_b(Cst, BPOS, BConstruct), | |
1095 | !, | |
1096 | translate_pos(POS, BPOS). | |
1097 | translate_cst_e_p(none(POS), TNone) :- | |
1098 | !, | |
1099 | translate_pos(POS, BPOS), | |
1100 | TNone = empty_set(BPOS). | |
1101 | translate_cst_e_p(this, _) :- | |
1102 | print('this not yet translated'), | |
1103 | nl, | |
1104 | fail. | |
1105 | ||
1106 | ||
1107 | alloy_constant_to_b(none, BPOS, empty_set(BPOS)). | |
1108 | alloy_constant_to_b('boolean''False', BPOS, boolean_false(BPOS)). | |
1109 | alloy_constant_to_b('boolean''True', BPOS, boolean_true(BPOS)). | |
1110 | alloy_constant_to_b('boolean''Bool', BPOS, bool_set(BPOS)). | |
1111 | ||
1112 | translate_unary_e_p(cast2int(Expr,_Type,POS), Result) :- | |
1113 | !, | |
1114 | translate_pos(POS, BPOS), | |
1115 | translate_e_p_integer(Expr, TExpr), | |
1116 | Result = set_extension(BPOS, [TExpr]). | |
1117 | translate_unary_e_p(cast2sigint(Expr,_Type,_Pos), Result) :- | |
1118 | % TO DO: do we need this? how is this written in Alloy? | |
1119 | !, | |
1120 | translate_e_p(Expr, TExpr), | |
1121 | Result = TExpr. | |
1122 | translate_unary_e_p(string(StringCodes,POS), Result) :- | |
1123 | atom_codes(String, StringCodes), | |
1124 | !, | |
1125 | translate_pos(POS, BPOS), | |
1126 | Result = set_extension(BPOS,[string(BPOS,String)]). | |
1127 | translate_unary_e_p(seq(ID), Result) :- | |
1128 | translate_unary_e_p(ID, TID), | |
1129 | !, | |
1130 | Result = sequence_extension(none,TID). | |
1131 | translate_unary_e_p(Int, Result) :- | |
1132 | integer(Int), % Note: this is not really a unary operator but a constant | |
1133 | !, | |
1134 | Result = integer(none,Int). | |
1135 | translate_unary_e_p(Term, Result) :- | |
1136 | ( Term = identifier(ID,type(TypeList,_),POS), | |
1137 | TypeList = [SigName|_] | |
1138 | ; Term = ID, | |
1139 | POS = none | |
1140 | ), | |
1141 | atom(ID), | |
1142 | sig_field_id(ID, SigName), | |
1143 | !, | |
1144 | atom_concat(ID, '_', NID), | |
1145 | atom_concat(NID, SigName, NNID), | |
1146 | translate_pos(POS, BPOS), | |
1147 | (is_singleton_set_id(NNID) | |
1148 | -> Result = set_extension(BPOS,[identifier(BPOS,NNID)]) | |
1149 | ; Result = identifier(BPOS,NNID)). | |
1150 | translate_unary_e_p(identifier(ID,Type,POS), Result) :- | |
1151 | prepend_identifier_of_module_import(ID, Type, NID), | |
1152 | % singleton enumerated set like S = {_S} | |
1153 | enumerated_set(NID), | |
1154 | is_singleton_set_id(NID), | |
1155 | !, | |
1156 | translate_pos(POS, BPOS), | |
1157 | Result = set_extension(BPOS,[identifier(BPOS,NID)]). | |
1158 | translate_unary_e_p(identifier(ID,Type,POS), Result) :- | |
1159 | prepend_identifier_of_module_import(ID, Type, NID), | |
1160 | is_singleton_set_id(NID), | |
1161 | !, | |
1162 | translate_pos(POS, BPOS), | |
1163 | Result = set_extension(BPOS,[identifier(BPOS,NID)]). | |
1164 | translate_unary_e_p(ID, Result) :- | |
1165 | atom(ID), | |
1166 | prepend_module_name(ID,NID), | |
1167 | enumerated_set(NID), | |
1168 | is_singleton_set_id(NID), | |
1169 | !, | |
1170 | Result = set_extension(none,[identifier(none,NID)]). | |
1171 | translate_unary_e_p(ID, Result) :- | |
1172 | atom(ID), | |
1173 | prepend_module_name(ID,NID), | |
1174 | is_singleton_set_id(NID), | |
1175 | !, | |
1176 | Result = set_extension(none,[identifier(none,NID)]). | |
1177 | translate_unary_e_p(integer(A,POS), Result) :- | |
1178 | !, | |
1179 | translate_pos(POS, BPOS), | |
1180 | Result = set_extension(BPOS,[integer(BPOS,A)]). | |
1181 | translate_unary_e_p(identifier('Int',_Type,POS), Result) :- | |
1182 | !, | |
1183 | translate_pos(POS, BPOS), | |
1184 | integer_type(IntOrInteger), | |
1185 | Result = integer_set(BPOS,IntOrInteger). | |
1186 | translate_unary_e_p(identifier(Id,_Type,POS), Result) :- | |
1187 | alloy_natural_id(Id, 'Natural'), | |
1188 | !, | |
1189 | translate_pos(POS, BPOS), | |
1190 | natural_type(NatOrNatural), | |
1191 | Result = integer_set(BPOS,NatOrNatural). | |
1192 | translate_unary_e_p(identifier(Id,_Type,POS), Result) :- | |
1193 | alloy_natural_id(Id, 'One'), | |
1194 | !, | |
1195 | translate_pos(POS, BPOS), | |
1196 | Result = set_extension(BPOS,[integer(BPOS,1)]). | |
1197 | translate_unary_e_p(identifier(Id,_Type,POS), Result) :- | |
1198 | alloy_natural_id(Id, 'Zero'), | |
1199 | !, | |
1200 | translate_pos(POS, BPOS), | |
1201 | Result = set_extension(BPOS,[integer(BPOS,0)]). | |
1202 | translate_unary_e_p(identifier('String',_,_), Result) :- | |
1203 | !, | |
1204 | Result = string_set(none). | |
1205 | translate_unary_e_p(identifier(ID,Type,POS), Result) :- | |
1206 | !, | |
1207 | prepend_identifier_of_module_import(ID, Type, NID), | |
1208 | translate_pos(POS, BPOS), | |
1209 | Result = identifier(BPOS,NID). | |
1210 | translate_unary_e_p('Int', Result) :- | |
1211 | !, | |
1212 | integer_type(IntOrInteger), | |
1213 | Result = integer_set(none,IntOrInteger). | |
1214 | translate_unary_e_p(Id, Result) :- | |
1215 | alloy_natural_id(Id, 'Natural'), | |
1216 | !, | |
1217 | natural_type(NatOrNatural), | |
1218 | Result = integer_set(none,NatOrNatural). | |
1219 | translate_unary_e_p(Id, Result) :- | |
1220 | alloy_natural_id(Id, 'One'), | |
1221 | !, | |
1222 | Result = set_extension(none,[integer(none,1)]). | |
1223 | translate_unary_e_p(Id, Result) :- | |
1224 | alloy_natural_id(Id, 'Zero'), | |
1225 | !, | |
1226 | Result = set_extension(none,[integer(none,0)]). | |
1227 | translate_unary_e_p(boolean(true,POS), Result) :- | |
1228 | !, % was boolean_true, but Alloy has no booleans | |
1229 | translate_pos(POS, BPOS), | |
1230 | Result = truth(BPOS). | |
1231 | translate_unary_e_p(boolean(false,POS), Result) :- | |
1232 | !, % was boolean_false | |
1233 | translate_pos(POS, BPOS), | |
1234 | Result = falsity(BPOS). | |
1235 | translate_unary_e_p(Type, Result) :- | |
1236 | % lhs of sequence is integer, Alloy Types are like [seqInt,A] for set(couple(INTEGER,A)) | |
1237 | atom(Type), | |
1238 | atom_prefix(seq, Type), | |
1239 | !, | |
1240 | integer_type(IntOrInteger), | |
1241 | Result = integer_set(none,IntOrInteger). | |
1242 | translate_unary_e_p(ID, Result) :- | |
1243 | atom(ID), | |
1244 | prepend_module_name(ID,NID), | |
1245 | !, | |
1246 | Result = identifier(none,NID). | |
1247 | translate_unary_e_p(UnaryP, TUnaryP) :- | |
1248 | % and/or defines a list of ast nodes | |
1249 | UnaryP =.. [Op,ArgList|_], | |
1250 | memberchk(Op, [and,or]), % translated to conjunct/disjunct | |
1251 | is_list(ArgList), | |
1252 | !, | |
1253 | maplist(translate_e_p, ArgList, TArgList), | |
1254 | join_predicates(Op, TArgList, TUnaryP). | |
1255 | translate_unary_e_p(UnaryP, TUnaryP) :- | |
1256 | UnaryP =.. [Op,Arg,_Type,POS], | |
1257 | member(Op, [no,one,some,lone,exactlyof,oneof,someof,loneof]), | |
1258 | !, | |
1259 | translate_e_p(Arg, TArg), | |
1260 | translate_pos(POS, BPOS), | |
1261 | translate_quantified_e(BPOS, Op, TArg, TUnaryP). | |
1262 | translate_unary_e_p(card(Arg,_Type,POS), set_extension(BPOS,[card(BPOS,TArg)])) :- | |
1263 | !, | |
1264 | translate_e_p(Arg, TArg), | |
1265 | translate_pos(POS, BPOS). | |
1266 | translate_unary_e_p(UnaryP, TUnaryP) :- | |
1267 | UnaryP =.. [Op,Arg,_Type,POS], | |
1268 | translate_e_p(Arg, TArg), | |
1269 | alloy_to_b_unary_operator(Op, BOp), | |
1270 | translate_pos(POS, BPOS), | |
1271 | TUnaryP =.. [BOp,BPOS,TArg]. | |
1272 | ||
1273 | alloy_natural_id(Id, Suffix) :- | |
1274 | atom(Id), | |
1275 | module_in_scope_but_root('util''natural', Alias), | |
1276 | atom_concat(Alias, '\'', Prefix), | |
1277 | atom_concat(Prefix, Suffix, Id). | |
1278 | ||
1279 | % support for Alloy modules to maintain unique identifiers | |
1280 | prepend_module_name(ID,NID) :- | |
1281 | atom(ID), | |
1282 | \+ has_module_prefix(ID), | |
1283 | current_module_in_scope(ModuleName), | |
1284 | !, | |
1285 | atom_concat(ModuleName, '''', ModuleNameP), | |
1286 | atom_concat(ModuleNameP, ID, NID). | |
1287 | prepend_module_name(ID,ID). | |
1288 | ||
1289 | has_module_prefix(ID) :- | |
1290 | atom(ID), | |
1291 | module_in_scope_but_root(ModuleName, Alias), | |
1292 | ( atom_prefix(ModuleName , ID) | |
1293 | ; Alias \== 'alloy2b_none', | |
1294 | atom_prefix(Alias, ID) | |
1295 | ), | |
1296 | !. | |
1297 | ||
1298 | prepend_identifier_of_module_import(ID, Type, NID) :- | |
1299 | % field names of signatures from imported modules are not unique, | |
1300 | % their type (the signature name) is unique though | |
1301 | atom(ID), | |
1302 | Type = type([Inner|_],_), | |
1303 | module_in_scope_but_root(ModuleName, Alias), | |
1304 | ( Alias \== 'alloy2b_none' | |
1305 | -> Name = Alias | |
1306 | ; Name = ModuleName | |
1307 | ), | |
1308 | atom_prefix(Name, Inner), | |
1309 | atom_concat(Name, '''', NameP), | |
1310 | \+ atom_prefix(NameP, ID), | |
1311 | !, | |
1312 | atom_concat(NameP, ID, NID). | |
1313 | prepend_identifier_of_module_import(ID, _, ID). | |
1314 | ||
1315 | %% FUNCTIONS defined by func in util: | |
1316 | % alloy_to_b_natural_utility_function(Name, BPOS, Params, Result). | |
1317 | % Special cases with no direct counterpart in B. | |
1318 | alloy_to_b_natural_utility_function('''ord''first', BPOS, [], set_extension(BPOS,[integer(BPOS,0)])). | |
1319 | alloy_to_b_natural_utility_function('''ord''last', BPOS, [], set_extension(BPOS,[max_int(BPOS)])). | |
1320 | alloy_to_b_natural_utility_function('''ord''next', BPOS, [P1Int], set_extension(BPOS,[add(BPOS,P1Int,integer(BPOS,1))])). | |
1321 | alloy_to_b_natural_utility_function('''ord''prev', BPOS, [P1Int], set_extension(BPOS,[minus(BPOS,P1Int,integer(BPOS,1))])). | |
1322 | alloy_to_b_natural_utility_function('''One', BPOS, [], set_extension(BPOS,[integer(BPOS,1)])). | |
1323 | alloy_to_b_natural_utility_function('''Zero', BPOS, [], set_extension(BPOS,[integer(BPOS,0)])). | |
1324 | alloy_to_b_natural_utility_function('''min', BPOS, [], set_extension(BPOS,[max(BPOS,set_extension(BPOS,[integer(BPOS,0),min_int(BPOS)]))])). | |
1325 | alloy_to_b_natural_utility_function('''inc', BPOS, [P1Int], set_extension(BPOS,[add(BPOS,P1Int,integer(BPOS,1))])). | |
1326 | alloy_to_b_natural_utility_function('''dec', BPOS, [P1Int], set_extension(BPOS,[minus(BPOS,P1Int,integer(BPOS,1))])). | |
1327 | ||
1328 | % alloy_to_b_integer_utility_function(Name, BPOS, Params, Result). | |
1329 | % Special cases with no direct counterpart in B. | |
1330 | alloy_to_b_integer_utility_function('''signum', BPOS, [P1Int], Result) :- | |
1331 | !, | |
1332 | % IF int(p) < 0 THEN -1 ELSE IF int(p) > 0 THEN 1 ELSE 0 END END | |
1333 | Result = if_then_else(BPOS,less(BPOS,P1Int,integer(BPOS,0)),set_extension(BPOS,[integer(BPOS,-1)]),if_then_else(BPOS,greater(BPOS,P1Int,integer(BPOS,0)),set_extension(BPOS,[integer(BPOS,1)]),set_extension(BPOS,[integer(BPOS,0)]))). | |
1334 | alloy_to_b_integer_utility_function('''zero', BPOS, [P1Int], Result) :- | |
1335 | !, | |
1336 | % int(p) = 0 | |
1337 | Result = equal(BPOS,P1Int,integer(BPOS,0)). | |
1338 | alloy_to_b_integer_utility_function(Name, BPOS, [P1Int], Result) :- | |
1339 | ( Name == '''pos', BOp = greater | |
1340 | ; Name == '''neg', BOp = less), | |
1341 | !, | |
1342 | translate_e_p_integer(P1Int, P1Int), | |
1343 | % int(p) > 0 or int(p) < 0 | |
1344 | Result =.. [BOp,BPOS,P1Int,integer(BPOS,0)]. | |
1345 | alloy_to_b_integer_utility_function(Name, BPOS, [P1Int], Result) :- | |
1346 | ( Name == '''nonpos', BOp = less_equal | |
1347 | ; Name == '''nonneg', BOp = greater_equal), | |
1348 | !, | |
1349 | translate_e_p_integer(P1Int, P1Int), | |
1350 | % int(p) <= 0 or int(p) >= 0 | |
1351 | Result =.. [BOp,BPOS,P1Int,integer(BPOS,0)]. | |
1352 | alloy_to_b_integer_utility_function(Name, BPOS, [P1Int,P2Int], Result) :- | |
1353 | ( Name == '''smaller', BOp = min | |
1354 | ; Name == '''larger', BOp = max), | |
1355 | !, | |
1356 | % {min({int(p1),int(p2)})} or {max({int(p1),int(p2)})} | |
1357 | Inner =.. [BOp,BPOS,set_extension(BPOS,[P1Int,P2Int])], | |
1358 | Result = set_extension(BPOS,[Inner]). | |
1359 | alloy_to_b_integer_utility_function(Name, BPOS, [P1Int], Result) :- | |
1360 | ( Name == '''nexts', BOp = greater | |
1361 | ; Name == '''prevs', BOp = less), | |
1362 | !, | |
1363 | % {x | x : INTEGER & x > int(p)} or {x | x : INTEGER & x < int(p)} | |
1364 | Inner =.. [BOp,BPOS,identifier(BPOS,x),P1Int], | |
1365 | integer_type(IntOrInteger), | |
1366 | check_position(BPOS,alloy_to_b_integer_utility_function(Name)), | |
1367 | Result = comprehension_set(BPOS,[identifier(BPOS,x)], | |
1368 | conjunct(BPOS,member(BPOS,identifier(BPOS,x),integer_set(none,IntOrInteger)),Inner)). | |
1369 | ||
1370 | %% alloy_to_b_integer_natural_utility_func(Name,Arity,BOperator,ArgType,ReturnType). | |
1371 | % Special cases with direct counterparts in B. | |
1372 | alloy_to_b_integer_natural_utility_func(FunName, Arity, BOp, ArgType, ReturnType) :- | |
1373 | is_util_integer_natural_func(FunName, _, AlloyOp), | |
1374 | ( alloy_to_b_integer_func(AlloyOp, Arity, BOp, ArgType, ReturnType) | |
1375 | ; alloy_to_b_integer_operator_func(AlloyOp, Arity, BOp, ArgType, ReturnType) | |
1376 | ; alloy_to_b_integer_to_integer_func(AlloyOp, Arity, BOp, ArgType, ReturnType) | |
1377 | ; alloy_to_b_operator_func(AlloyOp, Arity, BOp, ArgType, ReturnType) | |
1378 | ),!. | |
1379 | ||
1380 | is_util_integer_natural_func(FunName, UtilType, AlloyOp) :- | |
1381 | atom(FunName), | |
1382 | module_in_scope_but_root(ModuleName, Alias), | |
1383 | atom(ModuleName), | |
1384 | atom(Alias), | |
1385 | ( atom_concat(ModuleName, AlloyOp, FunName) | |
1386 | ; atom_concat(Alias, AlloyOp, FunName) | |
1387 | ), | |
1388 | ( ModuleName == 'util''integer', | |
1389 | UtilType = integer | |
1390 | ; ModuleName == 'util''natural', | |
1391 | UtilType = natural | |
1392 | ). | |
1393 | ||
1394 | % functions with integer arguments and result | |
1395 | alloy_to_b_integer_to_integer_func('''minus', 2, minus, integer, integer). | |
1396 | alloy_to_b_integer_to_integer_func('''plus', 2, add, integer, integer). | |
1397 | alloy_to_b_integer_to_integer_func('''div', 2, div, integer, integer). | |
1398 | alloy_to_b_integer_to_integer_func('''mul', 2, multiplication, integer, integer). | |
1399 | alloy_to_b_integer_to_integer_func('''rem', 2, modulo, integer, integer). | |
1400 | % alloy_to_b_integer_to_integer_func('''sum',sigma, integer, integer). % TO DO: translate sum | |
1401 | alloy_to_b_integer_to_integer_func('''add', 2, add, integer, integer). | |
1402 | alloy_to_b_integer_to_integer_func('''sub', 2, minus, integer, integer). | |
1403 | % Unary: | |
1404 | alloy_to_b_integer_to_integer_func('''negate', 1, unary_minus, integer, integer). | |
1405 | alloy_to_b_integer_to_integer_func('''next', 1, successor, integer, integer). % not sure this is called; seems to call arity 0 and use join | |
1406 | alloy_to_b_integer_to_integer_func('''prev', 1, predecessor, integer, integer). | |
1407 | ||
1408 | % functions with integer result but set arguments | |
1409 | alloy_to_b_integer_func('''min', 1, min, set, integer). | |
1410 | alloy_to_b_integer_func('''max', 1, max, set, integer). | |
1411 | alloy_to_b_integer_func('''min', 0, min_int, set, integer). % TO DO: investigate bit-width related translation | |
1412 | alloy_to_b_integer_func('''max', 0, max_int, set, integer). | |
1413 | % Note: for run Arithmetic for 6 Int the Alloy Evaluator produces {-32} for min and {+31} for max | |
1414 | % predicate functions with integer arguments | |
1415 | alloy_to_b_operator_func('''next', 0, successor, set, set). | |
1416 | alloy_to_b_operator_func('''prev', 0, predecessor, set, set). | |
1417 | ||
1418 | % predicate functions with integer arguments | |
1419 | alloy_to_b_integer_operator_func('''gt', 2, greater, integer, set). | |
1420 | alloy_to_b_integer_operator_func('''lt', 2, less, integer, set). | |
1421 | alloy_to_b_integer_operator_func('''gte', 2, greater_equal, integer, set). | |
1422 | alloy_to_b_integer_operator_func('''lte', 2, less_equal, integer, set). | |
1423 | alloy_to_b_integer_operator_func('''eq', 2, equal, integer, set). | |
1424 | ||
1425 | % Not[b] = bool(b = FALSE) | |
1426 | alloy_to_b_boolean_utility_function('boolean''Not', [TP1], BPOS, Result) :- | |
1427 | !, | |
1428 | Result = convert_bool(BPOS,equal(BPOS,TP1,boolean_false(BPOS))). | |
1429 | alloy_to_b_boolean_utility_function('boolean''isTrue', [TP1], BPOS, Result) :- | |
1430 | !, | |
1431 | Result = equal(BPOS,TP1,boolean_true(BPOS)). | |
1432 | alloy_to_b_boolean_utility_function('boolean''isFalse', [TP1], BPOS, Result) :- | |
1433 | !, | |
1434 | Result = equal(BPOS,TP1,boolean_false(BPOS)). | |
1435 | % And[a,b] = bool(a = TRUE & b = TRUE) | |
1436 | alloy_to_b_boolean_utility_function('boolean''And', [TP1,TP2], BPOS, Result) :- | |
1437 | !, | |
1438 | Result = convert_bool(BPOS,conjunct(BPOS,equal(BPOS,TP1,boolean_true(BPOS)),equal(BPOS,TP2,boolean_true(BPOS)))). | |
1439 | % Or[a,b] = bool(a = TRUE or b = TRUE) | |
1440 | alloy_to_b_boolean_utility_function('boolean''Or', [TP1,TP2], BPOS, Result) :- | |
1441 | !, | |
1442 | Result = convert_bool(BPOS,disjunct(BPOS,equal(BPOS,TP1,boolean_true(BPOS)),equal(BPOS,TP2,boolean_true(BPOS)))). | |
1443 | % Nand[a,b] = bool(a = FALSE or b = FALSE) | |
1444 | alloy_to_b_boolean_utility_function('boolean''Nand', [TP1,TP2], BPOS, Result) :- | |
1445 | !, | |
1446 | Result = convert_bool(BPOS,disjunct(BPOS,equal(BPOS,TP1,boolean_false(BPOS)),equal(BPOS,TP2,boolean_false(BPOS)))). | |
1447 | % Nor[a,b] = bool(a = FALSE & b = FALSE) | |
1448 | alloy_to_b_boolean_utility_function('boolean''Nor', [TP1,TP2], BPOS, Result) :- | |
1449 | !, | |
1450 | Result = convert_bool(BPOS,conjunct(BPOS,equal(BPOS,TP1,boolean_false(BPOS)),equal(BPOS,TP2,boolean_false(BPOS)))). | |
1451 | % Xor[a,b] = bool((a = FALSE & b = TRUE) or (a = TRUE & b = FALSE)) | |
1452 | alloy_to_b_boolean_utility_function('boolean''Xor', [TP1,TP2], BPOS, Result) :- | |
1453 | !, | |
1454 | LHS = conjunct(BPOS,equal(BPOS,TP1,boolean_false(BPOS)),equal(BPOS,TP2,boolean_true(BPOS))), | |
1455 | RHS = conjunct(BPOS,equal(BPOS,TP1,boolean_true(BPOS)),equal(BPOS,TP2,boolean_false(BPOS))), | |
1456 | Result = convert_bool(BPOS,disjunct(BPOS,LHS,RHS)). | |
1457 | alloy_to_b_boolean_utility_function(FunName, _, BPOS, truth(BPOS)) :- | |
1458 | atom_prefix('boolean', FunName), | |
1459 | !, | |
1460 | add_error(alloy_to_b_boolean_utility_function, 'Function from util/boolean currently not supported', [], BPOS). | |
1461 | ||
1462 | % dom[r] = dom(r) | |
1463 | alloy_to_b_relational_utility_function(UtilFunName, [TRelation], BPOS, TCall) :- | |
1464 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1465 | atom_concat(RelationAlias, '\'dom', UtilFunName), | |
1466 | !, | |
1467 | TCall = domain(BPOS,TRelation). | |
1468 | % ran[r] = ran(r) | |
1469 | alloy_to_b_relational_utility_function(UtilFunName, [TRelation], BPOS, TCall) :- | |
1470 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1471 | atom_concat(RelationAlias, '\'ran', UtilFunName), | |
1472 | !, | |
1473 | TCall = range(BPOS,TRelation). | |
1474 | % total[r,d] = !x.(x:d => r[{x}] /= {}}) | |
1475 | alloy_to_b_relational_utility_function(UtilFunName, [TRelation,TDomain], BPOS, TCall) :- | |
1476 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1477 | atom_concat(RelationAlias, '\'total', UtilFunName), | |
1478 | !, | |
1479 | ID0 = identifier(BPOS,x), | |
1480 | LHS = member(BPOS,ID0,TDomain), | |
1481 | RHS = not_equal(BPOS,image(BPOS,TRelation,set_extension(BPOS,[ID0])),empty_set(BPOS)), | |
1482 | TCall = forall(BPOS,[ID0],implication(BPOS,LHS,RHS)). | |
1483 | % functional[r,d] = !x.(x:d => card(r[{x}]) <= 1) | |
1484 | alloy_to_b_relational_utility_function(UtilFunName, [TRelation,TDomain], BPOS, TCall) :- | |
1485 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1486 | atom_concat(RelationAlias, '\'functional', UtilFunName), | |
1487 | !, | |
1488 | ID0 = identifier(BPOS,x), | |
1489 | LHS = member(BPOS,ID0,TDomain), | |
1490 | RHS = less_equal(BPOS,card(BPOS,image(BPOS,TRelation,set_extension(BPOS,[ID0]))),integer(BPOS,1)), | |
1491 | TCall = forall(BPOS,[ID0],implication(BPOS,LHS,RHS)). | |
1492 | % function[r,d] = !x.(x:d => card(r[{x}]) = 1) | |
1493 | alloy_to_b_relational_utility_function(UtilFunName, [TRelation,TDomain], BPOS, TCall) :- | |
1494 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1495 | atom_concat(RelationAlias, '\'function', UtilFunName), | |
1496 | !, | |
1497 | ID0 = identifier(BPOS,x), | |
1498 | LHS = member(BPOS,ID0,TDomain), | |
1499 | RHS = equal(BPOS,card(BPOS,image(BPOS,TRelation,set_extension(BPOS,[ID0]))),integer(BPOS,1)), | |
1500 | TCall = forall(BPOS,[ID0],implication(BPOS,LHS,RHS)). | |
1501 | % injective[r,c] = !(y).(y:c => card(r~[{y}]) <= 1) | |
1502 | alloy_to_b_relational_utility_function(UtilFunName, [TRelation,TCodomain], BPOS, TCall) :- | |
1503 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1504 | atom_concat(RelationAlias, '\'injective', UtilFunName), | |
1505 | !, | |
1506 | ID0 = identifier(BPOS,y), | |
1507 | LHS = member(BPOS,ID0,TCodomain), | |
1508 | RHS = less_equal(BPOS,card(BPOS,image(BPOS,reverse(BPOS,TRelation),set_extension(BPOS,[ID0]))),integer(BPOS,1)), | |
1509 | TCall = forall(BPOS,[ID0],implication(BPOS,LHS,RHS)). | |
1510 | % surjective[r,c] = !(y).(y:c => r~[{y}] /= {}) | |
1511 | alloy_to_b_relational_utility_function(UtilFunName, [TRelation,TCodomain], BPOS, TCall) :- | |
1512 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1513 | atom_concat(RelationAlias, '\'surjective', UtilFunName), | |
1514 | !, | |
1515 | ID0 = identifier(BPOS,y), | |
1516 | LHS = member(BPOS,ID0,TCodomain), | |
1517 | RHS = not_equal(BPOS,image(BPOS,reverse(BPOS,TRelation),set_extension(BPOS,[ID0])),empty_set(BPOS)), | |
1518 | TCall = forall(BPOS,[ID0],implication(BPOS,LHS,RHS)). | |
1519 | % bijective[r,c] = !(y).(y:c => card(r~[{y}]) = 1) | |
1520 | alloy_to_b_relational_utility_function(UtilFunName, [TRelation,TCodomain], BPOS, TCall) :- | |
1521 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1522 | atom_concat(RelationAlias, '\'bijective', UtilFunName), | |
1523 | !, | |
1524 | ID0 = identifier(BPOS,y), | |
1525 | LHS = member(BPOS,ID0,TCodomain), | |
1526 | RHS = equal(BPOS,card(BPOS,image(BPOS,reverse(BPOS,TRelation),set_extension(BPOS,[ID0]))),integer(BPOS,1)), | |
1527 | TCall = forall(BPOS,[ID0],implication(BPOS,LHS,RHS)). | |
1528 | % bijection[r,d,c] = function[r,d] & bijective[r,c] | |
1529 | alloy_to_b_relational_utility_function(UtilFunName, [Relation,Domain,Codomain], BPOS, TCall) :- | |
1530 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1531 | atom_concat(RelationAlias, '\'bijection', UtilFunName), | |
1532 | !, | |
1533 | atom_concat(RelationAlias, '\'function', Function), | |
1534 | atom_concat(RelationAlias, '\'bijective', Bijective), | |
1535 | alloy_to_b_relational_utility_function(Function, [Relation,Domain], BPOS, IsFunction), | |
1536 | alloy_to_b_relational_utility_function(Bijective, [Relation,Codomain], BPOS, IsBijective), | |
1537 | TCall = conjunct(BPOS,IsFunction,IsBijective). | |
1538 | % reflexive[r,s] = id(s) <: r | |
1539 | alloy_to_b_relational_utility_function(UtilFunName, [TRelation1,TRelation2], BPOS, TCall) :- | |
1540 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1541 | atom_concat(RelationAlias, '\'reflexive', UtilFunName), | |
1542 | !, | |
1543 | TCall = subset(BPOS,identity(BPOS,TRelation2),TRelation1). | |
1544 | % irreflexive[r] = id(dom(r)) /\ r = {} | |
1545 | alloy_to_b_relational_utility_function(UtilFunName, [TRelation], BPOS, TCall) :- | |
1546 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1547 | atom_concat(RelationAlias, '\'irreflexive', UtilFunName), | |
1548 | !, | |
1549 | TCall = equal(BPOS,intersection(BPOS,identity(BPOS,domain(BPOS,TRelation)),TRelation),empty_set(BPOS)). | |
1550 | % symmetric[r] = r~ <: r | |
1551 | alloy_to_b_relational_utility_function(UtilFunName, [TRelation], BPOS, TCall) :- | |
1552 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1553 | atom_concat(RelationAlias, '\'symmetric', UtilFunName), | |
1554 | !, | |
1555 | TCall = subset(BPOS,reverse(BPOS,TRelation),TRelation). | |
1556 | % antisymmetric[r] = (r~ /\ r) <: id(dom(r)) | |
1557 | alloy_to_b_relational_utility_function(UtilFunName, [TRelation], BPOS, TCall) :- | |
1558 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1559 | atom_concat(RelationAlias, '\'antisymmetric', UtilFunName), | |
1560 | !, | |
1561 | Intersection = intersection(BPOS,reverse(BPOS,TRelation),TRelation), | |
1562 | TCall = subset(BPOS,Intersection,identity(BPOS,domain(BPOS,TRelation))). | |
1563 | % transitive[r] = (r ; r) <: r | |
1564 | alloy_to_b_relational_utility_function(UtilFunName, [TRelation], BPOS, TCall) :- | |
1565 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1566 | atom_concat(RelationAlias, '\'transitive', UtilFunName), | |
1567 | !, | |
1568 | TCall = subset(BPOS,composition(BPOS,TRelation,TRelation),TRelation). | |
1569 | % acyclic[r,s] = !x.(x:s => x|->x /: closure1(r)) | |
1570 | alloy_to_b_relational_utility_function(UtilFunName, [TRelation1,TRelation2], BPOS, TCall) :- | |
1571 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1572 | atom_concat(RelationAlias, '\'acyclic', UtilFunName), | |
1573 | !, | |
1574 | ID0 = identifier(BPOS,x), | |
1575 | TCall = forall(BPOS,[ID0],implication(BPOS,member(BPOS,ID0,TRelation2), | |
1576 | not_member(BPOS,couple(BPOS,ID0,ID0),closure(BPOS,TRelation1)))). | |
1577 | % complete[r,s] = !(x,y).(x:s & y:s & x /= y => x|->y : (r \/ r~)) | |
1578 | alloy_to_b_relational_utility_function(UtilFunName, [TRelation1,TRelation2], BPOS, TCall) :- | |
1579 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1580 | atom_concat(RelationAlias, '\'complete', UtilFunName), | |
1581 | !, | |
1582 | ID0 = identifier(BPOS,x), | |
1583 | ID1 = identifier(BPOS,y), | |
1584 | LHS = conjunct(BPOS,conjunct(BPOS,member(BPOS,ID0,TRelation2),member(BPOS,ID1,TRelation2)),not_equal(BPOS,ID0,ID1)), | |
1585 | RHS = member(BPOS,couple(BPOS,ID0,ID1),union(BPOS,TRelation1,reverse(BPOS,TRelation1))), | |
1586 | TCall = forall(BPOS,[ID0,ID1],implication(BPOS,LHS,RHS)). | |
1587 | % preorder[r,s] = reflexive[r,s] & transitive[r] | |
1588 | alloy_to_b_relational_utility_function(UtilFunName, [Relation1,Relation2], BPOS, TCall) :- | |
1589 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1590 | atom_concat(RelationAlias, '\'preorder', UtilFunName), | |
1591 | !, | |
1592 | atom_concat(RelationAlias, '\'reflexive', Reflexive), | |
1593 | atom_concat(RelationAlias, '\'transitive', Transitive), | |
1594 | alloy_to_b_relational_utility_function(Reflexive, [Relation1,Relation2], BPOS, IsReflexive), | |
1595 | alloy_to_b_relational_utility_function(Transitive, [Relation1], BPOS, IsTransitive), | |
1596 | TCall = conjunct(BPOS,IsReflexive,IsTransitive). | |
1597 | % equivalence[r,s] = preorder[r,s] & symmetric[r] | |
1598 | alloy_to_b_relational_utility_function(UtilFunName, [Relation1,Relation2], BPOS, TCall) :- | |
1599 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1600 | atom_concat(RelationAlias, '\'equivalence', UtilFunName), | |
1601 | !, | |
1602 | atom_concat(RelationAlias, '\'preorder', Preorder), | |
1603 | atom_concat(RelationAlias, '\'symmetric', Symmetric), | |
1604 | alloy_to_b_relational_utility_function(Preorder, [Relation1,Relation2], BPOS, IsReflexive), | |
1605 | alloy_to_b_relational_utility_function(Symmetric, [Relation1], BPOS, IsTransitive), | |
1606 | TCall = conjunct(BPOS,IsReflexive,IsTransitive). | |
1607 | % partialOrder[r,s] = preorder[r,s] & antisymmetric[r] | |
1608 | alloy_to_b_relational_utility_function(UtilFunName, [Relation1,Relation2], BPOS, TCall) :- | |
1609 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1610 | atom_concat(RelationAlias, '\'partialOrder', UtilFunName), | |
1611 | !, | |
1612 | atom_concat(RelationAlias, '\'preorder', Preorder), | |
1613 | atom_concat(RelationAlias, '\'antisymmetric', Antisymmetric), | |
1614 | alloy_to_b_relational_utility_function(Preorder, [Relation1,Relation2], BPOS, IsReflexive), | |
1615 | alloy_to_b_relational_utility_function(Antisymmetric, [Relation1], BPOS, IsTransitive), | |
1616 | TCall = conjunct(BPOS,IsReflexive,IsTransitive). | |
1617 | % totalOrder[r,s] = partialOrder[r,s] & complete[r,s] | |
1618 | alloy_to_b_relational_utility_function(UtilFunName, [Relation1,Relation2], BPOS, TCall) :- | |
1619 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1620 | atom_concat(RelationAlias, '\'totalOrder', UtilFunName), | |
1621 | !, | |
1622 | atom_concat(RelationAlias, '\'partialOrder', Partialorder), | |
1623 | atom_concat(RelationAlias, '\'complete', Complete), | |
1624 | alloy_to_b_relational_utility_function(Partialorder, [Relation1,Relation2], BPOS, IsReflexive), | |
1625 | alloy_to_b_relational_utility_function(Complete, [Relation1,Relation2], BPOS, IsTransitive), | |
1626 | TCall = conjunct(BPOS,IsReflexive,IsTransitive). | |
1627 | alloy_to_b_relational_utility_function(FunName, _, BPOS, truth(BPOS)) :- | |
1628 | module_in_scope_but_root('util\'relation', RelationAlias), | |
1629 | atom_prefix(RelationAlias, FunName), | |
1630 | !, | |
1631 | add_error(alloy_to_b_relational_utility_function, 'Function from util/relation currently not supported', [], BPOS). | |
1632 | ||
1633 | % remove_prime(Old,New) :- atom_codes(Old,OldC) , delete(OldC,39,NewC) , atom_codes(New,NewC). | |
1634 | get_clean_ordering_fun_name(OrderingFunctionName, CleanOrdFunName) :- | |
1635 | member(CleanOrdFunName, [first,last,min,max,next,nexts,prev,prevs,larger,smaller,lt,lte,gt,gte]), | |
1636 | atom_suffix(CleanOrdFunName, OrderingFunctionName). | |
1637 | ||
1638 | get_ordering_definition_name(TempFunctionName, SignatureName, FunctionName) :- | |
1639 | member(TempFunctionName, [next,prev,nexts,prevs]), | |
1640 | atom_concat(TempFunctionName, '_', TTempFunctionName), | |
1641 | atom_concat(TTempFunctionName, SignatureName, FunctionName). | |
1642 | ||
1643 | translate_binary_e_p(in(Arg1,Arg2,_Type,POS), Translation) :- | |
1644 | !, | |
1645 | translate_pos(POS, BPOS), | |
1646 | translate_e_p(Arg1, TArg1), | |
1647 | translate_binary_in(TArg1, Arg2, BPOS, Translation). | |
1648 | translate_binary_e_p(CartTerm, TCartesian) :- | |
1649 | is_cartesian_product_term(CartTerm, Arg1, Arg2, POS), | |
1650 | !, | |
1651 | translate_pos(POS, BPOS), | |
1652 | translate_cartesian(BPOS, Arg1, Arg2, TCartesian). | |
1653 | % S <: Rel --> {x1,..,xk| x1:S & (x1,...,xk):Rel} | |
1654 | translate_binary_e_p(dom_restr(Arg1,Arg2,_Type,POS), TDom) :- | |
1655 | \+ is_binary_relation(Arg2), % for binary relation translation to B domain_restriction works | |
1656 | !, | |
1657 | get_type_and_arity_from_alloy_term(Arg2, _, Arity), | |
1658 | gen_ids_and_couple(Arity, IDS, Couple), | |
1659 | translate_pos(POS, BPOS), | |
1660 | TDom = comprehension_set(BPOS,IDS,conjunct(BPOS,Mem1,Mem2)), | |
1661 | IDS = [ID1|_], | |
1662 | check_position(BPOS,translate_binary_e_p), | |
1663 | Mem1 = member(BPOS,ID1,TArg1), | |
1664 | Mem2 = member(BPOS,Couple,TArg2), | |
1665 | translate_e_p(Arg1, TArg1), | |
1666 | translate_e_p(Arg2, TArg2). | |
1667 | translate_binary_e_p(join(Arg1,Arg2,_,_), TJoin) :- | |
1668 | % special case for join with unary ordering definition call such as 'this.(prev + next)' | |
1669 | % improves performance to translate to 'this.prev + this.next' | |
1670 | annotate_undefined_ordering_definition_call(Arg2, Arg1, NArg2), | |
1671 | Arg2 \== NArg2, | |
1672 | !, | |
1673 | translate_e_p(NArg2, TJoin). | |
1674 | translate_binary_e_p(join(Arg1,Arg2,_,_), TJoin) :- | |
1675 | % see above clause | |
1676 | annotate_undefined_ordering_definition_call(Arg1, Arg2, NArg1), | |
1677 | Arg1 \== NArg1, | |
1678 | !, | |
1679 | translate_e_p(NArg1, TJoin). | |
1680 | translate_binary_e_p(join(Arg1,Arg2,_Type,POS), TJoin) :- | |
1681 | !, | |
1682 | translate_pos(POS, BPOS), | |
1683 | translate_join(BPOS, Arg1, Arg2, TJoin). | |
1684 | translate_binary_e_p(let(VarName,Expr,Sub,Type,POS), TLet) :- | |
1685 | !, | |
1686 | translate_e_p(VarName, TVarName), | |
1687 | translate_e_p(Expr, TExpr), | |
1688 | ( Type == type(['PrimitiveBoolean'],0) -> | |
1689 | NewFunctor = let_predicate | |
1690 | ; NewFunctor = let_expression | |
1691 | ), | |
1692 | translate_e_p(Sub, TSub), | |
1693 | translate_pos(POS, BPOS), | |
1694 | TLet =.. [NewFunctor,BPOS,[TVarName],equal(BPOS,TVarName,TExpr),TSub]. | |
1695 | translate_binary_e_p(Call, TCall) :- | |
1696 | Call =.. [Functor,Name,Params,Type,POS], | |
1697 | ( Functor = pred_call | |
1698 | ; Functor = fun_call | |
1699 | ), | |
1700 | !, | |
1701 | translate_pos(POS, BPOS), | |
1702 | translate_function_call(Name, Params, Type, BPOS, TCall). | |
1703 | translate_binary_e_p(Binary, TBinary) :- | |
1704 | Binary =.. [Op,Arg1,Arg2,_Type,POS], | |
1705 | alloy_to_b_integer_operator(Op, BOp), | |
1706 | !, | |
1707 | translate_e_p_integer(Arg1, TArg1), | |
1708 | translate_e_p_integer(Arg2, TArg2), | |
1709 | translate_pos(POS, BPOS), | |
1710 | TBinary =.. [BOp,BPOS,TArg1,TArg2]. | |
1711 | translate_binary_e_p(Binary, TBinary) :- | |
1712 | Binary =.. [Op,Arg1,Arg2,_Type,POS], | |
1713 | \+ add_error_for_univ_or_iden(translate_binary_e_p, Op, Arg1, Arg2), | |
1714 | alloy_to_b_binary_operator(Op, BOp), | |
1715 | translate_e_p(Arg1, TArg1), | |
1716 | translate_e_p(Arg2, TArg2), | |
1717 | translate_pos(POS, BPOS), | |
1718 | TBinary =.. [BOp,BPOS,TArg1,TArg2]. | |
1719 | ||
1720 | % We do not allow keywords univ or iden for specific operators which we cannot translate to | |
1721 | % an equivalent B expression without introducing the universal type. | |
1722 | binary_op_not_allowing_iden_or_univ(equal). | |
1723 | binary_op_not_allowing_iden_or_univ(not_equal). | |
1724 | binary_op_not_allowing_iden_or_univ(in). | |
1725 | binary_op_not_allowing_iden_or_univ(not_in). | |
1726 | ||
1727 | add_error_for_univ_or_iden(Src, Op, Arg1, Arg2) :- | |
1728 | binary_op_not_allowing_iden_or_univ(Op), | |
1729 | ( ground(Arg1), | |
1730 | is_iden(Arg1) | |
1731 | ; ground(Arg2), | |
1732 | is_iden(Arg2) | |
1733 | ), | |
1734 | atom_concat('iden keyword in Alloy operator ', Op, Msg), | |
1735 | add_error_not_supported(Src, Msg). | |
1736 | add_error_for_univ_or_iden(Src, Op, Arg1, Arg2) :- | |
1737 | binary_op_not_allowing_iden_or_univ(Op), | |
1738 | ( ground(Arg1), | |
1739 | is_univ(Arg1) | |
1740 | ; ground(Arg2), | |
1741 | is_univ(Arg2) | |
1742 | ), | |
1743 | atom_concat('univ keyword in Alloy operator ', Op, Msg), | |
1744 | add_error_not_supported(Src, Msg). | |
1745 | ||
1746 | add_error_not_supported(Src, NotSupported) :- | |
1747 | atom(NotSupported), | |
1748 | add_error(Src, 'Expression not supported by our translation: ', [NotSupported]). | |
1749 | ||
1750 | translate_binary_in(TArg1, Arg2, _, _) :- | |
1751 | add_error_for_univ_or_iden(translate_binary_in, in, TArg1, Arg2), | |
1752 | !. | |
1753 | % translate TArg1 in Arg2 (multplicities are not allowed for not in) | |
1754 | translate_binary_in(TArg1, Mult, BPOS, Translation) :- | |
1755 | compound(Mult), | |
1756 | functor(Mult, AlloyFunctor, 4), % binary operator with type and pos info | |
1757 | alloy_to_b_special_multiplicity_type(AlloyFunctor, _, _, _, _, _, _, _), % TO DO: clean up | |
1758 | arg(1, Mult, MDom), | |
1759 | arg(2, Mult, MRan), | |
1760 | translate_e_p(MDom, TMDom), | |
1761 | translate_e_p(MRan, TMRan), | |
1762 | alloy_to_b_special_multiplicity_type(AlloyFunctor, BPOS, TArg1, TMDom, TMRan, TMult, AdditionalConstraint, UseInverse), | |
1763 | !, | |
1764 | ( UseInverse == yes | |
1765 | -> TTArg1 = reverse(BPOS,TArg1) | |
1766 | ; TTArg1 = TArg1 | |
1767 | ), | |
1768 | ( AdditionalConstraint == none | |
1769 | -> Translation = member(BPOS,TTArg1,TMult) % use element of rather than subset for special type operators | |
1770 | ; Translation = conjunct(BPOS,member(BPOS,TTArg1,TMult),AdditionalConstraint) | |
1771 | ). | |
1772 | % cartesian | |
1773 | translate_binary_in(TArg1, Arg2, BPOS, subset(BPOS,TArg1,TArg2)) :- | |
1774 | translate_e_p(Arg2, TArg2). | |
1775 | ||
1776 | % translate expressions of the form Arg1 MULT -> MULT Arg2 with MULT : {some,lone, one, ''} | |
1777 | % The result is to be used in member(Pos,LHS,TBinary) not for subset checks ! | |
1778 | translate_special_field_multiplicity_binary_e_p(Binary, TBinary) :- | |
1779 | Binary =.. [AlloyFunctor,Arg1,Arg2,_Type,POS], | |
1780 | translate_pos(POS, BPOS), | |
1781 | translate_e_p(Arg1, TArg1), | |
1782 | translate_e_p(Arg2, TArg2), | |
1783 | % some functions of alloy_to_b_special_multiplicity_type/8 do not apply to | |
1784 | % field multiplicities as only one multiplicity is allowed there | |
1785 | alloy_to_b_special_multiplicity_type(AlloyFunctor, BPOS, _, TArg1, TArg2, TBinary, _, _WasNo). | |
1786 | ||
1787 | % Alloy: r in T -> some U | |
1788 | % B: r : T <-> U & dom(r)=T | |
1789 | alloy_to_b_special_multiplicity_type(total_relation, BPOS, TRel, TMDom, TMRan, relations(BPOS,TMDom,TMRan), AdditionalConstraint, no) :- | |
1790 | AdditionalConstraint = equal(BPOS,domain(BPOS,TRel),TMDom). | |
1791 | % Alloy: r in T -> one U | |
1792 | % B: r : T --> U | |
1793 | alloy_to_b_special_multiplicity_type(total_function, BPOS, _, TMDom, TMRan, total_function(BPOS,TMDom,TMRan), none, no). | |
1794 | % Alloy: r in T -> lone U | |
1795 | % B: r : T +-> U | |
1796 | alloy_to_b_special_multiplicity_type(partial_function, BPOS, _, TMDom, TMRan, partial_function(BPOS,TMDom,TMRan), none, no). | |
1797 | % Alloy: r in T some -> U | |
1798 | % B: r : T <-> U & ran(r) = U | |
1799 | alloy_to_b_special_multiplicity_type(surjection_relation, BPOS, TRel, TMDom, TMRan, relations(BPOS,TMDom,TMRan), AdditionalConstraint, no) :- | |
1800 | AdditionalConstraint = equal(BPOS,range(BPOS,TRel),TMRan). | |
1801 | % Alloy: r in T some -> some U | |
1802 | % B: r : T <-> U & dom(r)=T & ran(r) = U | |
1803 | alloy_to_b_special_multiplicity_type(total_surjection_relation, BPOS, TRel, TMDom, TMRan, relations(BPOS,TMDom,TMRan), AdditionalConstraint, no) :- | |
1804 | AdditionalConstraint = conjunct(BPOS,equal(BPOS,domain(BPOS,TRel),TMDom),equal(BPOS,range(BPOS,TRel),TMRan)). | |
1805 | % Alloy: r in T some -> lone U | |
1806 | % B: r : T +->> U | |
1807 | alloy_to_b_special_multiplicity_type(partial_surjection, BPOS, _, TMDom, TMRan, partial_surjection(BPOS,TMDom,TMRan), none, no). | |
1808 | % Alloy: r in T some -> one U | |
1809 | % B: r : T -->> U | |
1810 | alloy_to_b_special_multiplicity_type(total_surjection, BPOS, _, TMDom, TMRan, total_surjection(BPOS,TMDom,TMRan), none, no). | |
1811 | % Alloy: r in T lone -> U | |
1812 | % B: r~ : U +-> T | |
1813 | alloy_to_b_special_multiplicity_type(injection_relation, BPOS, _, TMDom, TMRan, partial_function(BPOS,TMRan,TMDom), none, yes). | |
1814 | % Alloy: r in T lone -> some U | |
1815 | % B: r~ : U +->> T | |
1816 | alloy_to_b_special_multiplicity_type(injection_surjection_relation, BPOS, _, TMDom, TMRan, partial_surjection(BPOS,TMRan,TMDom), none, yes). | |
1817 | % Alloy: r in T lone -> lone U | |
1818 | % B: r : T >+> U | |
1819 | alloy_to_b_special_multiplicity_type(partial_injection, BPOS, _, TMDom, TMRan, partial_injection(BPOS,TMDom,TMRan), none, no). | |
1820 | % Alloy: r in T lone -> one U | |
1821 | % B: r : T >-> U | |
1822 | alloy_to_b_special_multiplicity_type(total_injection, BPOS, _, TMDom, TMRan, total_injection(BPOS,TMDom,TMRan), none, no). | |
1823 | % Alloy: r in T one -> U | |
1824 | % B: r~ : U --> T | |
1825 | alloy_to_b_special_multiplicity_type(bijection_relation, BPOS, _, TMDom, TMRan, total_function(BPOS,TMRan,TMDom), none, yes). | |
1826 | % Alloy: r in T one -> some U | |
1827 | % B: r~ : U -->> T | |
1828 | alloy_to_b_special_multiplicity_type(total_bijection_relation, BPOS, _, TMDom, TMRan, total_surjection(BPOS,TMRan,TMDom), none, yes). | |
1829 | % Alloy: r in T one -> lone U | |
1830 | % B: r : T >+>> U | |
1831 | alloy_to_b_special_multiplicity_type(partial_bijection, BPOS, _, TMDom, TMRan, partial_bijection(BPOS,TMDom,TMRan), none, no). | |
1832 | % Alloy: r in T one -> one U | |
1833 | % B: r : T >->> U | |
1834 | alloy_to_b_special_multiplicity_type(total_bijection, BPOS, _, TMDom, TMRan, total_bijection(BPOS,TMDom,TMRan), none, no). | |
1835 | ||
1836 | % Note: Sequences in B are indexed from 1 to n, in Alloy from 0 to n-1. | |
1837 | % We do not use B sequences when translating from Alloy but define a partial function with domain 0..n-1 | |
1838 | % (we have to manually implement most functions anyway) | |
1839 | % Documentation available at http://alloytools.org/quickguide/seq.html | |
1840 | %% Sequence function calls without a parameter but the sequence itself. | |
1841 | % first: seq[{0}] | |
1842 | sequence_function_to_b('seq\'first', BPOS, [Seq], image(BPOS,Seq,set_extension(BPOS,[integer(BPOS,0)]))). | |
1843 | % last: seq[{card(seq)-1}] | |
1844 | sequence_function_to_b('seq\'last', BPOS, [Seq], image(BPOS,Seq,set_extension(BPOS,[minus(BPOS,card(BPOS,Seq),integer(BPOS,1))]))). | |
1845 | % rest: IF seq = <> THEN <> ELSE %x.(x:0..card(seq)-2 | seq(x+1) END | |
1846 | sequence_function_to_b('seq\'rest', BPOS, [Seq], if_then_else(BPOS,equal(BPOS,Seq,empty_sequence(BPOS)),empty_sequence(BPOS),lambda(BPOS,[identifier(BPOS,x)],member(BPOS,identifier(BPOS,x),interval(BPOS,integer(BPOS,0),minus(BPOS,card(BPOS,Seq),integer(BPOS,2)))),function(BPOS,Seq,add(BPOS,identifier(BPOS,x),integer(BPOS,1)))))). | |
1847 | % elems: ran(seq) | |
1848 | sequence_function_to_b('seq\'elems', BPOS, [Seq], range(BPOS,Seq)). | |
1849 | % butlast: seq[{card(seq)-2}] | |
1850 | sequence_function_to_b('seq\'butlast', BPOS, [Seq], image(BPOS,Seq,set_extension(BPOS,[minus(BPOS,card(BPOS,Seq),integer(BPOS,2))]))). | |
1851 | % isEmpty: seq = <> | |
1852 | sequence_function_to_b('seq\'isEmpty', BPOS, [Seq], equal(BPOS,Seq,empty_sequence(BPOS))). | |
1853 | % hasDups: card(seq) /= card(ran(seq)) | |
1854 | sequence_function_to_b('seq\'hasDups', BPOS, [Seq], not_equal(BPOS,card(BPOS,Seq),card(BPOS,range(BPOS,Seq)))). | |
1855 | % inds: 0..card(seq)-1 | |
1856 | sequence_function_to_b('seq\'inds', BPOS, [Seq], interval(BPOS,integer(BPOS,0),minus(BPOS,card(BPOS,Seq),integer(BPOS,1)))). | |
1857 | % lastIdx: {card(seq)-1} /\ 0..(card(seq)-1) | |
1858 | sequence_function_to_b('seq\'lastIdx', BPOS, [Seq], intersection(BPOS,set_extension(BPOS,[minus(BPOS,card(BPOS,Seq),integer(BPOS,1))]),interval(BPOS,integer(BPOS,0),minus(BPOS,card(BPOS,Seq),integer(BPOS,1))))). | |
1859 | % afterLastIdx: {card(seq)} /\ 0..(MAXSEQ-1) | |
1860 | sequence_function_to_b('seq\'afterLastIdx', BPOS, [Seq], intersection(BPOS,set_extension(BPOS,[card(BPOS,Seq)]),interval(BPOS,integer(BPOS,0),minus(BPOS,integer(BPOS,MaxSeq),integer(BPOS,1))))) :- | |
1861 | maxseq(MaxSeq). | |
1862 | %% Sequence function calls with one parameter and the sequence itself. | |
1863 | % idxOf[X]: IF seq~[{E_{one}(X)}] /= {} THEN {min(seq~[{E_{one}(X)}])} ELSE {} END | |
1864 | sequence_function_to_b('seq\'idxOf', BPOS, [Seq,P1], if_then_else(BPOS,not_equal(BPOS,image(BPOS,reverse(BPOS,Seq),set_extension(BPOS,[P1One])),empty_set(BPOS)),set_extension(BPOS,[min(BPOS,image(BPOS,reverse(BPOS,Seq),set_extension(BPOS,[P1One])))]),empty_set(BPOS))) :- | |
1865 | translate_e_p_one(P1, P1One). | |
1866 | % lastIdxOf[X]: IF seq~[{E_{one}(X)}] /= {} THEN {max(seq~[{E_{one}(X)}])} ELSE {} END | |
1867 | sequence_function_to_b('seq\'lastIdxOf', BPOS, [Seq,P1], if_then_else(BPOS,not_equal(BPOS,image(BPOS,reverse(BPOS,Seq),set_extension(BPOS,[P1One])),empty_set(BPOS)),set_extension(BPOS,[max(BPOS,image(BPOS,reverse(BPOS,Seq),set_extension(BPOS,[P1One])))]),empty_set(BPOS))) :- | |
1868 | translate_e_p_one(P1, P1One). | |
1869 | % indsOf[X]: seq~[{E_{one}(X)}] | |
1870 | sequence_function_to_b('seq\'indsOf', BPOS, [Seq,P1], image(BPOS,reverse(BPOS,Seq),set_extension(BPOS,[P1One]))) :- | |
1871 | translate_e_p_one(P1, P1One). | |
1872 | % append[seq2]: (0..(MAXSEQ-1)) <| (seq \/ %x.(x:card(seq)..(card(seq)+card(seq2)-1) | seq2(x-card(seq)))) | |
1873 | sequence_function_to_b('seq\'append', BPOS, [Seq,P1], domain_restriction(BPOS,interval(BPOS,integer(BPOS,0),minus(BPOS,integer(BPOS,MaxSeq),integer(BPOS,1))),union(BPOS,Seq,lambda(BPOS,[identifier(BPOS,x)],member(BPOS,identifier(BPOS,x),interval(BPOS,card(BPOS,Seq),minus(BPOS,add(BPOS,card(BPOS,Seq),card(BPOS,P1)),integer(BPOS,1)))),function(BPOS,P1,minus(BPOS,identifier(BPOS,x),card(BPOS,Seq))))))) :- | |
1874 | maxseq(MaxSeq). | |
1875 | % Throw WD error if X is not singleton. | |
1876 | % add[X]: IF card(seq) < MAXSEQ THEN seq \/ {card(seq) |-> E_{one}(X)} ELSE seq END | |
1877 | sequence_function_to_b('seq\'add', BPOS, [Seq,P1], if_then_else(BPOS,less(BPOS,card(BPOS,Seq),integer(BPOS,MaxSeq)),union(BPOS,Seq,set_extension(BPOS,[couple(BPOS,card(BPOS,Seq),P1One)])),Seq)) :- | |
1878 | maxseq(MaxSeq), | |
1879 | translate_e_p_one(P1, P1One). | |
1880 | % delete[i]: IF (i >= 0) THEN (0..(i-1) <| seq) \/ %x.(x:i..(card(seq)-2) & (x+1):dom(seq) | seq(x+1)) ELSE seq END | |
1881 | sequence_function_to_b('seq\'delete', BPOS, [Seq,P1], if_then_else(BPOS,greater_equal(BPOS,P1Int,integer(BPOS,0)),union(BPOS,domain_restriction(BPOS,interval(BPOS,integer(BPOS,0),minus(BPOS,P1Int,integer(BPOS,1))),Seq),lambda(BPOS,[identifier(BPOS,x)],conjunct(BPOS,member(BPOS,identifier(BPOS,x),interval(BPOS,P1Int,minus(BPOS,card(BPOS,Seq),integer(BPOS,2)))),member(BPOS,add(BPOS,identifier(BPOS,x),integer(BPOS,1)),domain(BPOS,Seq))),function(BPOS,Seq,add(BPOS,identifier(BPOS,x),integer(BPOS,1))))),Seq)) :- | |
1882 | untyped_b_ast_to_integer(P1, P1Int). | |
1883 | % Throw WD error if X is not singleton. | |
1884 | % setAt[i,X]: IF (i >= 0 & i <= card(seq) ) THEN seq <+ {i |-> E_{one}(X)} ELSE seq END | |
1885 | sequence_function_to_b('seq\'setAt', BPOS, [Seq,P1,P2], if_then_else(BPOS,conjunct(BPOS,greater_equal(BPOS,P1Int,integer(BPOS,0)),less_equal(BPOS,P1Int,card(BPOS,Seq))),overwrite(Seq,set_extension(BPOS,[couple(BPOS,P1Int,P2One)])),Seq)) :- | |
1886 | untyped_b_ast_to_integer(P1, P1Int), | |
1887 | translate_e_p_one(P2, P2One). | |
1888 | % Throw WD error if X is not singleton. | |
1889 | % special case for i=0 | |
1890 | % insert[0,X]: 0..(m-1) <| {0 |-> E_{one}(X)} \/ %z.(z:1..card(seq) ∧ (z-1):dom(seq) | seq(z-1))) | |
1891 | sequence_function_to_b('seq\'insert', BPOS, [Seq,P1,P2], domain_restriction(BPOS,interval(BPOS,integer(BPOS,0),minus(BPOS,integer(BPOS,MaxSeq),integer(BPOS,1))),union(BPOS,set_extension(BPOS,[couple(BPOS,integer(BPOS,0),P2One)]),lambda(BPOS,[identifier(BPOS,z)],member(BPOS,identifier(BPOS,z),interval(BPOS,integer(BPOS,1),card(BPOS,Seq))),function(BPOS,Seq,minus(BPOS,identifier(BPOS,z),integer(BPOS,1))))))) :- | |
1892 | untyped_b_ast_to_integer(P1, P1Int), | |
1893 | P1Int = integer(_,0), | |
1894 | maxseq(MaxSeq), | |
1895 | translate_e_p_one(P2, P2One). | |
1896 | % insert[i,X]: 0..(m-1) <| ((0..(i-1) <| seq) \/ {i |-> E_{one}(X)} \/ %z.(z:(i+1)..card(seq) ∧ (z-1):dom(seq) | seq(z-1))) | |
1897 | sequence_function_to_b('seq\'insert', BPOS, [Seq,P1,P2], domain_restriction(BPOS,interval(BPOS,integer(BPOS,0),minus(BPOS,integer(BPOS,MaxSeq),integer(BPOS,1))),union(BPOS,union(BPOS,domain_restriction(BPOS,interval(BPOS,integer(BPOS,0),minus(BPOS,P1Int,integer(BPOS,1))),Seq),set_extension(BPOS,[couple(BPOS,P1Int,P2One)])),lambda(BPOS,[identifier(BPOS,z)],member(BPOS,identifier(BPOS,z),interval(BPOS,add(BPOS,P1Int,integer(BPOS,1)),card(BPOS,Seq))),function(BPOS,Seq,minus(BPOS,identifier(BPOS,z),integer(BPOS,1))))))) :- | |
1898 | P1 \= integer(_,0), | |
1899 | maxseq(MaxSeq), | |
1900 | untyped_b_ast_to_integer(P1, P1Int), | |
1901 | translate_e_p_one(P2, P2One). | |
1902 | % subseq[from,to]: IF 0 <= from & from <= to & to < card(seq) THEN %z.(z:0..(to-from) & (z+from):dom(seq) | seq(z+from)) ELSE {} END | |
1903 | sequence_function_to_b('seq\'subseq', BPOS, [Seq,P1,P2], if_then_else(BPOS,conjunct(BPOS,conjunct(BPOS,less_equal(BPOS,integer(BPOS,0),P1Int),less_equal(BPOS,P1Int,P2Int)),less(BPOS,P2Int,card(BPOS,Seq))),lambda(BPOS,[identifier(BPOS,z)],member(BPOS,identifier(BPOS,z),interval(BPOS,integer(BPOS,0),minus(BPOS,P2Int,P1Int))),function(BPOS,Seq,add(BPOS,identifier(BPOS,z),P1Int))),empty_set(BPOS))) :- | |
1904 | untyped_b_ast_to_integer(P1, P1Int), | |
1905 | untyped_b_ast_to_integer(P2, P2Int). | |
1906 | sequence_function_to_b(FunName, BPOS, _, Res) :- | |
1907 | atom_prefix('seq', FunName), | |
1908 | !, | |
1909 | add_error(sequence_function_to_b, 'Function from util/sequences currently not supported', [], BPOS), | |
1910 | Res = truth(BPOS). | |
1911 | ||
1912 | translate_seq_function_param(join(identifier(this,type([Type],1),IPos),Arg2,JType,JPos), TParam) :- | |
1913 | !, | |
1914 | % remove join with this for signature fields | |
1915 | translate_e_p(join(identifier(Type,type([Type],1),IPos),Arg2,JType,JPos), TTParam), | |
1916 | TParam = TTParam. | |
1917 | translate_seq_function_param(Param, TParam) :- | |
1918 | translate_e_p(Param, TParam). | |
1919 | ||
1920 | % Translation of operations from utility libraries | |
1921 | % util/sequence | |
1922 | translate_function_call(Function, Params, _Type, BPOS, TCall) :- | |
1923 | maplist(translate_seq_function_param, Params, TParams), | |
1924 | sequence_function_to_b(Function, BPOS, TParams, TCall), | |
1925 | !. | |
1926 | % util/boolean | |
1927 | translate_function_call(Name, Params, _Type, BPOS, Result) :- | |
1928 | maplist(translate_e_p, Params, TParams), | |
1929 | alloy_to_b_boolean_utility_function(Name, TParams, BPOS, Result). | |
1930 | % util/relation | |
1931 | translate_function_call(Name, Params, _Type, BPOS, Result) :- | |
1932 | maplist(translate_e_p, Params, TParams), | |
1933 | check_position(BPOS,alloy_to_b_relational_utility_function), | |
1934 | alloy_to_b_relational_utility_function(Name, TParams, BPOS, Result). | |
1935 | % util/integer with no direct counterparts in B | |
1936 | translate_function_call(Name, Params, _Type, BPOS, Result) :- | |
1937 | is_util_integer_natural_func(Name, UtilType, AlloyOp), | |
1938 | UtilType == integer, | |
1939 | maplist(translate_e_p_integer, Params, TParams), | |
1940 | alloy_to_b_integer_utility_function(AlloyOp, BPOS, TParams, Result). | |
1941 | % util/natural with no direct counterparts in B | |
1942 | translate_function_call(Name, Params, _Type, BPOS, Result) :- | |
1943 | is_util_integer_natural_func(Name, UtilType, AlloyOp), | |
1944 | UtilType == natural, | |
1945 | maplist(translate_e_p_integer, Params, TParams), | |
1946 | alloy_to_b_natural_utility_function(AlloyOp, BPOS, TParams, Result), | |
1947 | !. | |
1948 | % util/integer and util/natural with direct counterparts in B | |
1949 | translate_function_call(Name, Params, _Type, BPOS, Result) :- | |
1950 | length(Params, Arity), | |
1951 | alloy_to_b_integer_natural_utility_func(Name, Arity, BOp, ArgType, ReturnType), | |
1952 | !, % functions like plus, minus, mul, ... with integer arguments and result | |
1953 | ( ArgType = integer -> | |
1954 | maplist(translate_e_p_integer, Params, TParams) | |
1955 | ; maplist(translate_e_p, Params, TParams) | |
1956 | ), | |
1957 | ( ReturnType = integer -> | |
1958 | Result = set_extension(BPOS,[TCall]) % we add set_extension wrapper | |
1959 | ; Result = TCall | |
1960 | ), | |
1961 | TCall =.. [BOp,BPOS|TParams]. | |
1962 | % util/ordering | |
1963 | translate_function_call(FunName, Params, type([SignatureName|_],_), BPOS, TCall) :- | |
1964 | translate_e_p(SignatureName, TSignatureName), | |
1965 | get_clean_ordering_fun_name(FunName, CleanOrdFunName), | |
1966 | translate_ordering_function(CleanOrdFunName, Params, SignatureName, TSignatureName, BPOS, TCall). | |
1967 | % definition call for calls to user defined Alloy predicates and functions | |
1968 | translate_function_call(Name, Params, _, BPOS, TCall) :- | |
1969 | % predicate and function call | |
1970 | maplist(translate_e_p, Params, TParams), | |
1971 | ( (definition_without_param(Name), TParams = [TParam]) | |
1972 | -> TCall = image(BPOS,definition(BPOS,Name,[]),TParam) | |
1973 | ; maplist(strip_singleton_set_from_this, TParams, TTParams), % identifier 'this' is added for fields which is already wrapped in a set extension | |
1974 | TCall = definition(BPOS,Name,TTParams) | |
1975 | ). | |
1976 | ||
1977 | % first: IF S_ord /= {} THEN {min(S_ord)} ELSE {} | |
1978 | translate_ordering_function(first, [], _, TSignatureName, BPOS, if_then_else(BPOS,not_equal(BPOS,TSignatureName,empty_set(BPOS)),set_extension(BPOS,[min(BPOS,TSignatureName)]),empty_set(BPOS))) :- | |
1979 | !. | |
1980 | % last: IF S_ord /= {} THEN {max(S_ord)} ELSE {} | |
1981 | translate_ordering_function(last, [], _, TSignatureName, BPOS, if_then_else(BPOS,not_equal(BPOS,TSignatureName,empty_set(BPOS)),set_extension(BPOS,[max(BPOS,TSignatureName)]),empty_set(BPOS))) :- | |
1982 | !. | |
1983 | % min[p]: IF p /= {} THEN {min(p)} ELSE {} | |
1984 | % max[p]: IF p /= {} THEN {min(p)} ELSE {} | |
1985 | translate_ordering_function(FunName, [Param], _, _, BPOS, TCall) :- | |
1986 | ( FunName == min | |
1987 | ; FunName == max | |
1988 | ), | |
1989 | !, | |
1990 | translate_e_p(Param, TParam), | |
1991 | InnerCall =.. [FunName,BPOS,TParam], | |
1992 | TCall = if_then_else(BPOS,not_equal(BPOS,TParam,empty_set(BPOS)),set_extension(BPOS,[InnerCall]),empty_set(BPOS)). | |
1993 | translate_ordering_function(FunName, [TParam], SignatureName, _, BPOS, TCall) :- | |
1994 | ( FunName == next | |
1995 | ; FunName == nexts | |
1996 | ; FunName == prev | |
1997 | ; FunName == prevs | |
1998 | ), | |
1999 | !, | |
2000 | get_ordering_definition_name(FunName, SignatureName, DefName), | |
2001 | translate_e_p(TParam, TTParam), | |
2002 | strip_singleton_set_from_this(TTParam, Param), | |
2003 | % The definition is something like this: next_House(s) == IF succ()[s] <: House THEN succ()[s] ELSE {} END | |
2004 | TCall = definition(BPOS,DefName,[Param]). | |
2005 | % larger[p1,p1]: IF p1 \/ p2 /= {} THEN {max(p1 \/ p2)} ELSE {} END | |
2006 | % smaller[p1,p2]: IF p1 \/ p2 /= {} THEN {min(p1 \/ p2)} ELSE {} END | |
2007 | translate_ordering_function(FunName, [P1,P2], _, _, BPOS, TCall) :- | |
2008 | ( FunName == larger | |
2009 | ; FunName == smaller | |
2010 | ), | |
2011 | !, | |
2012 | get_partial_b_functor_for_ordering(FunName, BFunctor), | |
2013 | translate_e_p(P1, TP1), | |
2014 | translate_e_p(P2, TP2), | |
2015 | ParamUnion = union(BPOS,TP1,TP2), | |
2016 | TInnerCall =.. [BFunctor,BPOS,ParamUnion], | |
2017 | InnerCall = set_extension(BPOS,[TInnerCall]), | |
2018 | TCall = if_then_else(BPOS,not_equal(BPOS,ParamUnion,empty_set(BPOS)),InnerCall,empty_set(BPOS)). | |
2019 | % lt[p1,p2]: (p1 = {}) or (p1 /= {} & p2 /= {} & p1 /= p2 & {min(p1 \/ p2)} = p1)) | |
2020 | % gt[p1,p2]: (p1 = {}) or (p1 /= {} & p2 /= {} & p1 /= p2 & {max(p1 \/ p2)} = p1)) | |
2021 | translate_ordering_function(FunName, [P1,P2], _, _, BPOS, TCall) :- | |
2022 | ( FunName == lt | |
2023 | ; FunName == gt | |
2024 | ), | |
2025 | !, | |
2026 | get_partial_b_functor_for_ordering(FunName, BFunctor), | |
2027 | translate_e_p(P1, TP1), | |
2028 | translate_e_p(P2, TP2), | |
2029 | TInnerCall =.. [BFunctor,BPOS,union(BPOS,TP1,TP2)], | |
2030 | InnerCall = set_extension(BPOS,[TInnerCall]), | |
2031 | LHS = equal(BPOS,TP1,empty_set(BPOS)), | |
2032 | RHS = conjunct(BPOS,conjunct(BPOS,conjunct(BPOS,not_equal(BPOS,TP1,empty_set(BPOS)), | |
2033 | not_equal(BPOS,TP2,empty_set(BPOS))), | |
2034 | not_equal(BPOS,TP1,TP2)), | |
2035 | equal(BPOS,InnerCall,TP1)), | |
2036 | TCall = disjunct(BPOS,LHS,RHS). | |
2037 | % lte[p1,p2]: (p1 = {}) or (p1 /= {} & p2 /= {} & {min(p1 \/ p2)} = p1) | |
2038 | % gte[p1,p2]: (p1 = {}) or (p1 /= {} & p2 /= {} & {max(p1 \/ p2)} = p1) | |
2039 | translate_ordering_function(FunName, [P1,P2], _, _, BPOS, TCall) :- | |
2040 | ( FunName == lte | |
2041 | ; FunName == gte | |
2042 | ), | |
2043 | !, | |
2044 | get_partial_b_functor_for_ordering(FunName, BFunctor), | |
2045 | translate_e_p(P1, TP1), | |
2046 | translate_e_p(P2, TP2), | |
2047 | TInnerCall =.. [BFunctor,BPOS,union(BPOS,TP1,TP2)], | |
2048 | InnerCall = set_extension(BPOS,[TInnerCall]), | |
2049 | LHS = equal(BPOS,TP1,empty_set(BPOS)), | |
2050 | RHS = conjunct(BPOS,conjunct(BPOS,not_equal(BPOS,TP1,empty_set(BPOS)), | |
2051 | not_equal(BPOS,TP2,empty_set(BPOS))), | |
2052 | equal(BPOS,InnerCall,TP1)), | |
2053 | TCall = disjunct(BPOS,LHS,RHS). | |
2054 | translate_ordering_function(FunName, [], SignatureName, _, BPOS, Res) :- | |
2055 | % ordering without argument used in a unary operator, e.g., 'some t : Trace | all s : t.^next | s.state = Off' | |
2056 | % as used in reset_flip_flop_with_enable.als | |
2057 | % for binary operators, we can inline joins as is done in annotate_undefined_ordering_definition_call/3 | |
2058 | ( FunName == next | |
2059 | ; FunName == nexts | |
2060 | ; FunName == prev | |
2061 | ; FunName == prevs | |
2062 | ), | |
2063 | !, | |
2064 | get_ordering_definition_name(FunName, SignatureName, DefName), | |
2065 | % e.g., generate %x.(x:House|MU(next_House({x}))) | |
2066 | TCall = mu(BPOS,definition(BPOS,DefName,[TParam])), | |
2067 | TId = identifier(BPOS,'_o_0'), | |
2068 | TParam = set_extension(BPOS,[TId]), | |
2069 | Member = member(BPOS,TId,identifier(BPOS,SignatureName)), | |
2070 | Res = lambda(BPOS,[TId],Member,TCall). | |
2071 | translate_ordering_function(FunName, Params, SignatureName, _Type, BPOS, empty_set(BPOS)) :- | |
2072 | length(Params,Arity), | |
2073 | ajoin(['Cannot translate ordering function for ',SignatureName,':'],Msg), | |
2074 | add_error(translate_ordering_function, Msg, FunName/Arity, BPOS). | |
2075 | ||
2076 | get_partial_b_functor_for_ordering(larger, max). | |
2077 | get_partial_b_functor_for_ordering(smaller, min). | |
2078 | get_partial_b_functor_for_ordering(lt, min). | |
2079 | get_partial_b_functor_for_ordering(lte, min). | |
2080 | get_partial_b_functor_for_ordering(gt, max). | |
2081 | get_partial_b_functor_for_ordering(gte, max). | |
2082 | ||
2083 | strip_singleton_set(set_extension(_,[identifier(Pos,Name)]), identifier(Pos,Name)) :- | |
2084 | !. | |
2085 | strip_singleton_set(A, A). | |
2086 | ||
2087 | strip_singleton_set_from_this(set_extension(_,[identifier(Pos,this)]), identifier(Pos,this)) :- | |
2088 | !. | |
2089 | strip_singleton_set_from_this(A, A). | |
2090 | ||
2091 | translate_cartesian(TPos, Arg1, Arg2, Res) :- | |
2092 | translate_e_p(Arg1, TArg1), | |
2093 | translate_cartesian2(TPos, TArg1, Arg2, Res). | |
2094 | ||
2095 | translate_cartesian2(TPos, TArg1, Arg2, Res) :- | |
2096 | translate_e_p(Arg2, TArg2), | |
2097 | translate_cartesian3(TPos, TArg1, Arg2, TArg2, Res). | |
2098 | % P -> Q --> P*Q | |
2099 | translate_cartesian3(TPos, TArg1, Arg2, TArg2, cartesian_product(TPos,TArg1,TArg2)) :- | |
2100 | is_unary_relation(Arg2), | |
2101 | !. | |
2102 | % P -> Q --> {c0, c1,...,ck | c0:P & (c1,...,ck):Q} | |
2103 | translate_cartesian3(TPos, TArg1, Arg2, TArg2, comprehension_set(TPos,[ID0|IDs2],conjunct(TPos,Mem1,Mem2))) :- | |
2104 | get_type_and_arity_from_alloy_term(Arg2, _Type, Arity), | |
2105 | gen_ids_and_couple(Arity, IDs2, Couple2), | |
2106 | gen_identifier(0, "_c_", ID0), | |
2107 | check_position(TPos,translate_cartesian3), | |
2108 | Mem1 = member(TPos,ID0,TArg1), | |
2109 | Mem2 = member(TPos,Couple2,TArg2). | |
2110 | ||
2111 | gen_ids_and_couple(Arity, [ID1|IDListRest], NestedCouple) :- | |
2112 | Arity > 0, | |
2113 | gen_identifier(1, "_c_", ID1), | |
2114 | gen_ids_and_couple(2, Arity, IDListRest, ID1, NestedCouple). | |
2115 | ||
2116 | gen_ids_and_couple(Nr, Arity, L, Acc, Res) :- | |
2117 | Nr > Arity, | |
2118 | !, | |
2119 | L = [], | |
2120 | Res = Acc. | |
2121 | gen_ids_and_couple(Nr, Arity, [IDN|T], Acc, Res) :- | |
2122 | gen_identifier(Nr, "_c_", IDN), | |
2123 | N1 is Nr+1, | |
2124 | gen_ids_and_couple(N1, Arity, T, couple(none,Acc,IDN), Res). | |
2125 | ||
2126 | gen_identifier(Nr, Prefix, identifier(none,ID)) :- | |
2127 | number_codes(Nr, NrC), | |
2128 | append(Prefix, NrC, AC), | |
2129 | atom_codes(ID, AC). | |
2130 | ||
2131 | is_ordering_definition_call(FunCall) :- | |
2132 | FunCall =.. [Functor,Name,_,_,_], | |
2133 | Functor == fun_call, | |
2134 | atom_concat('ordering\'', Rest, Name), | |
2135 | memberchk(Rest, [next,prev,nexts,prevs]). | |
2136 | ||
2137 | %% annotate_undefined_ordering_definition_call(+AlloyTerm, +AnnotateWith, -AnnotatedAlloyTerm). | |
2138 | % Top-level call is assumed to be a join. We join "undefined" ordering definition calls in | |
2139 | % arguments of the top-level join with an Alloy term. | |
2140 | % For instance: | |
2141 | % 'this.(prev + next)', where it actually is 'this.prev + this.next' as used in einsteins_enum.als | |
2142 | annotate_undefined_ordering_definition_call(Atom, _, Out) :- | |
2143 | atom(Atom), | |
2144 | !, | |
2145 | Out = Atom. | |
2146 | annotate_undefined_ordering_definition_call(join(Arg1,Arg2,Type,POS), AnnotateWith, Annotated) :- | |
2147 | is_ordering_definition_call(Arg1), | |
2148 | !, | |
2149 | annotate_undefined_ordering_definition_call(Arg2, AnnotateWith, NArg2), | |
2150 | Annotated = join(Arg1,NArg2,Type,POS). | |
2151 | annotate_undefined_ordering_definition_call(join(Arg1,Arg2,Type,POS), AnnotateWith, Annotated) :- | |
2152 | is_ordering_definition_call(Arg2), | |
2153 | !, | |
2154 | annotate_undefined_ordering_definition_call(Arg1, AnnotateWith, NArg1), | |
2155 | Annotated = join(NArg1,Arg2,Type,POS). | |
2156 | annotate_undefined_ordering_definition_call(Binary, AnnotateWith, Annotated) :- | |
2157 | % TODO: nested join with different top-level ordering definition call here? | |
2158 | functor(Binary, BFun, 4), | |
2159 | !, | |
2160 | arg(1, Binary, Arg1), | |
2161 | arg(2, Binary, Arg2), | |
2162 | arg(3, Binary, Type), | |
2163 | arg(4, Binary, POS), | |
2164 | ( is_ordering_definition_call(Arg1) | |
2165 | -> get_alloy_position(Arg1, A1POS), | |
2166 | get_alloy_type(Arg1, Arg1Type), | |
2167 | NArg1 = join(AnnotateWith,Arg1,Arg1Type,A1POS) | |
2168 | ; annotate_undefined_ordering_definition_call(Arg1, AnnotateWith, NArg1) | |
2169 | ), | |
2170 | ( is_ordering_definition_call(Arg2) | |
2171 | -> get_alloy_position(Arg2, A2POS), | |
2172 | get_alloy_type(Arg2, Arg2Type), | |
2173 | NArg2 = join(AnnotateWith,Arg2,Arg2Type,A2POS) | |
2174 | ; annotate_undefined_ordering_definition_call(Arg2, AnnotateWith, NArg2) | |
2175 | ), | |
2176 | functor(Annotated, BFun, 4), | |
2177 | arg(1, Annotated, NArg1), | |
2178 | arg(2, Annotated, NArg2), | |
2179 | arg(3, Annotated, Type), | |
2180 | arg(4, Annotated, POS). | |
2181 | annotate_undefined_ordering_definition_call(AlloyTerm, _, AlloyTerm). | |
2182 | ||
2183 | atom_ends_with(Atom, EndsWith) :- | |
2184 | atom_concat(_, EndsWith, Atom). | |
2185 | ||
2186 | % ordering function calls like s.next.next are converted to fun_call | |
2187 | translate_join(_, Arg1, Arg2, TBinaryJoin) :- | |
2188 | Arg2 = fun_call(Name,[],Type,A2Pos), | |
2189 | \+ atom_prefix('integer', Name), | |
2190 | \+ atom_ends_with(Name, 'first'), % one can use just first or last without a join while the actual Signature is inferred by its type | |
2191 | \+ atom_ends_with(Name, 'last'), % e.g., see ring_election1.als 'no elected.first' where elected is an ordered signature field of sig P | |
2192 | % 'no elected.first' is thus equivalent to 'no elected.(P.first)' | |
2193 | translate_e_p(fun_call(Name,[Arg1],Type,A2Pos), TBinaryJoin). | |
2194 | % Translation of the dot join operator has several special cases depending on the arity of the arguments. | |
2195 | translate_join(TPos, Arg1, Arg2, TBinaryJoin) :- | |
2196 | translate_e_p(Arg1, TArg1), | |
2197 | translate_e_p(Arg2, TArg2), | |
2198 | translate_join_aux(TPos, Arg1, Arg2, TArg1, TArg2, TBinaryJoin). | |
2199 | ||
2200 | % Note: N-ary relation are encoded in B as follows: | |
2201 | % { (e1 |-> (e2 |-> (e3 |-> ...))) ,... }, for example: | |
2202 | % {(tracy|->(spanish|->french)),(carol|->(spanish|->french)), ...} | |
2203 | % univ.P --> ran(P) % this does NOT work for ternary,... relations | |
2204 | translate_join_aux(TPos, Arg1, Arg2, _TArg1, TArg2, range(TPos,TArg2)) :- | |
2205 | is_univ(Arg1), | |
2206 | is_binary_relation(Arg2), | |
2207 | !. | |
2208 | % P.univ --> dom(P) % this works for ternary,... relations | |
2209 | translate_join_aux(TPos, _Arg1, Arg2, TArg1, _TArg2, domain(TPos,TArg1)) :- | |
2210 | is_univ(Arg2), | |
2211 | !. | |
2212 | % {el}.R --> {R(el)} | |
2213 | translate_join_aux(TPos, Arg1, Arg2, TArg1, TArg2, set_extension(TPos,[function(TPos,TArg2,[TTArg1])])) :- | |
2214 | get_type_and_arity_from_alloy_term(Arg1, Type1, Arity), | |
2215 | Arity = 1, % is_unary_relation(Arg1), | |
2216 | is_binary_relation(Arg2), % TO DO: Arg2 can probably be n-ary? | |
2217 | is_total_function(TArg2, Type1), | |
2218 | can_remove_singleton_set(TArg1, TTArg1), | |
2219 | !. | |
2220 | % unary.R --> R[unary] | |
2221 | translate_join_aux(TPos, Arg1, Arg2, TArg1, TArg2, Join) :- % Unary.Arg2 --> Arg2[Unary] | |
2222 | is_unary_relation(Arg1), | |
2223 | is_binary_relation(Arg2), | |
2224 | !, | |
2225 | Join = image(TPos,TArg2,TArg1). | |
2226 | % binary.unary | |
2227 | % Next RULE is broken, can lead to Type errors and test is wrong: | |
2228 | % translate_join_aux(Pos,Arg1,Arg2,TArg1,TArg2,function(Pos,reverse(Pos,TArg1),[TTArg2])) :- | |
2229 | % % function call if lhs is a total function and rhs a unary relation | |
2230 | % is_binary_relation(Arg1), | |
2231 | % is_unary_relation(Arg2), | |
2232 | % is_total_function(TArg1) , ! , % TEST SHOULD BE SURJECTIVE and INJECTIVE !! | |
2233 | % remove_singleton_set(TArg2,TTArg2). | |
2234 | % Arg1.Unary --> Arg1~[Unary] | |
2235 | translate_join_aux(TPos, _Arg1, Arg2, TArg1, TArg2, image(TPos,reverse(TPos,TArg1),TArg2)) :- | |
2236 | is_unary_relation(Arg2), | |
2237 | !. | |
2238 | % binary.binary: Arg1.Arg2 --> (Arg1 ; Arg2) | |
2239 | translate_join_aux(TPos, Arg1, Arg2, TArg1, TArg2, composition(TPos,TArg1,TArg2)) :- | |
2240 | is_binary_relation(Arg1), | |
2241 | is_binary_relation(Arg2), | |
2242 | !. | |
2243 | translate_join_aux(TPos, Arg1, Arg2, TArg1, TArg2, comprehension_set(TPos,IDS,Body)) :- | |
2244 | Body = conjunct(TPos,TypingPred,exists(TPos,[JoinID],conjunct(TPos,JoinTypingPred,conjunct(TPos,Mem1,Mem2)))), | |
2245 | get_type_and_arity_from_alloy_term(Arg2, [JoinIDType|RTypes], Arity), % first type refers to outter variable | |
2246 | gen_ids_and_couple(Arity, IDs2, Couple2), | |
2247 | IDs2 = [JoinID|Rest2], | |
2248 | get_typing_pred_for_ids(TPos, RTypes, Rest2, TypingPred), | |
2249 | get_typing_pred_for_ids(TPos, [JoinIDType], [JoinID], JoinTypingPred), | |
2250 | ( is_univ(Arg1) -> | |
2251 | Mem1 = truth(TPos), | |
2252 | IDS = Rest2 | |
2253 | ; is_unary_relation(Arg1) -> | |
2254 | %strip_singleton_set_from_this(TArg1, TTArg1), | |
2255 | Mem1 = member(TPos,JoinID,TArg1), | |
2256 | IDS = Rest2 | |
2257 | ; gen_identifier(0, "_c_", ID0), | |
2258 | IDS = [ID0|Rest2], | |
2259 | Mem1 = member(TPos,couple(TPos,ID0,JoinID),TArg1) | |
2260 | ), | |
2261 | check_position(TPos,translate_join_aux), | |
2262 | Mem2 = member(TPos,Couple2,TArg2). | |
2263 | translate_join_aux(Pos, Arg1, Arg2, _TArg1, _TArg2, empty_set(Pos)) :- | |
2264 | format('~nJoin not supported this way:~nLeft: ~w~nRight: ~w~n', [Arg1,Arg2]), | |
2265 | add_error(load_alloy_model, 'Cannot translate this type of join yet', [Arg1,Arg2], Pos). | |
2266 | ||
2267 | % This predicate relies on the order of elements in 2. and 3. argument for typing. | |
2268 | % TO DO: improve | |
2269 | get_typing_pred_for_ids(Pos, [], [], truth(Pos)). | |
2270 | get_typing_pred_for_ids(Pos, [Type|TT], [ID|IT], TypingPred) :- | |
2271 | translate_e_p(Type, TType), | |
2272 | get_typing_pred_for_ids_acc(Pos, TT, IT, member(Pos,ID,TType), TypingPred). | |
2273 | ||
2274 | get_typing_pred_for_ids_acc(_, [], [], Acc, Acc). | |
2275 | get_typing_pred_for_ids_acc(Pos, [Type|TT], [ID|IT], Acc, TypingPred) :- | |
2276 | translate_e_p(Type, TType), | |
2277 | get_typing_pred_for_ids_acc(Pos, TT, IT, conjunct(Pos,Acc,member(Pos,ID,TType)), TypingPred). | |
2278 | ||
2279 | % translate Alloy position to B position | |
2280 | translate_pos(Var,Res) :- \+ ground(Var),!, | |
2281 | add_internal_error('Translating non-ground Alloy position to none:',translate_pos(Var,Res)), | |
2282 | Res=none. | |
2283 | translate_pos(pos(Col,Row), Res) :- nonvar(Col), nonvar(Row), | |
2284 | !, | |
2285 | row_col_to_position(Row,Col,Res). | |
2286 | translate_pos(_, none). | |
2287 | ||
2288 | %get_position(Term, BPos) :- | |
2289 | % get_alloy_position(Term, APos), | |
2290 | % !, | |
2291 | % translate_pos(APos, BPos). | |
2292 | %get_position(_, none). | |
2293 | ||
2294 | get_alloy_type(Term, AType) :- | |
2295 | functor(Term, _, Arity), | |
2296 | Arity > 0, | |
2297 | Arity1 is Arity - 1, | |
2298 | arg(Arity1, Term, AType). | |
2299 | ||
2300 | get_alloy_position(Term, APos) :- | |
2301 | functor(Term, _, Arity), | |
2302 | Arity > 0, | |
2303 | arg(Arity, Term, APos). | |
2304 | ||
2305 | get_b_position(UntypedBAst, BPOS) :- | |
2306 | functor(UntypedBAst, _, Arity), | |
2307 | Arity > 0, | |
2308 | arg(1, UntypedBAst, BPOS). | |
2309 | ||
2310 | check_position(Var,PP) :- var(Var),!,add_internal_error('Variable position at: ',PP),Var=none. | |
2311 | check_position(P,_) :- is_valid_position(P),!. | |
2312 | check_position(T,PP) :- add_warning(check_position,'Unknown position at: ',PP:T). | |
2313 | ||
2314 | translate_quantified_e(Pos, no, TArg, equal(Pos,TArg,empty_set(none))). | |
2315 | translate_quantified_e(Pos, AlloyKw, TArg, equal(Pos,card(none,TArg),integer(none,1))) :- | |
2316 | % naming possibly differs having the same semantics | |
2317 | AlloyKw = one; AlloyKw = oneof. | |
2318 | translate_quantified_e(Pos, AlloyKw, TArg, greater(Pos,card(none,TArg),integer(none,0))) :- | |
2319 | AlloyKw = some; AlloyKw = someof. | |
2320 | translate_quantified_e(Pos, AlloyKw, TArg, less_equal(Pos,card(none,TArg),integer(none,1))) :- | |
2321 | AlloyKw = lone; AlloyKw = loneof. | |
2322 | translate_quantified_e(_, exactlyof, TArg, TArg). | |
2323 | ||
2324 | % Join a list of types using cartesian_product like ((A*B)*C) for types [A,B,C]. | |
2325 | create_cartesian_type([ID], TID) :- | |
2326 | !, | |
2327 | translate_unary_e_p(ID, TID). | |
2328 | create_cartesian_type([ID1,ID2], cartesian_product(none,TID1,TID2)) :- | |
2329 | !, | |
2330 | translate_unary_e_p(ID1, TID1), | |
2331 | translate_unary_e_p(ID2, TID2). | |
2332 | create_cartesian_type([ID1,ID2|T], cartesian_product(none,cartesian_product(none,TID1,TID2),NT)) :- | |
2333 | translate_unary_e_p(ID1, TID1), | |
2334 | translate_unary_e_p(ID2, TID2), | |
2335 | create_cartesian_type(T, NT). | |
2336 | ||
2337 | % Field declarations have several special cases depending on the keywords set, one, some, lone or seq. | |
2338 | % We are inside Signature TSignatureName sig NAME { field : DeclTerm } | |
2339 | translate_field_decl(BPOS, SignatureName, TSignatureName, DeclTerm, FieldID, TFieldID, TFieldPred) :- | |
2340 | % DeclTerm =.. [_,SetID|_], | |
2341 | replace_this_in_signature_field(DeclTerm, NewDeclTerm), | |
2342 | debug_format(19, 'Translating Field ~w inside signature ~w,~n Term=~w,~n~n', [FieldID,SignatureName,NewDeclTerm]), | |
2343 | ( translate_special_field_multiplicity_binary_e_p(NewDeclTerm, TFUNC) | |
2344 | -> | |
2345 | % we could do: translate_cartesian2(BPOS,TSignatureName,NewDeclTerm,Cart), TFieldPred = subset(BPOS,TFieldID,Cart), and add predicate | |
2346 | % TO DO: better treatment, also detect nested special multiplicity annotations inside NewDeclTerm | |
2347 | % generate forall x:SIG => x.field : TFUNC | |
2348 | ID = identifier(BPOS,'_x_'), | |
2349 | AID = identifier('_x_',type([SignatureName],1),none), | |
2350 | ( singleton_set('_x_') -> | |
2351 | true | |
2352 | ; assert_singleton_set('_x_') | |
2353 | ), | |
2354 | translate_e_p(AID, TAID), % will add set-extension wrapper | |
2355 | LHS = member(BPOS,ID,TSignatureName), | |
2356 | RHS = member(BPOS,TJoin,TFUNC), | |
2357 | TFieldPred = conjunct(BPOS,FieldTyping,forall(BPOS,[ID],implication(none,LHS,RHS))), | |
2358 | translate_join_aux(BPOS, AID, FieldID, TAID, TFieldID, TJoin), | |
2359 | get_type_and_arity_from_alloy_term(FieldID, TypeList, _), | |
2360 | create_cartesian_type(TypeList, CartesianType), | |
2361 | FieldTyping = member(BPOS,TFieldID,pow_subset(BPOS,CartesianType)) | |
2362 | ; translate_sig_field_rhs(FieldID, NewDeclTerm, BPOS, TSignatureName, TFieldID, FieldTypeExpr) | |
2363 | -> | |
2364 | add_constraint_for_someof_field(NewDeclTerm, member(BPOS,TFieldID,FieldTypeExpr), TSignatureName, TTFieldPred), | |
2365 | add_constraint_for_seq_field(NewDeclTerm, TSignatureName, TTFieldPred, TFieldID, TFieldPred) | |
2366 | ; add_error(alloy2b, 'Field declaration not implemented:', NewDeclTerm, BPOS), | |
2367 | TFieldPred = truth(none) | |
2368 | ). | |
2369 | ||
2370 | add_constraint_for_someof_field(someof(_,_,_), TempPred, TSignatureName, TPred) :- | |
2371 | !, | |
2372 | TempPred = member(BPOS,TFieldID,_), | |
2373 | TPred = conjunct(BPOS,equal(BPOS,domain(BPOS,TFieldID),TSignatureName),TempPred). | |
2374 | add_constraint_for_someof_field(_, Pred, _, Pred). | |
2375 | ||
2376 | add_constraint_for_seq_field('is_seq'(_,_,_), TSignatureName, TempPred, TFieldID, TPred) :- | |
2377 | !, | |
2378 | % !_s.(_s : TSignatureName => dom(TFieldID)[{_s}] = 0..card(dom(TFieldID)[{_s}])-1) | |
2379 | TPred = conjunct(BPOS,TempPred,forall([identifier(BPOS,'_s')], | |
2380 | implication(BPOS,member(identifier(BPOS,'_s'),TSignatureName), | |
2381 | equal(BPOS,image(BPOS,domain(BPOS,TFieldID),set_extension(BPOS,[identifier(BPOS,'_s')])), | |
2382 | interval(BPOS,integer(BPOS,0),minus(BPOS,card(BPOS,image(BPOS,domain(BPOS,TFieldID), | |
2383 | set_extension(BPOS,[identifier(BPOS,'_s')]))),integer(BPOS,1))))))). | |
2384 | add_constraint_for_seq_field(_, _, Pred, _, Pred). | |
2385 | ||
2386 | % sig SIG { field : set Set} --> field : SIG <-> Set | |
2387 | translate_sig_field_rhs(_, setof(Set,_,_), _, SIG, _, relations(none,SIG,TSet)) :- | |
2388 | translate_e_p(Set, TSet). | |
2389 | % sig SIG { field : one Set} --> field : SIG --> Set | |
2390 | translate_sig_field_rhs(_, oneof(Set,_,_), _, SIG, TFieldID, total_function(none,SIG,TSet)) :- | |
2391 | translate_e_p(Set, TSet), | |
2392 | assert_total_function(TFieldID, SIG). | |
2393 | % sig SIG { field : lone Set} --> field : SIG +-> Set | |
2394 | translate_sig_field_rhs(_, loneof(Set,_,_), _, SIG, _, partial_function(none,SIG,TSet)) :- | |
2395 | translate_e_p(Set, TSet). | |
2396 | % sig SIG { field : some Set} --> field : SIG +-> Set | |
2397 | translate_sig_field_rhs(_, someof(Set,_,_), _, SIG, _, relations(none,SIG,TSet)) :- | |
2398 | translate_e_p(Set, TSet). | |
2399 | % sig SIG { field : seq Set} --> field : (SIG * Integer) +-> Set | |
2400 | translate_sig_field_rhs(FieldID, 'is_seq'(_,Set,_,_), _, SIG, _, partial_function(none,cartesian_product(none,SIG,interval(none,integer(none,0),integer(none,MaxSeq1))),TSet)) :- | |
2401 | maxseq(MaxSeq), | |
2402 | MaxSeq1 is MaxSeq - 1, | |
2403 | FieldID = identifier(Name,Type,_), | |
2404 | prepend_identifier_of_module_import(Name, Type, NName), | |
2405 | assertz(is_seq(NName)), | |
2406 | translate_e_p(Set, TSet). | |
2407 | % sig SIG { field : FieldTerm} --> field : POW(SIG * FieldTerm) | |
2408 | translate_sig_field_rhs(_, FieldTerm, Pos, SIG, _, pow_subset(none,Cart)) :- | |
2409 | % TO DO: check whether lone, one, ... can appear inside FieldTerm | |
2410 | translate_cartesian2(Pos, SIG, FieldTerm, Cart). | |
2411 | ||
2412 | replace_this_in_signature_field(identifier('this',type([SignatureName],1),Pos), Res) :- | |
2413 | !, | |
2414 | Res = identifier(SignatureName,type([SignatureName],1),Pos). | |
2415 | replace_this_in_signature_field(DeclTerm, NewDeclTerm) :- | |
2416 | DeclTerm =.. [Functor,Arg1,Type,Pos], | |
2417 | !, | |
2418 | replace_this_in_signature_field(Arg1, NArg1), | |
2419 | NewDeclTerm =.. [Functor,NArg1,Type,Pos]. | |
2420 | replace_this_in_signature_field(DeclTerm, NewDeclTerm) :- | |
2421 | DeclTerm =.. [Functor,Arg1,Arg2,Type,Pos], | |
2422 | !, | |
2423 | replace_this_in_signature_field(Arg1, NArg1), | |
2424 | replace_this_in_signature_field(Arg2, NArg2), | |
2425 | NewDeclTerm =.. [Functor,NArg1,NArg2,Type,Pos]. | |
2426 | replace_this_in_signature_field(DeclTerm, DeclTerm). | |
2427 | ||
2428 | % Translate TFieldID: Declaration | |
2429 | fun_or_pred_decl_special_cases(setof(Expr,_,_), TFieldID, Pos, subset(Pos,TFieldID,TExpr), set) :- | |
2430 | translate_e_p(Expr, TExpr). | |
2431 | fun_or_pred_decl_special_cases(oneof(Expr,_,_), TFieldID, Pos, subset(Pos,TFieldID,TExpr), singleton) :- % Default | |
2432 | translate_e_p(Expr, TExpr). | |
2433 | fun_or_pred_decl_special_cases(loneof(Expr,_,_), TFieldID, Pos, conjunct(Pos,subset(Pos,TFieldID,TExpr),ExtraPred), set) :- | |
2434 | % x : lone S -> x <: S & card(x) <= 1 | |
2435 | ExtraPred = less_equal(Pos,card(Pos,TFieldID),integer(Pos,1)), | |
2436 | translate_e_p(Expr, TExpr). | |
2437 | fun_or_pred_decl_special_cases(someof(Expr,_,_), TFieldID, Pos, conjunct(Pos,subset(Pos,TFieldID,TExpr),ExtraPred), set) :- | |
2438 | % x : some S -> x <: S & x \= {} | |
2439 | ExtraPred = not_equal(Pos,TFieldID,empty_set(Pos)), | |
2440 | translate_e_p(Expr, TExpr). | |
2441 | fun_or_pred_decl_special_cases(Expr, TFieldID, Pos, Translation, IsSingleton) :- | |
2442 | % treat identifier, cartesian, union, ... use translate of TFieldID in Expr | |
2443 | translate_binary_in(TFieldID, Expr, Pos, Translation), | |
2444 | get_type_and_arity_from_alloy_term(Expr, _Type, Arity), | |
2445 | !, | |
2446 | ( Arity = 1 -> | |
2447 | IsSingleton = singleton | |
2448 | ; IsSingleton = set | |
2449 | ). | |
2450 | fun_or_pred_decl_special_cases(Term, _, Pos, falsity(Pos), set) :- | |
2451 | add_error(alloy2b, 'Field declaration for function or predicate not implemented:', Term, Pos). | |
2452 | ||
2453 | alloy_to_b_operator(Op, BOp) :- | |
2454 | alloy_to_b_unary_operator(Op, BOp), | |
2455 | !. | |
2456 | alloy_to_b_operator(Op, BOp) :- | |
2457 | alloy_to_b_binary_operator(Op, BOp), | |
2458 | !. | |
2459 | alloy_to_b_operator(Op, Op) :- | |
2460 | format('~n~nTRANSLATING ~w to B without modification!~n', [Op]). | |
2461 | ||
2462 | alloy_to_b_unary_operator(not, negation). | |
2463 | alloy_to_b_unary_operator(closure1, closure). | |
2464 | alloy_to_b_unary_operator(closure, reflexive_closure). | |
2465 | alloy_to_b_unary_operator(inverse, reverse). % works for binary relation only; indeed Alloy says: ~ can be used only with a binary relation. | |
2466 | ||
2467 | ||
2468 | alloy_to_b_binary_operator(in, subset). | |
2469 | alloy_to_b_binary_operator(not_in, not_subset). | |
2470 | alloy_to_b_binary_operator(plus, union). | |
2471 | alloy_to_b_binary_operator(or, disjunct). % actually treated as unary operator | |
2472 | alloy_to_b_binary_operator(and, conjunct). % actually treated as unary operator | |
2473 | alloy_to_b_binary_operator(minus, set_subtraction). | |
2474 | % alloy_to_b_binary_operator(cartesian,cartesian_product). % only works for X->UNARY ! | |
2475 | alloy_to_b_binary_operator(function, expression_definition). | |
2476 | alloy_to_b_binary_operator(predicate, predicate_definition). | |
2477 | alloy_to_b_binary_operator(not_equal, not_equal). | |
2478 | alloy_to_b_binary_operator(dom_restr, domain_restriction). % not ok for ternary, set must be unary | |
2479 | alloy_to_b_binary_operator(ran_restr, range_restriction). % also ok for ternary, ... relations | |
2480 | alloy_to_b_binary_operator(equal, equal). | |
2481 | alloy_to_b_binary_operator(conjunct, conjunct). | |
2482 | alloy_to_b_binary_operator(intersection, intersection). | |
2483 | alloy_to_b_binary_operator(implication, implication). | |
2484 | alloy_to_b_binary_operator(iff, equivalence). | |
2485 | alloy_to_b_binary_operator(override, overwrite). % ++ | |
2486 | ||
2487 | is_cartesian_product_term(Term, Arg1, Arg2, POS) :- | |
2488 | functor(Term, Functor, 4), | |
2489 | is_cartesian_product(Functor), | |
2490 | arg(1, Term, Arg1), | |
2491 | arg(2, Term, Arg2), | |
2492 | arg(4, Term, POS). | |
2493 | is_cartesian_product(cartesian). | |
2494 | ||
2495 | ||
2496 | % predicates with integer arguments | |
2497 | alloy_to_b_integer_operator(less, less). | |
2498 | alloy_to_b_integer_operator(greater, greater). | |
2499 | alloy_to_b_integer_operator(less_equal, less_equal). | |
2500 | alloy_to_b_integer_operator(greater_equal, greater_equal). | |
2501 | ||
2502 | ||
2503 | %%% | |
2504 | % Accumulate the translated machine parts and signature names during the translation and build the machine AST afterwards. | |
2505 | % We may need the signature names later on if translating a global scope. | |
2506 | build_machine_ast(b_machine(ListOfMachineParts,_SignatureNames), machine(generated(none,AbstractMachine))) :- | |
2507 | % filter empty machine parts | |
2508 | findall(MachinePart, ( member(MachinePart, ListOfMachineParts), | |
2509 | arg(2,MachinePart,L), | |
2510 | L \== [] | |
2511 | ), NonEmptyMachineSections), | |
2512 | % properties need to be conjoined | |
2513 | ( select(properties(none,L), NonEmptyMachineSections, RestListOfUsedMachineParts) -> | |
2514 | true | |
2515 | ; L == [], | |
2516 | RestListOfUsedMachineParts = NonEmptyMachineSections | |
2517 | ), | |
2518 | join_predicates(conjunct, L, FlatL), | |
2519 | AbstractMachine = abstract_machine(none,machine(none), | |
2520 | machine_header(none,alloytranslation,[]), | |
2521 | [properties(none,FlatL)|RestListOfUsedMachineParts]), | |
2522 | !. | |
2523 | ||
2524 | empty_machine_acc(b_machine([sets(none,[]),constants(none,[]), | |
2525 | definitions(none,[]),properties(none,[]), | |
2526 | assertions(none,[]),operations(none,[])],[])). | |
2527 | ||
2528 | % extend a machine section with a give list of conjuncts | |
2529 | extend_machine_with_conjuncts(_Section, [], Acc, Res) :- | |
2530 | !, | |
2531 | Res = Acc. | |
2532 | extend_machine_with_conjuncts(Section, Conjuncts, MAcc1, MAcc2) :- | |
2533 | join_untyped_ast_nodes(conjunct, Conjuncts, TPred), | |
2534 | extend_machine_acc(Section, MAcc1, TPred, MAcc2). | |
2535 | ||
2536 | :- if(environ(prob_safe_mode,true)). | |
2537 | extend_machine_acc(F, _, New, _) :- \+ ground(New), | |
2538 | add_internal_error('Non-ground new formula:',extend_machine_acc(F, _, New, _)), | |
2539 | fail. | |
2540 | :- endif. | |
2541 | extend_machine_acc(signatures, b_machine(MachineParts,SignatureNames), New, | |
2542 | b_machine(MachineParts,NewSignatureNames)) :- | |
2543 | !, | |
2544 | append(SignatureNames, New, TSignatureNames), | |
2545 | remove_dups(TSignatureNames, NewSignatureNames). | |
2546 | extend_machine_acc(Functor, b_machine(MachineParts,SignatureNames), New, | |
2547 | b_machine([NewMachinePart|RestMachineParts],SignatureNames)) :- | |
2548 | MachinePart =.. [Functor,none,List], | |
2549 | select(MachinePart, MachineParts, RestMachineParts), | |
2550 | append(List, [New], NewList), % keeps order, but is inefficient! | |
2551 | NewMachinePart =.. [Functor,none,NewList], | |
2552 | !. | |
2553 | extend_machine_acc(Functor, _, _, _) :- | |
2554 | add_internal_error('Failed: ', extend_machine_acc(Functor,_,_,_)), | |
2555 | fail. | |
2556 | ||
2557 | get_signature_names_from_machine_acc(b_machine(_MachineParts,SignatureNames), SignatureNames). | |
2558 | ||
2559 | finalize_extending_signatures(MAcc, NewMAcc) :- | |
2560 | extending_signatures(List), | |
2561 | !, | |
2562 | finalize_extending_signatures_aux(MAcc, List, NewMAcc). | |
2563 | finalize_extending_signatures(MAcc, MAcc). | |
2564 | ||
2565 | finalize_extending_signatures_aux(MAcc, [], MAcc). | |
2566 | finalize_extending_signatures_aux(MAcc, [(Parent,SubSigs)|T], NewMAcc) :- | |
2567 | length(SubSigs, AmountOfSubSigs), | |
2568 | AmountOfSubSigs > 1, | |
2569 | !, | |
2570 | maplist(translate_sub_sig, SubSigs, TSubSigs), | |
2571 | translate_e_p(Parent, TParent), | |
2572 | extend_machine_acc(properties, MAcc, partition(none,TParent,TSubSigs), MAcc2), | |
2573 | % equivalent to | |
2574 | % parent = sub1 \/ sub2 \/ ... \/ subN & sub1 /\ sub2 = {} & ... | |
2575 | findall(OneSub, member(OneSub/one, SubSigs), OneSubSigs), | |
2576 | length(OneSubSigs, AmountOneSubSigs), | |
2577 | ( AmountOneSubSigs = AmountOfSubSigs % we basically have an enumerated set with only singleton elements | |
2578 | -> | |
2579 | extend_machine_acc(properties, MAcc2, equal(none,card(none,TParent),integer(none,AmountOneSubSigs)), MAcc3) | |
2580 | ; extend_machine_acc(properties, MAcc2, greater_equal(none,card(none,TParent),integer(none,AmountOneSubSigs)), MAcc3) | |
2581 | ), | |
2582 | finalize_extending_signatures_aux(MAcc3, T, NewMAcc). | |
2583 | finalize_extending_signatures_aux(MAcc, [_|T], NewMAcc) :- | |
2584 | finalize_extending_signatures_aux(MAcc, T, NewMAcc). | |
2585 | %%% | |
2586 | translate_sub_sig(Name/_One, B) :- | |
2587 | translate_e_p(Name, B). | |
2588 | ||
2589 | assert_extending_signature(Name, Parent, One) :- | |
2590 | ( is_sub_signature_of(Name, Parent, One) -> | |
2591 | true | |
2592 | ; debug_format(19, '~w is sub signature of ~w (~w)~n', [Name,Parent,One]), | |
2593 | assertz(is_sub_signature_of(Name, Parent, One)) | |
2594 | ), | |
2595 | extending_signatures(List), | |
2596 | select((Parent,SubSigs), List, Rest), | |
2597 | !, | |
2598 | ( \+ memberchk(Name/One, SubSigs) | |
2599 | -> | |
2600 | retractall(extending_signatures(_)), | |
2601 | asserta(extending_signatures([(Parent,[Name/One|SubSigs])|Rest])) | |
2602 | ; true | |
2603 | ). | |
2604 | assert_extending_signature(Name, Parent, One) :- | |
2605 | retract(extending_signatures(List)), | |
2606 | !, | |
2607 | asserta(extending_signatures([(Parent,[Name/One])|List])). | |
2608 | assert_extending_signature(Name, Parent, One) :- | |
2609 | asserta(extending_signatures([(Parent,[Name/One])])). | |
2610 | ||
2611 | assert_enumerated_set(Options, Name) :- | |
2612 | % singleton top-level signatures are defined as an enumerated set like S = {_S} | |
2613 | member(one, Options), | |
2614 | % but extending singleton signatures are defined as constants and singleton subsets of their parent type | |
2615 | \+ member(subset(_), Options), | |
2616 | \+ member(subsig(_), Options), | |
2617 | !, | |
2618 | asserta(enumerated_set(Name)). | |
2619 | assert_enumerated_set(_, _). | |
2620 | ||
2621 | assert_abstract_sig(Options, Name) :- | |
2622 | member(abstract, Options), | |
2623 | !, | |
2624 | asserta(abstract_sig(Name)). | |
2625 | assert_abstract_sig(_, _). | |
2626 | ||
2627 | assert_singleton_set(Options, Name) :- | |
2628 | member(one, Options), | |
2629 | !, | |
2630 | assert_singleton_set(Name). | |
2631 | assert_singleton_set(_, _). | |
2632 | ||
2633 | assert_singleton_set(Name) :- % debug_format(19,'Singleton set : ~w~n',[Name]),nl, | |
2634 | % \+ singleton_set(Name), | |
2635 | asserta(singleton_set(Name)), | |
2636 | !. | |
2637 | % assert_singleton_set(_). | |
2638 | retract_singleton_set(BPOS, Name) :- | |
2639 | ( retract(singleton_set(Name)) -> | |
2640 | true | |
2641 | ; | |
2642 | add_message(retract_singleton_set, 'WARNING: no singleton_set fact for: ', Name, BPOS) % can be ok for non-singleton ids | |
2643 | ). | |
2644 | % retractall(singleton_set(Name)). | |
2645 | retract_state :- | |
2646 | % don't retract model_path here | |
2647 | retractall(fact(_)), | |
2648 | retractall(assertion(_)), | |
2649 | retractall(definition_without_param(_)), | |
2650 | retractall(skipped_command(_,_)), | |
2651 | retractall(translated_command(_,_)), | |
2652 | retractall(abstract_sig(_)), | |
2653 | retractall(sig_field_id(_, _)), | |
2654 | retractall(artificial_parent_type(_, _, _)), | |
2655 | retractall(current_module_in_scope(_)), | |
2656 | retractall(module_in_scope_but_root(_, _)), | |
2657 | retractall(enumerated_set(_)), | |
2658 | retractall(singleton_set(_)), | |
2659 | retractall(ordered_signature(_)), | |
2660 | retractall(total_function(_, _)), | |
2661 | retractall(extending_signatures(_)), | |
2662 | retractall(is_seq(_)), | |
2663 | retractall(maxseq(_)), | |
2664 | retractall(is_sub_signature_of(_, _, _)), | |
2665 | !. | |
2666 | ||
2667 | % an identifier which in B refers not to a set but an element of a set, representing a singleton set in Alloy | |
2668 | is_singleton_set_id(this). % this is always singleton (TO DO: check this ;-) | |
2669 | is_singleton_set_id(IDName) :- | |
2670 | singleton_set(IDName). | |
2671 | ||
2672 | assert_total_function(identifier(_,Name), SIG) :- | |
2673 | !, | |
2674 | assert_total_function(Name, SIG). | |
2675 | assert_total_function(Name, identifier(_,SIG)) :- | |
2676 | !, | |
2677 | assert_total_function(Name, SIG). | |
2678 | assert_total_function(Name, SIG) :- | |
2679 | debug_format(19, 'Total function ~w over ~w~n', [Name,SIG]), | |
2680 | asserta(total_function(Name, SIG)). | |
2681 | ||
2682 | is_total_function(predecessor(_), _). % translation of prev | |
2683 | is_total_function(successor(_), _). % translation of next | |
2684 | is_total_function(function(_,ID,_), ArgType) :- | |
2685 | !, | |
2686 | is_total_function(ID, ArgType). | |
2687 | is_total_function(identifier(_,Name), ArgType) :- | |
2688 | !, | |
2689 | is_total_function(Name, ArgType). | |
2690 | is_total_function(Name, [ArgType]) :- | |
2691 | total_function(Name, FunType), % TO DO: check if FunctionType is Superset of ArgType | |
2692 | ( strip_singleton_set(FunType, SFunType), | |
2693 | SFunType = identifier(_,ArgType) | |
2694 | -> true | |
2695 | ; ArgType = FunType -> true | |
2696 | ; is_sub_signature_of(ArgType, FunType, _) | |
2697 | ). | |
2698 | ||
2699 | assert_ordered_sig_name(Name) :- | |
2700 | asserta(ordered_signature(Name)). | |
2701 | ||
2702 | is_ordered_signature(IDName) :- | |
2703 | ordered_signature(IDName). | |
2704 | is_ordered_signature('ordering\''). | |
2705 | ||
2706 | is_univ(identifier('univ',_,_)). % universe | |
2707 | is_univ(identifier(_,'univ')). | |
2708 | ||
2709 | is_iden(iden(_)). | |
2710 | is_iden(event_b_identity(_)). | |
2711 | ||
2712 | is_unary_relation(AlloyTerm) :- | |
2713 | get_type_and_arity_from_alloy_term(AlloyTerm, _Type, Arity), | |
2714 | Arity == 1. | |
2715 | ||
2716 | is_binary_relation(AlloyTerm) :- | |
2717 | get_type_and_arity_from_alloy_term(AlloyTerm, _Type, Arity), | |
2718 | Arity == 2. | |
2719 | ||
2720 | can_remove_singleton_set(set_extension(_,[Element]), Element). | |
2721 | % remove_singleton_set(AST,AST). | |
2722 | get_type_and_arity_from_alloy_term(integer(_,_), Type, Arity) :- | |
2723 | !, | |
2724 | Type = integer, | |
2725 | Arity = 1. % will get translated to singleton set | |
2726 | get_type_and_arity_from_alloy_term(and(_,_), Type, Arity) :- | |
2727 | !, | |
2728 | Type = [untyped], | |
2729 | Arity = 0. % predicate | |
2730 | get_type_and_arity_from_alloy_term(or(_,_), Type, Arity) :- | |
2731 | !, | |
2732 | Type = [untyped], | |
2733 | Arity = 0. % predicate | |
2734 | get_type_and_arity_from_alloy_term(iden(_), Type, Arity) :- | |
2735 | !, | |
2736 | Type = univ, | |
2737 | Arity = 2. | |
2738 | get_type_and_arity_from_alloy_term(AlloyTerm, Type, NArity) :- | |
2739 | AlloyTerm =.. [_|Args], | |
2740 | member(type(Type,Arity), Args), | |
2741 | ( is_primitive_type(Type, NArity) | |
2742 | ; | |
2743 | ( length(Type, LType), | |
2744 | LType == Arity, | |
2745 | NArity = Arity | |
2746 | ) | |
2747 | ), | |
2748 | !. | |
2749 | get_type_and_arity_from_alloy_term(AlloyTerm, Type, Arity) :- | |
2750 | add_warning(alloy2b, 'Could not extract type and arity from Alloy term:', AlloyTerm), | |
2751 | Type = [untyped], | |
2752 | Arity = 0. % like predicate | |
2753 | ||
2754 | % TO DO: maybe there are more primitive types? | |
2755 | % primitive types have arity zero in Alloy for some reason | |
2756 | is_primitive_type(['PrimitiveBoolean'], 1). | |
2757 | ||
2758 | % atom_or_identifier_term(ID,ID) :- atom(ID). | |
2759 | % atom_or_identifier_term(identifier(IDName,_,_),IDName). | |
2760 | join_predicates(Op, TArgList, TBinaryP) :- | |
2761 | alloy_to_b_operator(Op, BOp), | |
2762 | ( BOp = conjunct | |
2763 | ; BOp = disjunct | |
2764 | ), | |
2765 | join_untyped_ast_nodes(BOp, TArgList, TBinaryP). | |
2766 | ||
2767 | join_untyped_ast_nodes(_, [], truth(none)). | |
2768 | join_untyped_ast_nodes(BOp, [H|T], Conjoined) :- | |
2769 | join_untyped_ast_nodes(BOp, T, H, Conjoined). | |
2770 | ||
2771 | join_untyped_ast_nodes(_, [], Acc, Acc). | |
2772 | join_untyped_ast_nodes(BOp, [H|T], Acc, Conjoined) :- | |
2773 | NewAcc =.. [BOp,none,Acc,H], | |
2774 | join_untyped_ast_nodes(BOp, T, NewAcc, Conjoined). | |
2775 | ||
2776 | ||
2777 | %:- load_files(library(system), [when(compile_time), imports([environ/2])]). | |
2778 | :- if(environ(prob_release,true)). | |
2779 | % Don't include tests in release mode. | |
2780 | :- else. | |
2781 | ||
2782 | :- use_module(library(plunit)). | |
2783 | :- use_module(testsrc(test_paths), []). % set up prob_examples(...) alias | |
2784 | ||
2785 | :- begin_tests(full_machine_translation). | |
2786 | ||
2787 | test(cards, []) :- | |
2788 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/cards.pl')). | |
2789 | ||
2790 | test(filesystem, []) :- | |
2791 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/filesystem.pl')). | |
2792 | ||
2793 | test(filesystem2, []) :- | |
2794 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/filesystem2.pl')). | |
2795 | ||
2796 | test(self_grandpas, []) :- | |
2797 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/selfgrandpas.pl')). | |
2798 | ||
2799 | test(queens, []) :- | |
2800 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/queens.pl')). | |
2801 | ||
2802 | test(queens2, []) :- | |
2803 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/queens2.pl')). | |
2804 | ||
2805 | test(queens3, []) :- | |
2806 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/queens3.pl')). | |
2807 | ||
2808 | test(queens4, []) :- | |
2809 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/queens4.pl')). | |
2810 | ||
2811 | test(river_crossing, []) :- | |
2812 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/rivercrossingpuzzle.pl')). | |
2813 | ||
2814 | test(river_crossing_v2, []) :- | |
2815 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/rivercrossingpuzzle_v2.pl')). | |
2816 | ||
2817 | test(enum_test, []) :- | |
2818 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/enumtest.pl')). | |
2819 | ||
2820 | test(disj_field_test, []) :- | |
2821 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/disjfieldtest.pl')). | |
2822 | ||
2823 | test(crewAlloc, []) :- | |
2824 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/crewalloc.pl')). | |
2825 | ||
2826 | test(http, []) :- | |
2827 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/http.pl')). | |
2828 | ||
2829 | test(knights, []) :- | |
2830 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/knights_and_knaves.pl')). | |
2831 | ||
2832 | test(join_binary, []) :- | |
2833 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/joinbinary.pl')). | |
2834 | ||
2835 | test(slot_data_v2, []) :- | |
2836 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/slot_data_v2.pl')). | |
2837 | ||
2838 | test(job_puzzle, []) :- | |
2839 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/job_puzzle.pl')). | |
2840 | ||
2841 | test(natural, []) :- | |
2842 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/natural.pl')). | |
2843 | ||
2844 | test(mutex,[]) :- | |
2845 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/mutex.pl')). | |
2846 | ||
2847 | test(family_v1,[]) :- | |
2848 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/family-v1.pl')). | |
2849 | ||
2850 | test(unary_ordering_rewriting, []) :- | |
2851 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/unary_ordering_rewriting.pl')). | |
2852 | ||
2853 | test(einstein1, []) :- | |
2854 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/einstein1.pl')). | |
2855 | ||
2856 | test(einstein2, []) :- | |
2857 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/einstein2.pl')). | |
2858 | ||
2859 | test(einstein3, []) :- | |
2860 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/einstein3.pl')). | |
2861 | ||
2862 | test(einsteins_enum, []) :- | |
2863 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/einsteins_enum.pl')). | |
2864 | ||
2865 | test(nodeweightedgraph, []) :- | |
2866 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/nodeweightedgraph.pl')). | |
2867 | ||
2868 | test(lists, []) :- | |
2869 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/lists.pl')). | |
2870 | ||
2871 | test(peano, []) :- | |
2872 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/peano.pl')). | |
2873 | ||
2874 | test(random_games, []) :- | |
2875 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/random_games.pl')). | |
2876 | ||
2877 | test(ordering_test, []) :- | |
2878 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/ordering_test.pl')). | |
2879 | ||
2880 | test(rellaws, []) :- | |
2881 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/rellaws.pl')). | |
2882 | ||
2883 | test(color_australia, []) :- | |
2884 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/color_australia.pl')). | |
2885 | ||
2886 | test(add_addr_correct, []) :- | |
2887 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/add_addr_correct.pl')). | |
2888 | ||
2889 | test(ambiguous_field_name, []) :- | |
2890 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/ambiguous_field_name.pl')). | |
2891 | ||
2892 | test(ambiguous_quantifier_field_name, []) :- | |
2893 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/ambiguous_quantifier_field_name.pl')). | |
2894 | ||
2895 | test(arithmetic, []) :- | |
2896 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/arithmetic.pl')). | |
2897 | ||
2898 | test(card, []) :- | |
2899 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/card.pl')). | |
2900 | ||
2901 | test(binary_differently_typed, []) :- | |
2902 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/binary_differently_typed.pl')). | |
2903 | ||
2904 | test(extendedfilesystem, []) :- | |
2905 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/extendedfilesystem.pl')). | |
2906 | ||
2907 | test(generated200, []) :- | |
2908 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/generated200.pl')). | |
2909 | ||
2910 | test(generated400, []) :- | |
2911 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/generated400.pl')). | |
2912 | ||
2913 | test(proofexample, []) :- | |
2914 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/proofexample.pl')). | |
2915 | ||
2916 | test(quantifiers, []) :- | |
2917 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/quantifiers.pl')). | |
2918 | ||
2919 | test(multiplicities, []) :- | |
2920 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/multiplicities.pl')). | |
2921 | ||
2922 | test(joinbinarycomplex, []) :- | |
2923 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/joinbinarycomplex.pl')). | |
2924 | ||
2925 | test(relations, []) :- | |
2926 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/relations.pl')). | |
2927 | ||
2928 | test(restrictions, []) :- | |
2929 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/restrictions.pl')). | |
2930 | ||
2931 | test(utilbool, []) :- | |
2932 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/utilbool.pl')). | |
2933 | ||
2934 | test(util_integer, []) :- | |
2935 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/util_integer.pl')). | |
2936 | ||
2937 | test(modulea, []) :- | |
2938 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/modulea.pl')). | |
2939 | ||
2940 | test(delundoesadd, []) :- | |
2941 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/delundoesadd.pl')). | |
2942 | ||
2943 | test(birthday, []) :- | |
2944 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/birthday.pl')). | |
2945 | ||
2946 | test(trivial_no_solution, []) :- | |
2947 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/trivial_no_solution.pl')). | |
2948 | ||
2949 | test(sets1, []) :- | |
2950 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/sets1.pl')). | |
2951 | ||
2952 | test(sets2, []) :- | |
2953 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/sets2.pl')). | |
2954 | ||
2955 | test(address_book, []) :- | |
2956 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/address_book.pl')). | |
2957 | ||
2958 | test(abstract_memory, []) :- | |
2959 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/abstract_memory.pl')). | |
2960 | ||
2961 | test(cache_memory, []) :- | |
2962 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/cache_memory.pl')). | |
2963 | ||
2964 | test(address_book_3a, []) :- | |
2965 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/address_book_3a.pl')). | |
2966 | ||
2967 | test(overlapping_ranges, []) :- | |
2968 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/overlapping_ranges.pl')). | |
2969 | ||
2970 | test(origin_tracking, []) :- | |
2971 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/origin_tracking.pl')). | |
2972 | ||
2973 | test(java_types, []) :- | |
2974 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/java_types.pl')). | |
2975 | ||
2976 | test(railway, []) :- | |
2977 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/railway.pl')). | |
2978 | ||
2979 | test(syllogism, []) :- | |
2980 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/syllogism.pl')). | |
2981 | ||
2982 | test(chord, []) :- | |
2983 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/chord.pl')). | |
2984 | ||
2985 | test(chord2, []) :- | |
2986 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/chord2.pl')). | |
2987 | ||
2988 | test(chord_bug_model, []) :- | |
2989 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/chord_bug_model.pl')). | |
2990 | ||
2991 | test(mark_sweep_gc, []) :- | |
2992 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/mark_sweep_gc.pl')). | |
2993 | ||
2994 | test(dijkstra_2_process, []) :- | |
2995 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/dijkstra_2_process.pl')). | |
2996 | ||
2997 | test(peterson, []) :- | |
2998 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/peterson.pl')). | |
2999 | ||
3000 | test(genealogy, []) :- | |
3001 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/genealogy.pl')). | |
3002 | ||
3003 | % wrong result for handshake, properties prevent finding a solution, probably caused by translation of 'one sig .. extends Person' | |
3004 | test(handshake, []) :- | |
3005 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/handshake.pl')). | |
3006 | ||
3007 | % error when setting up constants; argument of MU expression has more than one element | |
3008 | test(send_money, []) :- | |
3009 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/send_money.pl')). | |
3010 | % same as for send_money | |
3011 | test(four_bit_adder, []) :- | |
3012 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/4_bit_adder.pl')). | |
3013 | ||
3014 | test(farmer, []) :- | |
3015 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/farmer.pl')). | |
3016 | ||
3017 | test(ins, []) :- | |
3018 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/ins.pl')). | |
3019 | ||
3020 | test(ring_election1, []) :- | |
3021 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/ring_election1.pl')). | |
3022 | ||
3023 | test(ring_election2, []) :- | |
3024 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/ring_election2.pl')). | |
3025 | ||
3026 | test(com, []) :- | |
3027 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/com.pl')). | |
3028 | ||
3029 | test(paragraph_numbering, []) :- | |
3030 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/paragraph_numbering.pl')). | |
3031 | ||
3032 | %test(mark_sweep_gc2, []) :- | |
3033 | % load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/marksweepgc2.pl')). | |
3034 | ||
3035 | :- end_tests(full_machine_translation). | |
3036 | ||
3037 | :- endif. | |
3038 | % errors | |
3039 | /* | |
3040 | % translation failed for bijection_relation | |
3041 | test(ins_label, []) :- | |
3042 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/ins_label.pl')). | |
3043 | ||
3044 | % unknown identifier X in field while X_Sig exists | |
3045 | test(java_map, []) :- | |
3046 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/java_map.pl')). | |
3047 | % similar as above | |
3048 | test(firewire, []) :- | |
3049 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/firewire.pl')). | |
3050 | ||
3051 | % problems with ordering/next | |
3052 | test(view_backing, []) :- | |
3053 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/view_backing.pl')). | |
3054 | % similar as above | |
3055 | test(prisoners, []) :- | |
3056 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/prisoners.pl')). | |
3057 | ||
3058 | % missing translation for integer_sum | |
3059 | test(messaging, []) :- | |
3060 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/messaging.pl')). | |
3061 | ||
3062 | % Unknown identifier "this" in properties | |
3063 | test(hotel, []) :- | |
3064 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/hotel.pl')). | |
3065 | ||
3066 | test(p300_hotel, []) :- | |
3067 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/p300_hotel.pl')). | |
3068 | ||
3069 | test(flip_flop_state, []) :- | |
3070 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/flip_flop_state.pl')). | |
3071 | ||
3072 | test(reset_flip_flop_with_enable, []) :- | |
3073 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/reset_flip_flop_with_enable.pl')). | |
3074 | ||
3075 | % problem new remove this? singleton set problem with this | |
3076 | % but also uses unsupported module util/graph | |
3077 | test(dijkstra_k_state, []) :- | |
3078 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/dijkstra_k_state.pl')). | |
3079 | ||
3080 | % type errors, e.g., for bool'Not | |
3081 | test(ring_orientation, []) :- | |
3082 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/ring_orientation.pl')). | |
3083 | ||
3084 | %! An error occurred (source: type_error) ! | |
3085 | %! Type mismatch: Expected (((POW(Board)*INTEGER)*INTEGER)<->Queen), but was ((POW(Board)*INTEGER)<->(INTEGER*Queen)) in 'queens_Board' | |
3086 | %! Line: 7 Column: 16 until Line: 7 Column: 16 in file: alloytranslation | |
3087 | test(eight_queens, []) :- | |
3088 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/8_queens.pl')). | |
3089 | ||
3090 | % Could not infer type of _universe0 | |
3091 | test(java_types_soundness, []) :- | |
3092 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/java_types_soundness.pl')). | |
3093 | ||
3094 | test(sudoku1, []) :- | |
3095 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/sudoku1.pl')). | |
3096 | ||
3097 | test(sequence_test, []) :- | |
3098 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/sequence_test.pl')). | |
3099 | ||
3100 | test(seq_preconditions, []) :- | |
3101 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/seq_preconditions.pl')). | |
3102 | ||
3103 | test(hanoi, []) :- | |
3104 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/hanoi.pl')). | |
3105 | ||
3106 | test(directctrl,[]) :- | |
3107 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/directctrl.pl')). | |
3108 | ||
3109 | test(views,[]) :- | |
3110 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/views.pl')). | |
3111 | ||
3112 | % unsupported iden keyword in "in" | |
3113 | test(graphiso, []) :- | |
3114 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/graphiso.pl')). | |
3115 | ||
3116 | test(basic_auth, []) :- | |
3117 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/basic_auth.pl')). | |
3118 | ||
3119 | % Unknown identifier "univ" | |
3120 | test(properties, []) :- | |
3121 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/properties.pl')). | |
3122 | ||
3123 | % can probably not be translated due to univ | |
3124 | test(iolus, []) :- | |
3125 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/iolus.pl')). | |
3126 | test(distribution, []) :- | |
3127 | load_alloy_ast_prolog_file(prob_examples('public_examples/Alloy/Prolog/distribution.pl')). | |
3128 | */ |