1 % Heinrich Heine Universitaet Duesseldorf
2 % (c) 2021-2026 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(visb_visualiser,[load_visb_file/1,
6 load_visb_definitions_from_list_of_facts/2,
7 load_visb_definitions_from_term/2,
8 load_visb_file_if_necessary/1,
9 visb_file_is_loaded/1, visb_file_is_loaded/3,
10 visb_current_state_can_be_visualised/0, visb_state_can_be_visualised/1,
11 get_default_visb_file/2, extended_static_check_default_visb_file/0,
12 load_default_visb_file_if_necessary/0,
13 tcltk_get_visb_items/1, tcltk_get_visb_events/1,
14 tcltk_get_visb_objects/1, tcltk_get_visb_hovers/1,
15 get_visb_items/1, get_visb_attributes_for_state/2,
16 get_visb_click_events/1, get_visb_hovers/1,
17 perform_visb_click_event/4,
18 tcltk_perform_visb_click_event/1,
19 get_visb_svg_objects/1,
20 % used by visb_html_export:
21 get_change_attribute_for_state/4, get_and_eval_special_definition/6,
22 evaluate_visb_formula/6, b_value_to_string/2, add_visb_lint_message/3,
23 no_svg_available/0, match_operation/2,
24 visb_file_loaded/4, visb_svg_file/5, visb_empty_svg_box_height_width/3,
25 visb_svg_object/5, visb_svg_child/2, visb_svg_parent/2,
26 visb_svg_child_of_object_from_svg_file/1,
27 visb_has_hovers/1, visb_hover/6]).
28
29 :- use_module(probsrc(module_information),[module_info/2]).
30 :- module_info(group,visualization).
31 :- module_info(description,'This module provides VisB visualisation functionality.').
32
33 % special SVG object attributes treated by VisB
34 % title (creates hover tooltip; currently only works with objects created by VisB itself)
35 % id
36 % svg_class
37 % text
38 % hovers
39 % event, events
40 % predicate
41
42 :- meta_predicate process_repeat(-,-,-,2).
43
44 :- use_module(extrasrc(json_parser), [json_parse_file/3]).
45 :- use_module(library(lists)).
46 :- use_module(probsrc(error_manager)).
47 :- use_module(probsrc(preferences), [get_preference/2, reset_temporary_preference/2,
48 temporary_set_preference/3]).
49 :- use_module(probsrc(debug)).
50 :- use_module(probsrc(state_space), [visited_expression/2, get_constants_id_for_state_id/2,
51 get_constants_state_id_for_id/2, set_context_state/2,
52 clear_context_state/0, transition/3, transition/4,
53 multiple_concrete_constants_exist/0,
54 root_sufficiently_explored/0,
55 is_concrete_constants_state_id/1, current_state_id/1,
56 current_state_corresponds_to_setup_constants_b_machine/0,
57 visited_state_corresponds_to_setup_constants_b_machine/1,
58 time_out_for_node/3, max_reached_for_node/1]).
59 :- use_module(probsrc(translate), [translate_bvalue_to_codes/2,
60 translate_bvalue_to_codes_with_limit/3,
61 translate_bvalue_with_limit/3, pretty_type/2]).
62 :- use_module(probsrc(specfile), [eventb_mode/0, xtl_mode/0,
63 state_corresponds_to_initialised_b_machine/2,
64 state_corresponds_to_set_up_constants_only/2,
65 translate_operation_name/2, classical_b_mode/0, b_or_z_mode/0,
66 csp_with_bz_mode/0]).
67 :- use_module(probsrc(tools), [start_ms_timer/1, stop_ms_walltimer_with_msg/2, ajoin_with_sep/3,
68 split_list/4, get_filename_extension/2, split_chars/3,
69 safe_number_codes/2, safe_atom_codes/2]).
70 :- use_module(probsrc(bmachine), [b_machine_name/1, b_get_definition_name_with_pos/4,
71 bmachine_is_precompiled/0, pre_expand_typing_scope/2,
72 determine_type_of_formula/2, determine_type_of_formula/3,
73 b_load_additional_definitions_file/1,
74 b_load_additional_definitions_from_list_of_facts/1,
75 b_load_additional_definitions_from_term/1,
76 set_additional_filename_as_parsing_default/3,
77 reset_filename_parsing_default/2,
78 b_sorted_b_definition_prefixed/4, b_get_typed_definition/3,
79 b_is_operation_name/1,
80 b_parse_machine_operation_pre_post_predicate/5]).
81 :- use_module(probsrc(bsyntaxtree), [get_texpr_ids/2, get_texpr_pos/2, create_couple/2,
82 create_couple/3, replace_ids_by_exprs/4, create_equality/3,
83 get_texpr_type/2, find_identifier_uses/3,
84 conjunct_predicates/2, map_over_bexpr/2, get_texpr_id/2]).
85 :- use_module(probsrc(tools_lists), [include_maplist/3]).
86 :- use_module(probsrc(tools_matching), [get_all_svg_attributes/1, is_svg_attribute/1,
87 is_virtual_svg_attribute/1, is_svg_number_attribute/2,
88 is_svg_color_attribute/1, svg_attribute_default_value/2,
89 get_possible_fuzzy_matches_and_completions_msg/3,
90 get_all_svg_classes/1, is_svg_shape_class/1, is_html_tag/1,
91 is_html_attribute/1, dotshape2svg_class/2,
92 fix_svg_attribute/2, is_dot_attribute/1]).
93 :- use_module(visb_json_utils, [get_attr/3, get_attr_with_pos/5,
94 del_attr/4, del_attr_with_pos/6, non_det_del_attr_with_pos/6,
95 force_del_attr/5, force_del_attr_with_pos/6,
96 get_attr_true/3, is_optional_json_item/1, is_optional_json_item/3,
97 check_json_string/4,
98 get_pos_from_list/3, construct_prob_pos_term/3,
99 replace_in_json/4, multi_replace_in_json/4,
100 check_all_same_length_json/3]).
101 :- use_module(visb_static_check, [static_check_dynamic_update_def_body/1,
102 static_check_attribute_value_expression/2,
103 check_svg_attribute_value/3]).
104 :- use_module(probsrc(eventhandling), [register_event_listener/3]).
105 :- use_module(probsrc(pref_definitions), [b_get_definition_string_from_spec/3]).
106 :- use_module(library(file_systems), [file_exists/1, file_property/3]).
107 :- use_module(probsrc(tools_strings), [ajoin/2, safe_name/2, atom_prefix/2,
108 is_simple_classical_b_identifier_codes/1]).
109 :- use_module(probsrc(custom_explicit_sets), [try_expand_custom_set_with_catch/3,
110 is_custom_explicit_set/1, is_set_value/2,
111 singleton_set/2]).
112 :- use_module(probsrc(typing_tools), [any_value_for_type/2]).
113 :- use_module(extrasrc(external_functions_svg), [svg_points/4]).
114 :- use_module(probsrc(store), [lookup_value_for_existing_id/3]).
115 :- use_module(probsrc(gensym), [gensym/2]).
116 :- use_module(probsrc(self_check)).
117 :- use_module(probsrc(external_functions), [external_function_requires_state/1]).
118 :- use_module(probsrc(kernel_reals), [is_real/2]).
119 :- use_module(probsrc(b_global_sets), [add_prob_deferred_set_elements_to_store/3]).
120 :- use_module(probsrc(b_interpreter), [b_compute_expression_nowf/7,
121 b_compute_explicit_epression_no_wf/7]).
122
123
124 % --------------------------
125
126 % facts storing loaded JSON VisB file:
127 :- dynamic visb_file_loaded/4, visb_svg_file/5, visb_empty_svg_box_height_width/3,
128 visb_definition/6, visb_special_definition/6,
129 visb_item/7,
130 visb_event/7, visb_hover/6, visb_has_hovers/1, visb_has_visibility_hover/1,
131 visb_event_enable_list/5,
132 visb_svg_object/5, visb_svg_object_debug_info/2,
133 visb_svg_child/2, visb_svg_parent/2, % register parent/child relationships for groups, title, ...
134 visb_svg_child_of_object_from_svg_file/1. % true if parent of object is an SVG in an external file
135 :- dynamic auxiliary_visb_event/6.
136 % visb_svg_file(SVGFile, AbsolutePathSVGFile, JSONFileFromWhichWeImportSVG,PosTerm,ModLocTime)
137 % visb_item(SVGID,Attribute,TypedExpression,UsedIds,Description/Comment,StartPos,OtherMetaInfos)
138 % visb_event(SVGID,Event,PredicateList,ExecutionKind,TypedPredicate,File,Pos) ; ExecutionKind: default, first, random or heuristic
139 % visb_svg_object(SVGID,SVG_Class,AttrList,Description/Comment,Pos) ; AttrList contains svg_attribute(Attr,Val)
140
141 reset_visb :- debug_println(9,resetting_visb),
142 retractall(visb_file_loaded(_,_,_,_)),
143 retractall(visb_empty_svg_box_height_width(_,_,_)),
144 retractall(visb_svg_file(_,_,_,_,_)),
145 retractall(visb_definition(_,_,_,_,_,_)),
146 retractall(visb_special_definition(_,_,_,_,_,_)),
147 retractall(visb_item(_,_,_,_,_,_,_)),
148 retractall(visb_event(_,_,_,_,_,_,_)),
149 retractall(visb_hover(_,_,_,_,_,_)),
150 retractall(visb_has_hovers(_)),
151 retractall(visb_has_visibility_hover(_)),
152 retractall(visb_event_enable_list(_,_,_,_,_)),
153 retractall(auxiliary_visb_event(_,_,_,_,_,_)),
154 retractall(visb_svg_object(_,_,_,_,_)),
155 retractall(visb_svg_object_debug_info(_,_)),
156 retractall(visb_svg_child(_,_)),
157 retractall(visb_svg_child_of_object_from_svg_file(_)),
158 retractall(visb_svg_parent(_,_)),
159 reset_auto_attrs.
160
161 visb_file_is_loaded(JSONFile) :-
162 visb_file_is_loaded(JSONFile,_,true).
163 visb_file_is_loaded(JSONFile,SVGFile,AllowEmpty) :-
164 visb_file_loaded(JSONFile,_,_,_),
165 (visb_svg_file(_,SVGFile,_,_,_) -> true
166 ; visb_empty_svg_box_height_width(_,_,_) -> SVGFile = ''
167 ; user_has_defined_visb_objects -> SVGFile = ''
168 ; JSONFile='' ->
169 AllowEmpty=true,
170 add_warning(visb_visualiser,'No VISB_SVG_FILE or VISB_SVG_OBJECTS specified in DEFINITIONS',''),
171 % Note: warning also generated below No VisB JSON file is specified and no VISB_SVG_OBJECTS were created
172 SVGFile = ''
173 ; add_warning(visb_visualiser,'No SVG file or objects specified for VisB file:',JSONFile),
174 AllowEmpty=true,
175 SVGFile = ''
176 ).
177
178 no_svg_available :-
179 \+ user_has_defined_visb_objects,
180 no_svg_file_available,
181 \+ visb_empty_svg_box_height_width(_,_,_). % no SVG box provided
182
183 no_svg_file_available :- (visb_svg_file(_,SVGFile,_,_,_) -> SVGFile='' ; true).
184 %svg_file_available :- \+ no_svg_file_available.
185
186 visb_state_can_be_visualised(ID) :-
187 (b_or_z_mode -> visited_state_corresponds_to_setup_constants_b_machine(ID)
188 ; ID \= root
189 ).
190 visb_current_state_can_be_visualised :-
191 %visb_file_is_loaded(_), % no longer checked; Tcl/Tk will automatically load VisB file if required
192 current_state_id(ID), visb_state_can_be_visualised(ID).
193
194
195 :- register_event_listener(reset_specification,reset_visb,'Reset VisB information').
196 :- register_event_listener(reset_prob,reset_visb,'Reset VisB information').
197
198 %static_check_visb :- % now done when adding items and not storing multiple items
199 % visb_item(ID,Attr,F1,_,_,Pos1), visb_item(ID,Attr,F2,_,Pos2), F1 @< F2,
200 % add_warning(visb_visualiser,'Multiple formulas for SVG identifier and attribute:',ID/Attr,Pos1),
201 % add_message(visb_visualiser,'Location of second formula for:',ID/Attr,Pos2),
202 % fail.
203 % TO DO: some sanity checks on attributes and values
204 static_check_visb :-
205 no_svg_file_available,
206 ? visb_hover(SVGID,_ID,Attr,_Enter,_Exit,Pos),
207 \+ svg_id_exists(SVGID),
208 ajoin(['Hover for attribute ',Attr,' has unknown trigger-id (can lead to blank visualisation): '],Msg),
209 add_warning(visb_visualiser,Msg,SVGID,Pos),
210 fail.
211 static_check_visb :-
212 no_svg_file_available,
213 ? visb_hover(SVGID,ID,Attr,_Enter,_Exit,Pos), ID \= SVGID,
214 \+ svg_id_exists(ID),
215 ajoin(['Hover for attribute ',Attr,' has unknown id (can lead to blank visualisation): '],Msg),
216 add_warning(visb_visualiser,Msg,ID,Pos),
217 fail.
218 static_check_visb :-
219 visb_svg_file(SvgFile,_,_,Pos,_),
220 SvgFile \= '',
221 visb_empty_svg_box_height_width(H,W,ViewBox),
222 ajoin(['VISB_SVG_BOX (width=', W, ', height=', H, ', viewBox=', ViewBox,
223 ') cannot be applied to an existing SVG file (use VISB_SVG_CONTENTS for file or put info into SVG file): '],Msg),
224 add_warning(visb_visualiser,Msg,SvgFile,Pos),
225 fail.
226 static_check_visb.
227
228 svg_id_exists(SVGID) :- visb_svg_object(SVGID,_,_StaticAttrList,_,_ExitPos).
229 svg_id_has_class(SVGID,Class) :- visb_svg_object(SVGID,Class,_StaticAttrList,_,_ExitPos).
230 % --------------------------
231
232
233 get_default_visb_file(Path, Pos) :- get_default_visb_file(Path, _, Pos).
234 get_default_visb_file(Path, Kind, Pos) :- bmachine_is_precompiled,
235 (b_get_definition_string_from_spec('VISB_JSON_FILE', Pos, Path)
236 -> Kind=json % user provided explicit path to VISB JSON file
237 ; b_get_definition_string_from_spec('VISB_DEFINITIONS_FILE', Pos, Path)
238 -> Kind=def % user provided explicit path to VISB DEFINITIONS file
239 % TODO: in future check that file extension is compatible
240 % advantage of VISB_DEFINITIONS_FILE == "File" over "File"
241 % 1) visualisation can be re-loaded without re-loading machine
242 % 2) can be used in TLA+ or similar
243 % 3) avoid clash if definitions file itself defines empty VISB_JSON_FILE
244 % 4) avoid problems with Atelier-B or other tools
245 ; get_default_visb_svg_file(_,Pos)
246 -> Kind=empty, Path='' % user provided SVG file path
247 ; user_has_defined_visb_objects_in_defs
248 -> Kind=empty, Path='' % user provided SVG object definitions
249 ).
250
251 user_has_defined_visb_objects :-
252 ? (visb_svg_object(_,_,_,_,_) -> true % created in JSON file
253 ; user_has_defined_visb_objects_in_defs).
254
255 user_has_defined_visb_objects_in_defs :-
256 b_sorted_b_definition_prefixed(expression,'VISB_SVG_OBJECTS',_,_).
257
258 % you should use default_visb_svg_abs_file to obtain the absolute path
259 get_default_visb_svg_file(Path, Pos) :-
260 b_get_definition_string_from_spec('VISB_SVG_FILE', Pos, Path).
261
262 % the VISB_SVG_FILE is relative to the main B machine, also when a JSON file is used
263 default_visb_svg_abs_file('',AbsFile) :- !, AbsFile=''. % just dummy empty string provided by user
264 default_visb_svg_abs_file(SvgFile,AbsFile) :-
265 b_absolute_file_name_relative_to_main_machine(SvgFile,AbsFile).
266
267
268 % load default VISB_JSON_FILE if it is specified and not already loaded
269 load_default_visb_file_if_necessary :-
270 get_default_visb_file(File, Pos),!,
271 (load_visb_file_if_necessary(File) -> true
272 ; add_error(visb_visualiser,'Could not load VISB_JSON_FILE:',File,Pos)).
273 load_default_visb_file_if_necessary :-
274 (eventb_mode -> true ; add_message(visb_visualiser,'No VISB_JSON_FILE DEFINITION provided')).
275
276 % --------------------------
277 % Loading a JSON VisB file:
278
279
280 load_visb_file(File) :-
281 temporary_set_preference(allow_arith_operators_on_reals,true,Old),
282 call_cleanup((load_visb_file1(File)),
283 reset_temporary_preference(allow_arith_operators_on_reals,Old)).
284 load_visb_file1(File) :-
285 get_filename_extension(File,def), % a .def B definition file
286 !,
287 add_message(visb_visualiser,'Loading VisB DEFINITIONS from B file: ',File),
288 b_load_additional_definitions_file(File),
289 load_visb_file2('',File).
290 load_visb_file1(File) :- load_visb_file2(File,File).
291
292 load_visb_definitions_from_list_of_facts(DefFilePath, ListOfFacts) :-
293 temporary_set_preference(allow_arith_operators_on_reals,true,Old),
294 call_cleanup(
295 (b_load_additional_definitions_from_list_of_facts(ListOfFacts),
296 load_visb_file2('', DefFilePath)),
297 reset_temporary_preference(allow_arith_operators_on_reals,Old)).
298
299 load_visb_definitions_from_term(DefFilePath, Machine) :-
300 temporary_set_preference(allow_arith_operators_on_reals,true,Old),
301 call_cleanup(
302 (b_load_additional_definitions_from_term(Machine),
303 load_visb_file2('', DefFilePath)),
304 reset_temporary_preference(allow_arith_operators_on_reals,Old)).
305
306 load_visb_file2(JsonFile,OrigFileName) :-
307 reset_visb,
308 load_visb_from_definitions(no_inlining,JsonFile), % TODO: pass InlineObjects?
309 start_ms_timer(T),
310 (JsonFile='' % No JSON file
311 -> (get_default_visb_svg_file(SvgFile,DefaultSVGPos)
312 -> default_visb_svg_abs_file(SvgFile,AbsFile),
313 add_visb_svg_file(SvgFile,AbsFile,'',DefaultSVGPos)
314 ; check_objects_created(JsonFile) % no JSON and SVG file; check we have created objects
315 )
316 ; add_visb_file(JsonFile,[]),
317 stop_ms_walltimer_with_msg(T,'loading VisB JSON file: ')
318 ),
319 static_check_visb,
320 (OrigFileName = ''
321 -> ModTime=0, ModLocTime=0 % We could get modification time of files with VISB_SVG_OBJECTS, ... DEFINITIONS
322 ; file_property(OrigFileName, modify_timestamp, ModTime),
323 file_property(OrigFileName, modify_localtime, ModLocTime)
324 ),
325 constants_status(CS),
326 assertz(visb_file_loaded(OrigFileName,ModTime,ModLocTime,CS)).
327
328 % mainly useful for CSP||B where concrete_constants not directly available upon loading machine
329 % TODO: we could compute the number of constant values available and store this (e.g., when MAX_INITIALISATIONS is 0)
330 constants_status(X) :- root_sufficiently_explored,!, X=available.
331 constants_status(not_available).
332
333 check_objects_created(File) :-
334 ? (visb_svg_object(_,_,_,_,_) -> true
335 ; add_warning(visb_visualiser,'No VisB JSON file is specified and no VISB_SVG_OBJECTS were created:',File)
336 ).
337
338 load_visb_from_definitions(InlineObjects,JsonFileContext) :-
339 start_ms_timer(T),
340 pre_expand_typing_scope([variables_and_additional_defs],ExpandedScope), % do this only once for all defs
341 get_svg_objects_from_definitions(InlineObjects,ExpandedScope,JsonFileContext),
342 (get_SVG_BOX_definition(InlineObjects) -> true ; true), % TODO: also pass scope
343 stop_ms_walltimer_with_msg(T,'extracting VisB infos from DEFINITIONS: ').
344
345
346 %:- use_module(library(system),[now/1, datime/2]).
347 load_visb_file_if_necessary(File) :-
348 visb_file_loaded(File,ModTime1,_,ConstantsStatus), % this file is already loaded
349 constants_status(ConstantsStatus), % if constants status has changed we need to re-load visualisation
350 (File = '' -> true
351 ; file_exists(File),
352 file_property(File, modify_timestamp, ModTime2), % check if it has the same time stamp
353 debug_println(19,visb_json_already_loaded(File,ModTime1,ModTime2)),
354 ModTime1=ModTime2
355 ),
356 !.
357 load_visb_file_if_necessary(File) :- constants_status(not_available),!,
358 add_message(visb_visualiser,'Not loading VisB file no constants set up yet: ',File).
359 load_visb_file_if_necessary(File) :- load_visb_file(File).
360
361
362 add_visb_file(File,LoadedFiles) :- member(File,LoadedFiles),!,
363 add_error(visb_visualiser,'Circular inclusion of JSON files:',[File|LoadedFiles]).
364 add_visb_file(File,_) :- \+ file_exists(File),!,
365 add_error(visb_visualiser,'JSON file does not exist:',File), fail.
366 add_visb_file(File,LoadedFiles) :-
367 debug_format(19,'Loading JSON File: ~w~n',[File]),
368 set_additional_filename_as_parsing_default(File,NewNr,OldNr),
369 call_cleanup(add_visb_file2(File,LoadedFiles), reset_filename_parsing_default(NewNr,OldNr)).
370
371 add_visb_file2(File,LoadedFiles) :-
372 json_parse_file(File,json(List2),[position_infos(true),multiline_strings(true)]),
373 % tools_printing:trace_print(List2),nl,nl,
374 process_json(List2,File),
375 !,
376 (get_attr_with_pos(include,List2,JsonFileTerm,File,Pos)
377 -> check_json_string(JsonFileTerm,include,Pos,JsonFile),
378 absolute_file_name(JsonFile,AF,[relative_to(File)]),
379 formatsilent('Including JSON file: ~w~n',[JsonFile]),
380 absolute_file_name(File,AbsFile), % File may be relative; we compare absolute paths to detect cycles
381 add_visb_file(AF,[AbsFile|LoadedFiles])
382 ; true
383 ),
384 (LoadedFiles=[], % only check for main included file
385 ? get_attr_with_pos('model-name',List2,string(ModelName),File,MPos),
386 b_machine_name(Main), Main \= ModelName
387 -> ajoin(['VisB JSON file expects model-name ',ModelName,' instead of: '],MMsg),
388 add_warning(visb_visualiser,MMsg,Main,MPos)
389 ; true
390 ).
391 add_visb_file2(File,_) :-
392 add_error(visb_visualiser,'Unable to process JSON file:',File),
393 fail.
394
395 % process json content of a VisB file:
396 process_json(List,JSONFile) :- visb_svg_file(_,_,_,_,_),!, % we have already determined the SVG file
397 process_json2(List,JSONFile).
398 process_json(List,JSONFile) :-
399 ? get_attr_with_pos(svg_box,List,json(BoxList),JSONFile,Pos),
400 % json([=(width,400,3-3),=(height,400,3-3)])
401 ? (get_attr(svg,List,string(SvgFile)) -> SvgFile='' ; true), % check if svg attr empty or non-existant
402 force_get_attr_nr(height,BoxList,H,JSONFile),
403 force_get_attr_nr(width,BoxList,W,JSONFile),!,
404 (get_attr(viewBox,BoxList,string(VB)) -> ViewBox=VB ; ViewBox=''),
405 add_message(visb_visualiser,'Will create empty SVG image with specified dimensions: ',H:W,Pos),
406 assert_visb_empty_svg_box_height_width(H,W,ViewBox,Pos),
407 process_json2(List,JSONFile).
408 process_json(List,JSONFile) :-
409 ? get_attr_with_pos(svg,List,FileTerm,JSONFile,Pos),
410 check_json_string(FileTerm,svg,Pos,File),
411 (File = '' -> get_default_visb_svg_file(SvgFile, _),
412 default_visb_svg_abs_file(SvgFile,AbsFile) % VISB_SVG_FILE is relative to the B machine
413 ; get_default_visb_svg_file(SvgFile, DPos)
414 -> add_message(visb_visualiser,'Overriding svg file specified in JSON file: ',File,DPos),
415 default_visb_svg_abs_file(SvgFile,AbsFile)
416 ; SvgFile=File,
417 absolute_file_name(SvgFile,AbsFile,[relative_to(JSONFile)])),
418 !,
419 add_visb_svg_file(SvgFile,AbsFile,JSONFile,Pos),
420 process_json2(List,JSONFile).
421 process_json(List,JSONFile) :-
422 get_default_visb_svg_file(SvgFile, Pos), SvgFile \= '',
423 !,
424 default_visb_svg_abs_file(SvgFile,AbsFile),
425 add_visb_svg_file(SvgFile,AbsFile,JSONFile,Pos),
426 process_json2(List,JSONFile).
427 process_json(List,JSONFile) :-
428 process_json2(List,JSONFile),
429 (get_SVG_BOX_definition(no_inlining) -> true % TODO: pass (InlineObjects)
430 ; get_attr_with_pos(svg,List,_,JSONFile,Pos) -> % File is empty
431 add_message(visb_visualiser,'Creating empty SVG image. You can add a declaration like "svg_box": {"width":W,"height":H} to specify dimensions in: ',JSONFile,Pos)
432 ; add_warning(visb_visualiser,'Creating empty SVG image. The JSON file contains no svg attribute (pointing to the SVG file): ',JSONFile)
433 ).
434
435 add_visb_svg_file(SvgFile,AbsFile,JSONFile,Pos) :-
436 (SvgFile='' -> add_message(visb_visualiser,'Empty VisB SVG file:',SvgFile,Pos),
437 ModLocTime=unknown
438 ; file_exists(AbsFile)
439 -> debug_format(19,'SVG file = ~w (~w)~n',[SvgFile, AbsFile]),
440 file_property(AbsFile, modify_localtime, ModLocTime)
441 ; add_error(visb_visualiser,'The specified VisB SVG file does not exist:',SvgFile,Pos),
442 ModLocTime=unknown
443 ),
444 assertz(visb_svg_file(SvgFile,AbsFile,JSONFile,Pos,ModLocTime)).
445
446 process_json2(List,JSONFile) :-
447 process_json_definitions(List,JSONFile),
448 process_json_items(List,JSONFile),
449 process_json_events(List,JSONFile),
450 process_json_new_svg_objects(List,JSONFile).
451
452
453 % ----------------
454
455 % B definitions that can help write items, events more compactly
456 process_json_definitions(List,File) :-
457 ? get_attr(definitions,List,array(Defs)),!,
458 length(Defs,Len),
459 formatsilent('VisB Definitions: ~w~n',[Len]),
460 maplist(process_json_definition(File),Defs).
461 process_json_definitions(_,_File).
462
463 % VisB items which modify attributes of SVG objects
464 process_json_items(List,File) :-
465 ? get_attr(items,List,array(Items)),!,
466 length(Items,Len),
467 formatsilent('VisB Item declarations: ~w~n',[Len]),
468 maplist(process_visb_json_item(File),Items).
469 process_json_items(_,File) :- \+ visb_special_definition(visb_updates,_,_,_,_,_),!,
470 add_message(visb_visualiser,'The JSON file contains no items: ',File).
471 process_json_items(_,_).
472
473 % VisB events which react to clicks on SVG objects and display hovers
474 process_json_events(List,File) :-
475 get_attr(events,List,array(Items)),!,
476 length(Items,Len),
477 formatsilent('VisB Event declarations: ~w~n',[Len]),
478 maplist(process_json_event(File),Items).
479 process_json_events(_,_).
480
481 % VisB additional SVG objects added to SVG file
482 process_json_new_svg_objects(List,File) :-
483 ? get_attr(svg_objects,List,array(Items)),!,
484 length(Items,Len),
485 formatsilent('VisB additional svg_objects declarations: ~w~n',[Len]),
486 maplist(process_json_svg_object(File),Items).
487 process_json_new_svg_objects(List,File) :-
488 get_attr_with_pos(objects,List,array(Items),File,Pos),!,
489 add_warning(visb_visualiser,'Use "svg_objects" attribute instead of "objects" in: ',File,Pos),
490 maplist(process_json_svg_object(File),Items).
491 process_json_new_svg_objects(_,_).
492
493
494 % evaluate all VISB_SVG_OBJECTS... DEFINITIONS and try and extract SVG objects
495 % this must be a set of records with at least id and svg_class field
496 get_svg_objects_from_definitions(InlineObjects,ExpandedScope,_) :-
497 ? find_and_eval_visb_DEFINITION('VISB_SVG_OBJECTS', visb_objects, SVG_ID, AttrList, Desc, DefName,
498 _DefPos, EvalPos, InlineObjects, allow_separation, ExpandedScope),
499 %formatsilent('Generating new SVG object ~w with attrs ~w~n',[SVG_ID,AttrList]),
500 add_visb_object_from_definition(visb_objects, SVG_ID, AttrList, Desc, DefName, EvalPos),
501 fail.
502 get_svg_objects_from_definitions(InlineObjects,ExpandedScope,_) :-
503 find_and_eval_visb_DEFINITION('VISB_SVG_EVENTS', visb_events, SVG_ID, AttrList, Desc, DefName,
504 _DefPos, EvalPos, InlineObjects, no_separation, ExpandedScope),
505 % attributes: event or events, predicate
506 add_visb_object_from_definition(visb_events, SVG_ID, AttrList, Desc, DefName, EvalPos),
507 fail.
508 get_svg_objects_from_definitions(InlineObjects,ExpandedScope,_) :-
509 ? find_and_eval_visb_DEFINITION('VISB_SVG_HOVERS', visb_hovers, SVG_ID, AttrList, Desc, DefName,
510 _DefPos, EvalPos, InlineObjects, no_separation, ExpandedScope),
511 % attributes: id, trigger_id (optional), other SVG attributes
512 ? add_visb_hovers_from_definition(SVG_ID, AttrList, Desc, DefName, EvalPos),
513 fail.
514 get_svg_objects_from_definitions(_InlineObjects,ExpandedScope,JsonFileContext) :- % VISB_SVG_UPDATES
515 precompile_svg_object_updates(ExpandedScope,JsonFileContext).
516
517 % example definition VISB_SVG_HOVERS == rec(`id`:"myid", stroke:"black", stroke_exit:"gray");
518 % a hovers field inside VISB_SVG_OBJECTS can be something like:
519 % rec(.... hovers: (rec(class:"train-hover"),rec(trigger_id:"track_polyline",class:"train-hover")), ...)
520 % also used, e.g., as attribute in VISB_SVG_OBJECTS
521 add_visb_hovers_from_definition(SVG_ID,AttrList, Desc, DefName, DefPos) :-
522 select(svg_attribute(hovers,Hovers),AttrList,AttrList2),
523 !,
524 (AttrList2=[] -> true ; add_warning(visb_visualiser,'Unknown extra hover attributes:',AttrList2,DefPos)),
525 flex_member(Record,Hovers,DefName,DefPos),
526 (get_VISB_record_fields(Record,Fields) -> true
527 ; add_warning(visb_visualiser,'VisB hovers field value is not a record of SVG attributes: ',Record,DefPos),fail),
528 include_maplist(extract_attribute_from_record(DefPos),Fields,HoverAttrList),
529 add_visb_object_from_definition(visb_hovers, SVG_ID, HoverAttrList, Desc, DefName, DefPos).
530 add_visb_hovers_from_definition(SVG_ID,AttrList, Desc, DefName, DefPos) :-
531 ? add_visb_object_from_definition(visb_hovers, SVG_ID, AttrList, Desc, DefName, DefPos).
532
533
534 % add a VisB record object from a HOVER or OBJECT definition:
535 add_visb_object_from_definition(visb_objects,SVG_ID, AttrList, Desc, DefName, DefPos) :- !,
536 ? (select(svg_attribute(svg_class,SVG_Class),AttrList,AttrList2)
537 -> true % maybe we should call svg_class svg_shape and
538 % allow html_tag for children of foreignObject
539 ; add_warning(visb_visualiser,'SVG objects have no svg_class field: ',DefName,DefPos),
540 fail
541 ),
542 assert_visb_svg_object(SVG_ID,SVG_Class,AttrList2,Desc,DefName,DefPos).
543 add_visb_object_from_definition(visb_hovers,SVG_ID, AttrList, _Desc, _DefName, DefPos) :- !,
544 ? (select(svg_attribute(trigger_id,TriggerID),AttrList,AttrList2) -> true
545 ; select(svg_attribute('trigger-id',TriggerID),AttrList,AttrList2) -> true
546 ; TriggerID = SVG_ID, AttrList2=AttrList),
547 ? member(svg_attribute(Attr,EnterVal),AttrList2), % will backtrack for every SVG attribute
548 \+ get_exit_attribute_name(_,Attr), % it is not an exit attribute
549 get_exit_attribute_name(Attr,AttrExitName),
550 (member(svg_attribute(AttrExitName,ExitVal), AttrList2) -> true % user specified exit value explicitly
551 ; visb_svg_object(SVG_ID,_,StaticAttrList,_,ExitPos)
552 -> (member(svg_attribute(Attr,ExitVal),StaticAttrList) -> true % use value specified in VISB_SVG_OBJECTS
553 ; default_attribute_value(Attr,ExitVal)
554 -> add_message(visb_visualiser,'Assuming default hover exit value for attribute: ',Attr,DefPos)
555 ; Attr = 'svg_class'
556 -> add_warning(visb_visualiser,
557 'svg_class cannot be set in hover for: ',SVG_ID,ExitPos),
558 ExitVal=EnterVal
559 ; (Attr = hover ; Attr = hovers) ->
560 add_error(visb_visualiser,
561 'Only provide SVG attributes here; no need to wrap them in this attribute:',Attr,DefPos),fail
562 ; add_warning(visb_visualiser,
563 'No static (exit) value can be retrieved for hover for attribute (be sure to define a static value for this attribute in a VISB_SVG_OBJECT definition): ',Attr,ExitPos),
564 ExitVal=EnterVal
565 )
566 ; ajoin(['No VISB_SVG_OBJECT for id ',SVG_ID,' and no value for ',AttrExitName,
567 '; cannot retrieve hover exit value for attribute: '],Msg),
568 add_warning(visb_visualiser,Msg,Attr,DefPos),
569 fail %ExitVal=EnterVal
570 ),
571 assert_visb_hover(TriggerID,SVG_ID,Attr,EnterVal,ExitVal,DefPos).
572 add_visb_object_from_definition(visb_events,SVG_ID, AttrList, _Desc, DefName, DefPos) :- !,
573 ( select(svg_attribute(event,Event),AttrList,AttrList1) -> true
574 ; select(svg_attribute(events,Events),AttrList,AttrList1) -> % we have events attributes with multiple events
575 flex_member(EvVal,Events,DefName,DefPos),
576 extract_attr_value(event,EvVal,Event,DefPos)
577 ; AttrList = [_|_], % at least one other attribute than id; otherwise it may stem from separating VISB_SVG_OBJECTS
578 add_warning(visb_visualiser,'Missing event attribute in VisB definition: ',DefName,DefPos),
579 fail
580 ),
581 (select(svg_attribute(predicate,Pred),AttrList1,AttrList2) -> Preds=[Pred]
582 ; member(P,[preds,predicates]),
583 select(svg_attribute(P,Pred),AttrList1,AttrList2)
584 -> Preds=[Pred], add_warning(visb_visualiser,'Use predicate as attribute for VisB events instead of: ',P,DefPos)
585 ; Preds=[], AttrList2=AttrList1
586 ),
587 (select(svg_attribute(execution_kind,EKind),AttrList2,AttrList3)
588 -> check_execution_kind(EKind,SVG_ID,DefPos,ExecKind)
589 ; default_execution_kind(ExecKind), AttrList3=AttrList2
590 ),
591 EnableItems=[],
592 maplist(warn_remaining_attributes(DefName,DefPos),AttrList3),
593 % TODO extract from AttrList2: visb_enable_item(SvgID,Attr,EnabledValExpr,DisabledValExpr,PosAttr)
594 (error_manager:extract_file_number_and_name(DefPos,_,File) -> true ; File='?'),
595 PredPos = DefPos, %TODO: improve
596 add_visb_event(SVG_ID,Event,Preds,ExecKind,[],EnableItems,File,DefPos,PredPos,[]).
597 add_visb_object_from_definition(SpecialClass,_, _, _, _DefName, DefPos) :-
598 add_warning(visb_visualiser,'Unknown special class, cannot add VisB objects for definition: ',SpecialClass,DefPos).
599
600 % for hovers allow to explicitly specify exit value, in case the SVG object comes from an SVG file
601 get_exit_attribute_name(Attr,ExitAttr) :- atom_concat(Attr,'_exit',ExitAttr).
602
603 warn_remaining_attributes(DefName,DefPos,svg_attribute(hover,_)) :- !,
604 add_warning(visb_visualiser,'Attribute hover not supported here (use separate VISB_SVG_HOVERS definition): ',DefName,DefPos).
605 warn_remaining_attributes(DefName,DefPos,svg_attribute(hovers,_)) :- !,
606 add_warning(visb_visualiser,'Attribute hovers not supported here (use separate VISB_SVG_HOVERS definition): ',DefName,DefPos).
607 warn_remaining_attributes(_,DefPos,Attr) :-
608 add_warning(visb_visualiser,'Unrecognised attribute in VISB_SVG_EVENTS definition: ',Attr,DefPos).
609
610
611 default_attribute_value(fill,black). % is remove for animate, ...
612 default_attribute_value(opacity,1).
613 default_attribute_value('stop-dasharray',none).
614 default_attribute_value('stop-opacity',1).
615 default_attribute_value('stroke-opacity',1).
616 default_attribute_value('stroke-width','1px').
617 default_attribute_value(stroke,none).
618 default_attribute_value(transform,none).
619 default_attribute_value(visibility,visible).
620
621 % is called in process_json when no SVG file specified
622 % VISB_SVG_BOX == rec(width:1000,height:1000,viewBox:"minx miny w h"),
623 get_SVG_BOX_definition(InlineObjects) :-
624 ? get_and_eval_special_definition('VISB_SVG_BOX','VISB_SVG_BOX',visb_box,DefPos,ResValue,InlineObjects),
625 (ResValue=(BH,BW) -> ViewBox=''
626 ; get_VISB_record_fields(ResValue,Fields),
627 ? select(field(height,BH),Fields,F1),
628 ? select(field(width,BW),F1,F2)
629 ? -> (select(field(viewBox,VBVal),F2,F3)
630 -> (VBVal=string(VB) -> ViewBox=VB
631 % Viewbox is "mx my w h" string, ie: minimum_x, minimum_y, width and height
632 ; VBVal = rec(VBFields)
633 -> (VBFields = [field(height,_),field(minx,_),field(miny,_),field(width,_)]
634 -> (get_view_box_rec(VBFields,DefPos,ViewBox) -> true ; ViewBox='') % error already reported
635 ; ViewBox='',
636 add_warning(visb_visualiser,'VISB_SVG_BOX viewBox record has illegal fields (expected minx miny width height):',VBVal,DefPos))
637 ; add_warning(visb_visualiser,'VISB_SVG_BOX viewBox is not a string ("minx miny width height") or record:',VBVal,DefPos),
638 ViewBox=''
639 )
640 ; ViewBox='', F3=F2
641 ),
642 (F3=[] -> true
643 ; add_warning(visb_visualiser,'VISB_SVG_BOX unknown attributes (not height, width, viewBox): ',F3,DefPos))
644 ; add_error(visb_visualiser,'Ignoring VISB_SVG_BOX, must be record or pair of integers (height,width):',ResValue,DefPos),
645 fail),
646 (get_number_from_bvalue(BH,H) -> true
647 ; add_error(visb_visualiser,'Ignoring VISB_SVG_BOX, height is not a number:',ResValue,DefPos), fail),
648 (get_number_from_bvalue(BW,W) -> true
649 ; add_error(visb_visualiser,'Ignoring VISB_SVG_BOX, width is not a number:',ResValue,DefPos), fail),
650 !,
651 assert_visb_empty_svg_box_height_width(H,W,ViewBox,DefPos).
652
653 get_view_box_rec([field(height,VH),field(minx,MXH),field(miny,MYH),field(width,VW)],DefPos,ViewBoxString) :-
654 (get_number_from_bvalue(VH,Height) -> true
655 ; add_error(visb_visualiser,'VISB_SVG_BOX viewBox\'height is not a number:',VH,DefPos),fail),
656 (get_number_from_bvalue(VW,Width) -> true
657 ; add_error(visb_visualiser,'VISB_SVG_BOX viewBox\'width is not a number:',VW,DefPos),fail),
658 (get_number_from_bvalue(MXH,MinX) -> true
659 ; add_error(visb_visualiser,'VISB_SVG_BOX viewBox\'minx is not a number:',MXH,DefPos),fail),
660 (get_number_from_bvalue(MYH,MinY) -> true
661 ; add_error(visb_visualiser,'VISB_SVG_BOX viewBox\'miny is not a number:',MYH,DefPos),fail),
662 ajoin([MinX,' ',MinY,' ', Width,' ',Height],ViewBoxString).
663
664
665 assert_visb_empty_svg_box_height_width(H,W,_ViewBox,Pos) :-
666 retract(visb_empty_svg_box_height_width(OldH,OldW,_)),
667 (OldH,OldW) \= (H,W),
668 add_message(visb_visualiser,'Overriding VisB SVG_BOX default dimensions: ',(OldH,OldW),Pos),
669 fail.
670 assert_visb_empty_svg_box_height_width(H,W,ViewBox,_Pos) :-
671 %add_message(visb_visualiser,'Setting VisB SVG_BOX default dimensions: ',(H,W,ViewBox),Pos),
672 assert(visb_empty_svg_box_height_width(H,W,ViewBox)).
673
674
675 % find DEFINITION with given prefix in name, evaluate it and return SVG_ID and attribute list:
676 % AllowSep = allow_seperation means that the record fields can be separated into static objects and dynamic updates
677 % and only static ones will be retained (dynamic ones dealt with later)
678 find_and_eval_visb_DEFINITION(DEFPREFIX, Kind, SVG_ID, AttrList,Desc, DefName, DefPos,EvalPos,
679 InlineObjects,AllowSep,ExpandedScope) :-
680 ? ( b_sorted_b_definition_prefixed(expression,DEFPREFIX,DefName,DefPos),
681 ? if(b_get_typed_definition(DefName,ExpandedScope,Body),true,
682 (add_visb_lint_message('Ignoring VisB DEFINITION due to errors',DefName,DefPos),fail))
683 ; special_backup(DEFPREFIX,Kind),
684 % also look at stored definitions (e.g., from static/dynamic separation of VISB_SVG_OBJECTS)
685 visb_special_definition(Kind,DefName,_Type,Body,_Class,DefPos)
686 ),
687 set_error_context(visb_error_context(definition,DefName,'all_attributes',DefPos)),
688 ? call_cleanup((get_typed_static_definition_with_constants_state(DefName,Body,
689 ResBody,DefPos,ConstState,InlineObjects,AllowSep),
690 (get_texpr_pos(ResBody,Pos0) -> true ; Pos0=DefPos), % this could be a part of the full definition body
691 get_visb_DEFINITION_svg_object(Kind,DefName,ResBody,Pos0,ConstState,SVG_ID,AttrList,Desc,EvalPos)),
692 clear_error_context).
693
694 special_backup('VISB_SVG_HOVERS',visb_hovers).
695
696 % evaluate a typed body expressions evaluating to a record or set of records
697 % and translate into SVG attribute lists:
698 get_visb_static_svg_object_for_typed_expr(Kind,DefName,Body,DefPos,SVG_ID,AttrList,Desc,InnerPos) :-
699 AllowSep=no_separation,
700 Inline=no_inlining,
701 get_typed_static_definition_with_constants_state(DefName,Body,ResBody,DefPos,ConstState,Inline,AllowSep),
702 get_visb_DEFINITION_svg_object(Kind,DefName,ResBody,DefPos,ConstState,SVG_ID,AttrList,Desc,InnerPos).
703
704
705 flatten_couple(b(couple(A,B),_,_)) --> !,flatten_couple(A),flatten_couple(B).
706 % we could also flatten union(A,B), but at the risk of duplicate warnings when the same element occurs in A and B
707 flatten_couple(A) --> [A].
708
709 get_typed_static_definition_with_constants_state(DefName,Body,ResBody,DefPos,ConstantsState,InlineObjects,AllowSep) :-
710 flatten_couple(Body,TupleList,[]),
711 % the VISB_SVG_OBJECT definition may consist of a tuple (Obj1,Obj2,...) to allow grouping
712 (TupleList = [_,_|_]
713 -> add_debug_message(visb_visualiser,'Decomposed VisB definition body tuple:',DefName,DefPos)
714 ; true),
715 ? nth1(DefNr,TupleList,TupleBody),
716 (get_texpr_pos(TupleBody,TuplePos) -> true ; TuplePos=DefPos),
717 get_typed_static_definition_with_constants_state3(DefName,DefNr,TupleBody,ResBody,TuplePos,
718 ConstantsState,InlineObjects,AllowSep).
719 get_typed_static_definition_with_constants_state3(DefName,DefNr,Body,ResBody,DefPos,ConstantsState,InlineObjects,AllowSep) :-
720 determine_type_of_visb_formula(Body,TIds,Class),
721 debug_format(19,'Detected VisB DEFINITION ~w (~w)~n',[DefName,Class]),
722 (%Class=requires_variables, % now also useful if event field present
723 AllowSep=allow_separation, % allowed for VISB_SVG_OBJECTS
724 ? if(separate_into_static_and_dynamic_part(DefName,DefNr,Body,StaticBody,DynamicBody,EventBody,HoverBody),
725 true,
726 (get_texpr_type(Body,Type),
727 (contains_events_or_hovers_field(Type)
728 -> add_warning(visb_visualiser,'Static/Dynamic separation failed for (required for events or hovers): ',DefName,DefPos)
729 % VISB_SVG_OBJECT definition contains events/hovers and as such requires separation; which failed here
730 ; add_debug_message(visb_visualiser,'Static/Dynamic separation failed or not useful for: ',DefName,DefPos)
731 ),
732 fail)),
733 determine_type_of_visb_formula(StaticBody,SIds,SClass),
734 get_unique_initial_state_for_visb(SClass,DefName,DefPos,SIds,ConstantsState)
735 -> Separated=true, ResBody=StaticBody,
736 add_debug_message(visb_visualiser,'Automatically separated fields into static and dynamic: ',DefName,DefPos),
737 %write('STATIC: '),translate:print_bexpr(StaticBody),nl, write('DYNAMIC: '),translate:print_bexpr(DynamicBody),nl,
738 assert_visb_udpate_def_body(visb_updates,DefName,DynamicBody,DefPos),
739 add_visb_events_from_def_body(visb_events,DefName,EventBody,DefPos),
740 assert_visb_udpate_def_body(visb_hovers,DefName,HoverBody,DefPos) % needs to be asserted so that it can be evaluated after svg objects have been created to extract exit value
741 ; get_unique_initial_state_for_visb(Class,DefName,DefPos,TIds,ConstantsState)
742 -> ResBody=Body
743 ; ResBody=Body,
744 (Class=requires_constants ->
745 get_texpr_ids(TIds,Ids),
746 % we could check if values of Ids are identical in all constants solutions
747 (is_concrete_constants_state_id(_)
748 -> BMsg='multiple solutions for used constants exist: '
749 ; \+ root_sufficiently_explored
750 -> BMsg='no constants were setup yet: '
751 % two cases below should not happen; but happen on Linux with CSP||B models
752 ; state_space:logical_transition_from_root(RID),
753 state_space:packed_visited_expression(RID,RSt),
754 functor(RSt,RFun,RArity)
755 -> ajoin(['no constants were found, but logical successor ',RFun,'/',RArity,
756 ' with id ', RID,' of root exists: '],BMsg)
757 ; BMsg='no constants were found: '
758 ),
759 ajoin(['DEFINITION ',DefName, ' requires constants but ',BMsg],Msg),
760 (get_a_constants_state(ConstantsState,StateID,InlineObjects)
761 -> (member(CstID,Ids),
762 other_constant_value_exists_for(CstID,ConstantsState,StateID)
763 -> add_warning(visb_visualiser,Msg,CstID,DefPos)
764 ; ajoin(['DEFINITION ',DefName,
765 ' requires constants and all SETUP_CONSTANTS solutions have same value thus far: '],Msg2),
766 % constraint-based checks or execute by predicate could later create others !
767 add_message(visb_visualiser,Msg2,Ids,DefPos)
768 )
769 ; add_error(visb_visualiser,Msg,Ids,DefPos),
770 fail % avoid other subsequent errors when evaluating with ConstantsState=[]
771 )
772 ; ConstantsState=[]
773 )
774 ),
775 (Class=requires_variables, Separated=false
776 -> get_texpr_ids(TIds,AIds),
777 include(b_is_variable,AIds,VIds),
778 ajoin_with_sep(VIds,',',SVids),
779 (get_preference(visb_allow_variables_for_objects,false)
780 -> ajoin(['Ignoring DEFINITION which requires variables ',SVids,': '],WMsg),
781 add_warning(visb_visualiser,WMsg,DefName,DefPos),fail
782 ; ConstantsState=[]
783 -> add_warning(visb_visualiser,'Ignoring DEFINITION which requires variables, could not find unique initial state: ',DefName,DefPos),fail
784 ; ajoin(['DEFINITION requires variables ',SVids,
785 '; exporting history to HTML may not work correctly'],WMsg),
786 add_visb_lint_message(WMsg,DefName,DefPos)
787 )
788 ; true).
789
790
791 % separate/split/filter VISB_SVG_OBJECTS definitions into a static part and a dynamic part depending on variables
792 % (aka updates/items)
793 separate_into_static_and_dynamic_part(DefName,DefNr,Body,StaticBody,DynamicBody,EventBody,HoverBody) :-
794 ? separate_static_dyn_aux(DefName,DefNr,[],[],Body,StaticBody,DynamicBody,EventBody,HoverBody).
795
796
797 rewrite_expression(let_expression(Ids,Exprs,Body),NewBody) :-
798 % currently we cannot process LET's here, expand them
799 replace_ids_by_exprs(Body,Ids,Exprs,NewBody). % see also expand_all_lets/2
800
801 separate_static_dyn_aux(DefName,DefNr,OuterQuantifiedIds,Path,b(Expr,_Type,_Info), Static,Dynamic,Event,Hover) :-
802 rewrite_expression(Expr,NewExpr),!,
803 separate_static_dyn_aux(DefName,DefNr,OuterQuantifiedIds,Path,NewExpr, Static,Dynamic,Event,Hover).
804 separate_static_dyn_aux(DefName,DefNr,OuterQuantifiedIds,Path,b(couple(A,B),_,_Info),
805 StaticCouple,DynamicCouple,EventCouple,HoverCouple) :- !,
806 separate_static_dyn_aux(DefName,DefNr,OuterQuantifiedIds,[0|Path],A,SA,DA,EA,HA),
807 separate_static_dyn_aux(DefName,DefNr,OuterQuantifiedIds,[1|Path],B,SB,DB,EB,HB),
808 create_couple(SA,SB,StaticCouple),
809 create_couple(DA,DB,DynamicCouple),
810 create_couple(EA,EB,EventCouple),
811 create_couple(HA,HB,HoverCouple).
812 separate_static_dyn_aux(DefName,DefNr,OuterQuantifiedIds,Path,TExpr,
813 b(StaticSetComp,set(ST),I),
814 b(DynamicSetComp,set(DT),I),
815 b(EventsSetComp,set(ET),I),
816 b(HoversSetComp,set(HT),I)) :-
817 ? bsyntaxtree:is_eventb_comprehension_set(TExpr,Ids,Pred,Expr),
818 (OuterQuantifiedIds = []
819 -> add_debug_message(visb_visualiser,'Top-level set comprehensions for VISB: ',Ids,TExpr)
820 ; add_message(visb_visualiser,'Nested set comprehensions for VISB: ',OuterQuantifiedIds,TExpr)
821 ),
822 !,
823 determine_type_of_visb_formula(b(exists(Ids,Pred),pred,[]),SIds,Class),
824 (get_unique_initial_state_for_visb(Class,DefName,Pred,SIds,_)
825 -> true % The predicate must be statically executable
826 ; time_out_for_node(root,O,_Type),
827 translate_operation_name(O,Op) ->
828 ajoin(['Due to TIME_OUT of ',Op,' the comprehension set domain cannot be evaluated: '],Msg),
829 add_warning(visb_visualiser,Msg,Pred,Pred),
830 % TODO: avoid other error messages that will occur after that
831 fail
832 ; (root_sufficiently_explored -> RMsg = '' ; RMsg = ' because constants were not set up yet'),
833 (multiple_concrete_constants_exist -> MMsg = ',multiple SETUP_CONSTANTS exist' ; MMsg = ''),
834 ajoin(['Cannot evaluate comprehension set domain (',Class,MMsg,')',RMsg],LMsg),
835 add_visb_lint_message(LMsg,Pred,Pred),
836 % at the moment we require the domain to be computable statically for both static/dynamic attributes
837 fail
838 ),
839 append(OuterQuantifiedIds,Ids,NewOuterQuantifiedIds),
840 ? separate_static_dyn_aux(DefName,DefNr,NewOuterQuantifiedIds,Path,Expr,
841 StaticRec,DynamicRec,EventRec,HoverRec),
842 get_texpr_type(StaticRec,ST),
843 get_texpr_type(DynamicRec,DT),
844 get_texpr_type(EventRec,ET),
845 get_texpr_type(HoverRec,HT),
846 bsyntaxtree:get_texpr_info(TExpr,I),
847 b_ast_cleanup:rewrite_event_b_comprehension_set(Ids,StaticRec,Pred, set(ST), StaticSetComp),
848 b_ast_cleanup:rewrite_event_b_comprehension_set(Ids,DynamicRec,Pred,set(DT), DynamicSetComp),
849 b_ast_cleanup:rewrite_event_b_comprehension_set(Ids,EventRec,Pred,set(ET), EventsSetComp),
850 b_ast_cleanup:rewrite_event_b_comprehension_set(Ids,HoverRec,Pred,set(HT), HoversSetComp).
851 separate_static_dyn_aux(DefName,DefNr,OuterQuantifiedIds,Path,b(rec(Fields),_,Info),
852 b(rec(StaticFields),record(SFT),Info), % VISB_OBJECTS (static)
853 b(UpdateRecWithDynamicFields,record(DFT),Info), % VISB_UPDATES (dynamic)
854 b(rec(AllEventFields),record(EFT),Info), % VISB_EVENTS
855 b(rec(AllHoverFields),record(HFT),Info) % VISB_HOVERS
856 ) :- !,
857 split_list(is_visb_event_field,Fields,EventFields,ObjectAndHoverFields),
858 (select(field(hovers,HoverRec),ObjectAndHoverFields,ObjectFields)
859 -> HoverFields = [field(hovers,HoverRec)] % record deconstructed in add_visb_hovers_from_definition
860 ; ObjectFields=ObjectAndHoverFields, HoverFields = []),
861 % maplist(check_field_is_supported(Info),ObjectFields), % will lead to duplicate warning messages, check_visb_update_type e.g., will be called for dynamic updates
862 separate_fields(ObjectFields,StaticFields0,DynamicFields,DefName),
863 ? (member(field(svg_class,_SVG_Class),StaticFields0) -> true
864 ; member(field(svg_class,SVG_Class),DynamicFields)
865 -> add_warning(visb_visualiser,'SVG svg_class field is not static in: ',DefName,SVG_Class),
866 fail
867 ; add_warning(visb_visualiser,'No svg_class field in: ',DefName,Info),
868 fail
869 ),
870 ? (member(field(id,SVGID),StaticFields0)
871 -> (DynamicFields \= [] ; EventFields \= [] ; HoverFields \= [] % separation is useful for this record
872 ; Path \= [] % we are part of a couple; maybe other records have a dynamic,event or hover part; so proceed
873 ),
874 StaticFields = StaticFields0,
875 TStatic_SVGID = SVGID
876 ; member(field(id,SVGID),DynamicFields)
877 -> add_warning(visb_visualiser,'SVG id field is not static in: ',DefName,SVGID),
878 fail
879 ; OuterQuantifiedIds = []
880 -> gensym(svg_id,GenID),
881 ajoin(['Generated id ',GenID, ' for SVG object without id field in: '],Msg),
882 add_debug_message(visb_visualiser,Msg,DefName,Info),
883 SVGID = b(string(GenID),string,[]),
884 sort([field(id,SVGID) | StaticFields0],StaticFields)
885 ; % the record is quantified within a set comprehension; we need to compute the ID from OuterQuantifiedIds
886 % it has to be identical for VISB_SVG_OBJECTS and VISB_SVG_UPDATES
887 SVGID = b(ExtFunCall,string,[]),
888 convert_path_to_int(Path,IntPath),
889 PathExpr = b(integer(IntPath),integer,[]),
890 create_couple([b(string(DefName),string,[]),b(integer(DefNr),integer,[]),PathExpr|OuterQuantifiedIds],Tuple),
891 % add DefName and DefNr to avoid clash if other Definition has same quantified ids
892 ExtFunCall = external_function_call('SHA_HASH_HEX',[Tuple]),
893 ajoin(['Generated call to compute id for SVG object without id field in ',DefName,': '],Msg),
894 add_debug_message(visb_visualiser,Msg,SVGID,Info),
895 sort([field(id,SVGID) | StaticFields0],StaticFields)
896 ),
897 maplist(get_field_type,StaticFields,SFT),
898 ( (select(field(visibility,VisibExpr),DynamicFields,RestDynamicFields)
899 ; member(field(visibility,VisibExpr),StaticFields0), % the visibility expression is static
900 RestDynamicFields = DynamicFields
901 ),
902 RestDynamicFields = [_|_],
903 \+ hovers_can_set_visibility(HoverFields),
904 (nonvar(TStatic_SVGID), TStatic_SVGID = b(string(Static_SVGID),_,_) % TODO: evaluate properly
905 -> \+ visb_has_visibility_hover(Static_SVGID) % TODO: check no other hover definition added later
906 ; true) % the ID is generated and cannot be accessed by other hovers which could make object visible
907 -> % create IF-THEN-ELSE to avoid evaluating fields when object not visible; also fixes WD issues
908 TRUE = b(boolean_true,boolean,[]),
909 sort([field(id,SVGID),field(visibility,TRUE)|RestDynamicFields],AllDynamicFields),
910 maplist(construct_dummy_value,RestDynamicFields,RestDummyFields),
911 sort([field(id,SVGID),field(visibility,b(boolean_false,boolean,[]))|RestDummyFields],AllDummyFields),
912 create_visibility_check(VisibExpr,VisibilityCond),
913 UpdateRecWithDynamicFields = if_then_else(VisibilityCond,
914 b(rec(AllDynamicFields),record(DFT),Info),
915 b(rec(AllDummyFields),record(DFT),Info)),
916 add_debug_message(visb_visualiser,'Refactoring VisB Record with visibility attribute: ',VisibilityCond)
917 % ,translate:print_bexpr(b(UpdateRecWithDynamicFields,any,[])),nl
918 ; sort([field(id,SVGID)|DynamicFields],AllDynamicFields), % TODO: use kernel_records
919 UpdateRecWithDynamicFields = rec(AllDynamicFields)
920 ),
921 maplist(get_field_type,AllDynamicFields,DFT),
922 sort([field(id,SVGID)|EventFields],AllEventFields), % ditto
923 maplist(get_field_type,AllEventFields,EFT),
924 sort([field(id,SVGID)|HoverFields],AllHoverFields), % ditto
925 maplist(get_field_type,AllHoverFields,HFT).
926 separate_static_dyn_aux(DefName,DefNr,OuterQuantifiedIds,_Path,TEXPR, _, _, _,_) :-
927 ajoin(['Uncovered expression for ',DefName,'-',DefNr,' ',OuterQuantifiedIds],Msg),
928 add_debug_message(visb_visualiser,Msg,TEXPR,TEXPR),fail.
929
930 hovers_can_set_visibility(HoverFields) :- member(field(hovers,Expr),HoverFields),
931 hover_expr_can_set_visibility(Expr).
932
933 hover_expr_can_set_visibility(b(E,_,_)) :- hovers_can_set_visib_aux(E).
934 hovers_can_set_visib_aux(couple(A,B)) :- !, (hover_expr_can_set_visibility(A) ; hover_expr_can_set_visibility(B)).
935 hovers_can_set_visib_aux(rec(Fields)) :- !, member(field(visibility,_),Fields).
936 hovers_can_set_visib_aux(set_extension(L)) :- !, member(E,L), hover_expr_can_set_visibility(E).
937 hovers_can_set_visib_aux(E) :- debug_format(19,'Assuming hover can set visibility: ~w~n',[E]).
938 % TODO: synchronise with flex_expand and check we cover all relevant cases like partial functions
939
940
941 create_visibility_check(VisibExpr,VisibilityCond) :- get_texpr_type(VisibExpr,string),!,
942 static_check_attribute_value_expression(visibility,VisibExpr),
943 VISIBLE = b(string('visible'),string,[]),
944 create_equality(VisibExpr,VISIBLE,VisibilityCond).
945 create_visibility_check(VisibExpr,VisibilityCond) :- get_texpr_type(VisibExpr,boolean),!,
946 TRUE = b(boolean_true,boolean,[]),
947 create_equality(VisibExpr,TRUE,VisibilityCond).
948 create_visibility_check(VisibExpr,b(truth,pred,[])) :-
949 add_warning(visb_visualiser,'The attribute visibility is not of type STRING or BOOL: ',VisibExpr,VisibExpr).
950
951
952 construct_dummy_value(field(Name,TExpr),field(Name,b(value(DummyValue),Type,[]))) :-
953 get_texpr_type(TExpr,Type),
954 get_default_or_dummy_value(Type,Name,DummyValue). % object not visible: do not evaluate expression; use dummy value
955
956 get_default_or_dummy_value(string,Name,DummyValue) :-
957 svg_attribute_default_value(Name,Default),!,
958 DummyValue=string(Default).
959 get_default_or_dummy_value(Type,_Name,DummyValue) :- any_value_for_type(Type,DummyValue).
960
961
962 % treat path of 0,1 as binary number, but add 1 at end to distinguish [0,0] and [0] or [1,0] and [1,0,0]
963 convert_path_to_int([],1).
964 convert_path_to_int([H|T],Res) :- convert_path_to_int(T,IT),
965 Res is IT*2+H.
966
967 % is the name of a field for events inside a VISB_SVG_OBJECT record
968 % example rec(..., event:"BOperation", predicate:"x=TRUE")
969 is_visb_event_field(field(Name,_)) :- is_visb_event_field_name(Name).
970 is_visb_event_field_name(event).
971 is_visb_event_field_name(events). % tuple or set of events inside VISB_SVG_OBJECTS
972 is_visb_event_field_name(predicate).
973 is_visb_event_field_name(predicates). % will generate warning
974 is_visb_event_field_name(preds). % will generate warning
975 is_visb_event_field_name(execution_kind).
976 % is the name of a field for hovers inside a VISB_SVG_OBJECT record
977 is_visb_hover_field_name(hovers).
978 is_visb_hover_field_name(items). % enable items
979 %is_visb_hover_field_name(optional). % not needed ?
980 %is_visb_hover_field_name(override). % not needed ?
981
982 % check if the type of a VISB_SVG_OBJECTS expression requires separation for hovers/events
983 contains_events_or_hovers_field(set(X)) :- contains_events_or_hovers_field(X).
984 contains_events_or_hovers_field(couple(A,B)) :-
985 (contains_events_or_hovers_field(A) -> true ; contains_events_or_hovers_field(B)).
986 ?contains_events_or_hovers_field(record(F)) :- member(field(Name,_),F),
987 (is_visb_event_field_name(Name) -> true ; is_visb_hover_field_name(Name)).
988
989 get_field_type(field(Name,Expr),field(Name,T)) :- get_texpr_type(Expr,T).
990
991 % separate fields from a SVG/VISB_SVG_OBJECT record into static part and dynamic update expressions
992 separate_fields([],[],[],_).
993 separate_fields([field(Field,Expr)|T],[field(Field,Expr)|TS],TD,DefName) :-
994 is_static_expr(Expr,Field,DefName),!,
995 separate_fields(T,TS,TD,DefName).
996 separate_fields([field(Field,Expr)|T],[field(Field,Default)|TS],[field(Field,Expr)|TD],DefName) :-
997 requires_static_default_value(Field,Default), !,
998 separate_fields(T,TS,TD,DefName).
999 separate_fields([H|T],TS,[H|TD],DefName) :-
1000 separate_fields(T,TS,TD,DefName).
1001
1002 % fields which require a static default value; here to set up child SVG objects of title
1003 % static default values could also be useful for hovers in general?
1004 requires_static_default_value(title,b(string('-title-not-set-'),string,[])).
1005
1006 % virtual SVG attributes which are translated to children tags/objects
1007 attribute_automatically_creates_children(title).
1008
1009 is_static_expr(Expr,Field,DefName) :-
1010 determine_type_of_visb_formula(Expr,_TIds,Class), % TODO pass local variables from exists above
1011 (Class=requires_nothing -> true
1012 ; Class=requires_constants,
1013 \+ multiple_concrete_constants_exist, % TODO: check if _TIds have multiple solutions
1014 is_concrete_constants_state_id(_) -> true
1015 ; ajoin(['Detected dynamic expression for field ',Field,' in ',DefName,': '],Msg),
1016 add_debug_message(visb_visualiser,Msg,Expr,Expr),fail
1017 ).
1018
1019 % --------
1020
1021
1022 extract_attribute_from_record(_,field(OrigName,Value),_) :-
1023 opt_rewrite_field_name(OrigName,Name),
1024 deconstruct_singleton_set(Name,Value,'$optional_field_absent'),
1025 !, fail. % optional field (e.g., in TLA+) not present; no error
1026 extract_attribute_from_record(DefPos,field(OrigName,Value),svg_attribute(Name,SVal)) :-
1027 opt_rewrite_field_name(OrigName,Name),
1028 extract_attribute_value(DefPos,Name,Value,SVal).
1029 extract_attribute_from_record(DefPos,F,_) :-
1030 add_error(visb_visualiser,'Extract SVG attribute failed: ',F,DefPos),fail.
1031
1032 % extract a value for a particular field Name
1033 extract_attribute_value(DefPos,Name,Value,SVal) :-
1034 (deconstruct_singleton_set(Name,Value,Element)
1035 -> Element \= '$optional_field_absent',
1036 extract_attr_value(Name,Element,SVal,DefPos)
1037 ; extract_attr_value(Name,Value,SVal,DefPos)
1038 ).
1039
1040
1041 % in TLA+ mode optional record fields have {TRUE|->VALUE} rather than VALUE
1042 % this can also be useful in B to allow mixing different SVG records in a set
1043 % for Alloy mode we may want to also support singleton sets as scalars
1044 deconstruct_singleton_set(Name,Set,Res) :-
1045 singleton_set(Set,El),
1046 detect_partial_records_for_field(Name),
1047 (El=(pred_true,El2)
1048 -> Val=El2 % TLA2B encoding for optional fields that are present
1049 ; Val=El), % Alloy
1050 !,
1051 Res = Val.
1052 deconstruct_singleton_set(Name,[],Res) :-
1053 detect_partial_records_for_field(Name),
1054 Res = '$optional_field_absent'.
1055
1056 detect_partial_records_for_field(points) :- !, fail. % can be set of points
1057 detect_partial_records_for_field(children) :- !, fail. % children field for groups is a set of ids
1058 detect_partial_records_for_field(Name) :-
1059 is_svg_number_attribute(Name),!. % sets never sensible as a numerical value
1060 detect_partial_records_for_field(Name) :- is_id_or_text_attribute(Name),!. % ditto for ids
1061 detect_partial_records_for_field(Name) :- is_svg_color_attribute(Name),!. % ditto for colors
1062 detect_partial_records_for_field(_) :-
1063 \+ classical_b_mode. % TODO: check that we are in TLA or Alloy mode?
1064 % TODO: ensure ProB2 sets animation minor mode for TLA
1065
1066
1067 extract_attr_value(Name,Value,SVal,DefPos) :-
1068 is_svg_number_attribute(Name),!, % we could try and extract svg_class from attribute list above
1069 (get_number_from_bvalue(Value,NrVal)
1070 -> SVal=NrVal % convert to atom?
1071 ; b_value_to_string(Value,SVal),
1072 (is_special_svg_number_form(SVal) -> true
1073 ; atom_codes(SVal,CC),
1074 safe_number_codes(_NrVal,CC) -> true
1075 ; ajoin(['The value of the SVG attribute "',Name,
1076 '" is not a number: '],Msg),
1077 add_warning(visb_visualiser,Msg,SVal,DefPos)
1078 )
1079 ).
1080 extract_attr_value(children,Value,SVal,DefPos) :- !,
1081 (is_set_value(Value,extract_attr_value)
1082 -> (try_expand_custom_set_with_catch(Value,ExpandedSet,extract_attr_value),
1083 maplist(b_value_to_id_string,ExpandedSet,SVal) -> true
1084 ; add_warning(visb_visualiser,'The children attribute should be a finite set of identifiers: ',Value,DefPos),
1085 SVal=[]
1086 )
1087 ; add_warning(visb_visualiser,'The children attribute should be a set of identifiers: ',Value,DefPos),
1088 SVal=[]).
1089 extract_attr_value(points,Value,SVal,DefPos) :- % Class should be polygon or polyline
1090 % automatically translate sequences or sets of pairs of numbers to SVG string format
1091 (Value = [(Nr,_)|_] ; Value = avl_set(node((Nr,_),_,_,_,_))),
1092 is_number(Nr), % TODO: check typing
1093 svg_points(Value,Str,DefPos,no_wf_available),!,
1094 string(SVal)=Str.
1095 extract_attr_value(points,Value,SVal,_DefPos) :- Value = [],!, SVal = ''.
1096 extract_attr_value(visibility,Value,SVal,DefPos) :- !,
1097 % convert TRUE -> visible, FALSE -> hidden
1098 ( Value=pred_true -> TxtVal='visible'
1099 ; Value=pred_false -> TxtVal='hidden'
1100 ; b_value_to_string(Value,TxtVal),
1101 ? (member(TxtVal,[collapse,hidden,visible]) -> true
1102 ; add_warning(visb_visualiser,'Value of SVG attribute "visibility" must be collapse,hidden or visible and not: ',TxtVal,DefPos))
1103 ),
1104 SVal=TxtVal.
1105 extract_attr_value(Name,Value,_,DefPos) :-
1106 check_svg_attribute_value(Name,Value,DefPos),fail.
1107 extract_attr_value(Name,Value,SVal,_) :-
1108 ? is_id_or_text_attribute(Name),!,
1109 (Name=text -> b_value_to_string(Value,SVal) % a pure text field; you probably want to see the B value
1110 ; is_text_attribute(Name) % something like transform, predicate, event; here maybe automatic concat can be useful
1111 -> b_value_to_attr_string(Value,SVal)
1112 ; b_value_to_id_string(Value,SVal)). % concatenates e.g. pairs of strings to string without ( |-> )
1113 extract_attr_value(Attr,RawValue,ResValue,_DefPos) :- raw_value_attribute(Attr),!, % keep value intact
1114 ResValue = RawValue.
1115 extract_attr_value(_Name,Value,SVal,_DefPos) :-
1116 b_value_to_string(Value,SVal).
1117
1118 % visb_visualiser:extract_attr_value(text,avl_set(node((fd(1,'Kunden'),int(0)),true,0,empty,empty)),S,unknown).
1119
1120 is_number(int(_)).
1121 is_number(term(floating(_))).
1122
1123 % do not convert to string; these are treated separately
1124 raw_value_attribute(hovers).
1125 raw_value_attribute(events).
1126
1127
1128 % evaluate a DEFINITION to a set of B records representing SVG objects
1129 % returns a possibly refined sub-position corresponding to generated SVG objects as last argument
1130 get_visb_DEFINITION_svg_object(SpecialClass,DefName,Body,_DefPos,ExpandedState,SVG_ID,AttrList,Desc,InnerPos) :-
1131 get_preference(visb_show_debug_infos,true),
1132 bsyntaxtree:is_eventb_comprehension_set(Body,Ids,Pred,Expr),
1133 % try and separate couple in Expr so that we have better position info for source of VISB_SVG_OBJECTS
1134 Body = b(_,Type,II),
1135 Expr = b(couple(_,_),_,_),
1136 !,
1137 couple_member(NewExpr,Expr), % evaluate each part of the couple in isolation, with better position info
1138 get_texpr_pos(NewExpr,InnerPos),
1139 b_ast_cleanup:rewrite_event_b_comprehension_set(Ids,NewExpr,Pred, Type, NewBody),
1140 get_visb_DEF_svg_object_aux(SpecialClass,DefName,b(NewBody,Type,II),InnerPos,ExpandedState,SVG_ID,AttrList,Desc).
1141 get_visb_DEFINITION_svg_object(SpecialClass,DefName,Body,DefPos,ExpandedState,SVG_ID,AttrList,Desc,DefPos) :-
1142 ? get_visb_DEF_svg_object_aux(SpecialClass,DefName,Body,DefPos,ExpandedState,SVG_ID,AttrList,Desc).
1143
1144 couple_member(TExpr,b(couple(A,B),_,_)) :- !, (couple_member(TExpr,A) ; couple_member(TExpr,B)).
1145 couple_member(TExpr,TExpr).
1146
1147 get_visb_DEF_svg_object_aux(SpecialClass,DefName,Body,DefPos,ExpandedState,SVG_ID,AttrList,Desc) :-
1148 evaluate_visb_formula(Body,DefName,'',ExpandedState,ResValue,DefPos),
1149 flex_expand(ResValue,DefName,DefPos,Expanded,[]),
1150 (Expanded = [] -> add_message(visb_visualiser,'Empty set of SVG object records: ',DefName,DefPos)
1151 ; Expanded =[Record|_] ->
1152 (get_VISB_record_fields(Record,Fs)
1153 % TODO: as we now can have different type of records, check in flex_expand below
1154 ? -> (member(field(id,_),Fs)
1155 -> true
1156 ; add_message(visb_visualiser,'DEFINITION needs an `id` field if you want to use updates: ',DefName,DefPos)
1157 )
1158 ; ajoin(['DEFINITION ', DefName,' should evaluate to a set of records or tuples of records: '],Msg),
1159 add_warning(visb_visualiser,Msg,Record,DefPos)
1160 )
1161 ),
1162 ? member(Records,Expanded),
1163 get_VISB_record_fields(Records,Fields),
1164 ? (select(field(id,VID),Fields,F1)
1165 -> extract_attribute_value(DefPos,id,VID,SVG_ID)
1166 ; gensym(svg_id,SVG_ID), F1=Fields),
1167 (select(field(comment,CC),F1,F2), b_value_to_string(CC,Desc) -> true
1168 ; Desc = '', F2=F1),
1169 ? (SpecialClass=visb_updates, member(field(visibility,VV),F2),
1170 is_invisible(VV),
1171 \+ visb_has_visibility_hover(SVG_ID) % check for hover which may make object visible
1172 -> AttrList=[svg_attribute(visibility,'hidden')] % do not transmit updates
1173 ; include_maplist(extract_attribute_from_record(DefPos),F2,AttrList0),
1174 exclude(check_unsupported_special_visb_attribute(SpecialClass,DefPos),AttrList0,AttrList),
1175 ? (select(svg_attribute(svg_class,SVG_Class),AttrList,A2)
1176 -> maplist(check_attribute_is_supported(SVG_Class,DefPos),A2)
1177 ; true)
1178 ).
1179
1180 is_invisible(string(hidden)). % also treat collapse ?
1181 is_invisible(pred_false).
1182
1183 % expand nested tuples/sets to a single list of SVG records (possibly of different types)
1184 flex_expand(Value,DefName,Pos) --> {is_custom_explicit_set(Value)},!,
1185 {try_expand_custom_set_with_catch(Value,ExpandedSet,flex_expand),
1186 (is_custom_explicit_set(ExpandedSet)
1187 -> ajoin(['Could not expand set of SVG object records or tuples thereof for ',DefName,': '],Msg),
1188 add_warning(visb_visualiser,Msg,Value,Pos),
1189 fail
1190 ; true)},
1191 flex_expand(ExpandedSet,DefName,Pos).
1192 flex_expand([],_,_) --> !, [].
1193 flex_expand(L,_DefName,_Pos) --> {L =[(string(_),string(_))|_]}, !,
1194 [L]. % is probably a partial function {"id" |-> ,...}
1195 flex_expand([H|T],DefName,Pos) --> !, flex_expand(H,DefName,Pos), l_flex_expand(T,DefName,Pos).
1196 flex_expand((A,B),DefName,Pos) --> !, flex_expand(A,DefName,Pos), flex_expand(B,DefName,Pos).
1197 flex_expand(T,_,_) --> [T].
1198
1199 l_flex_expand([],_,_) --> !, [].
1200 l_flex_expand([H|T],DefName,Pos) --> !, flex_expand(H,DefName,Pos), l_flex_expand(T,DefName,Pos).
1201 l_flex_expand(T,_,_) --> [T].
1202
1203 flex_member(X,SetPairVal,DefName,Pos) :- flex_expand(SetPairVal,DefName,Pos,List,[]), member(X,List).
1204
1205
1206 % get the fields of a B value record
1207 % also transparently handles alternative representations, like a function from STRING to STRING
1208 get_VISB_record_fields(rec(Fields),Res) :- !, Res=Fields.
1209 get_VISB_record_fields(StringFunction,Fields) :-
1210 is_set_value(StringFunction,get_VISB_record_fields),
1211 try_expand_custom_set_with_catch(StringFunction,Expanded,get_VISB_record_fields),
1212 % TODO: check we have no duplicates
1213 maplist(convert_to_field,Expanded,Fields).
1214 % TODO: support records as generated by READ_XML:
1215 % rec(attributes:{("fill"|->"#90ee90"),("height"|->"360px"),("id"|->"background_rect"),("width"|->"600px"),("x"|->"1px"),("y"|->"2px")},element:"rect",meta:{("xmlLineNumber"|->"3")},pId:5,recId:6)
1216 % for <rect height="360px" x="1px" y="2px" id="background_rect" width="600px" fill="#90ee90"></rect>
1217
1218 convert_to_field((string(FieldName),Value),field(FieldName,Value)).
1219 % TODO: maybe handle enumerated set values as field names
1220
1221
1222 % already extract and type-check VISB_SVG_UPDATES definitions
1223 precompile_svg_object_updates(ExpandedScope,JsonFileContext) :-
1224 b_sorted_b_definition_prefixed(expression,'VISB_SVG_UPDATES',DefName,DefPos),
1225 ? b_get_typed_definition(DefName,ExpandedScope,TypedExpr),
1226 assert_visb_udpate_def_body(visb_updates,DefName,TypedExpr,DefPos),
1227 add_children_for_visb_update_def_body(TypedExpr,DefName,JsonFileContext),
1228 fail.
1229 precompile_svg_object_updates(_,_).
1230
1231 % assert VisB updates or hovers from B DEFINITION,
1232 % either from VISB_SVG_UPDATES or hovers or dynamic parts of VISB_SVG_OBJECTS:
1233 assert_visb_udpate_def_body(_Kind,DefName,TypedExpr,_DefPos) :-
1234 TypedExpr=b(rec([field(id,_)]),_,Info),!, % can happen when all fields are static
1235 add_debug_message(visb_visualiser,'Useless update/hover (just id field): ',DefName,Info).
1236 assert_visb_udpate_def_body(Kind,DefName,b(couple(A,B),_,_),DefPos) :- !,
1237 % separate couples into individual expressions; in case of WD errors we have more localised errors/problems
1238 assert_visb_udpate_def_body(Kind,DefName,A,DefPos),
1239 assert_visb_udpate_def_body(Kind,DefName,B,DefPos).
1240 assert_visb_udpate_def_body(Kind,DefName,TypedExpr,DefPos) :-
1241 get_texpr_type(TypedExpr,Type),
1242 determine_type_of_visb_formula(TypedExpr,_,FormulaClass),
1243 debug_format(19,'Detected ~w: ~w (~w)~n',[Kind,DefName,FormulaClass]),
1244 assertz(visb_special_definition(Kind,DefName,Type,TypedExpr,FormulaClass,DefPos)),
1245 check_visb_update_type(Kind,Type,DefName,unknown_svg_class,DefPos),
1246 static_check_dynamic_update_def_body(TypedExpr).
1247
1248
1249 % check if we need to add virtual children for an VISB_SVG_UPDATES body:
1250 % (e.g., for title updates to SVG objects coming from an SVG file)
1251 add_children_for_visb_update_def_body(b(couple(A,B),_,_),DefName,JsonFile) :- !,
1252 (add_children_for_visb_update_def_body(A,DefName,JsonFile)
1253 ;
1254 add_children_for_visb_update_def_body(B,DefName,JsonFile)).
1255 add_children_for_visb_update_def_body(b(rec(Fields),_,_),DefName,JsonFile) :-
1256 ? select(field(id,TParentId),Fields,F2),!,
1257 is_static_expr(TParentId,id,DefName), % we need a static value to create children for it
1258 TParentId = b(_,_,Info),
1259 determine_type_of_visb_formula(TParentId,TIds,Class),
1260 eval_static_def(Class,DefName,'',TParentId,TIds,ResValue,Info), % evaluate in constants state if necessary
1261 b_value_to_id_string(ResValue,ParentId),
1262 \+ svg_id_exists(ParentId),
1263 (\+ get_default_visb_svg_file(_,_), % cannot call no_svg_file_available yet
1264 JsonFile = '', % No JSON file to be loaded after, which could add SVG file or objects
1265 \+ special_definition_exists('VISB_SVG_CONTENTS')
1266 -> ajoin(['Unknown VISB_SVG_OBJECT with id ',ParentId,' in: '],Msg),
1267 add_warning(visb_visualiser,Msg,DefName,Info)
1268 ? ; select(field(title,TTitle),F2,_),
1269 (TTitle = b(string(Title),_,_) -> true ; Title = 'no-title-set'),
1270 get_texpr_pos(TTitle,Pos),
1271 add_virtual_title_object(Title,DefName,Pos,TitleID),
1272 assert_visb_svg_child(ParentId,Pos,TitleID),
1273 assert(visb_svg_child_of_object_from_svg_file(TitleID))
1274 ),
1275 fail.
1276 % TODO: set comprehensions like {x•x:Obj|rec(id=("obj",x), title=....)}
1277 % TODO: support sets and warn when ID is not statically known?
1278
1279 % assert VisB events or hovers from B DEFINITION, from part of VISB_SVG_OBJECTS:
1280 % supporting event: attribute and optional predicate attribute
1281 % Kind = visb_events %or visb_hovers
1282 add_visb_events_from_def_body(Kind,DefName,Body,DefPos) :-
1283 get_texpr_type(Body,Type),
1284 \+ empty_update_or_hover_type(Type), % at least one more field than just id field
1285 get_visb_static_svg_object_for_typed_expr(Kind,DefName,Body,DefPos,SVG_ID,AttrList,Desc,InnerPos),
1286 add_visb_object_from_definition(Kind, SVG_ID, AttrList, Desc, DefName, InnerPos),
1287 fail.
1288 add_visb_events_from_def_body(_,_,_,_).
1289
1290 empty_update_or_hover_type(record(Fields)) :- !, Fields \= [_,_|_]. % at least one more field than just id field
1291 empty_update_or_hover_type(set(X)) :- !, empty_update_or_hover_type(X).
1292 empty_update_or_hover_type(couple(A,B)) :- !, empty_update_or_hover_type(A), empty_update_or_hover_type(B).
1293
1294 get_svg_object_updates_from_definitions(ExpandedBState,SVG_ID,SvgAttribute,Value,Pos) :-
1295 ? visb_special_definition(visb_updates,DefName,_Type,BodyTypedExpr,_Class,DefPos),
1296 ? get_visb_DEFINITION_svg_object(visb_updates,DefName,BodyTypedExpr,DefPos,ExpandedBState,SVG_ID,AttrList,_Desc,Pos),
1297 ? member(svg_attribute(SvgAttribute,Value),AttrList).
1298
1299 % check types of VisB updates
1300 check_visb_update_type(Kind,Type,DefName,Class,DefPos) :-
1301 get_update_record_fields_type(Type,Fields),
1302 % other types are STRING +-> STRING, ...; this will be checked in get_visb_DEFINITION_svg_object
1303 (memberchk(field('svg_class',_),Fields)
1304 -> add_warning(visb_visualiser,'VisB updates should not set svg_class:',DefName,DefPos)
1305 ; true),
1306 ? member(field(Name,FieldType),Fields),
1307 (Name = trigger_id ->
1308 (Kind=visb_hovers -> true ;
1309 add_warning(visb_visualiser,'Attribute trigger_id can only be used for hovers:',DefName,DefPos)
1310 )
1311 ; check_attribute_type_and_name(FieldType,Name,DefName,Class,DefPos)
1312 ),
1313 fail.
1314 check_visb_update_type(_,_,_,_,_).
1315
1316 % get a fields type list from a type
1317 get_update_record_fields_type(set(X),Fields) :- !,
1318 get_update_record_fields_type(X,Fields).
1319 get_update_record_fields_type(record(Fields),R) :- !, R=Fields.
1320 get_update_record_fields_type(couple(A,B),Fields) :-
1321 (get_update_record_fields_type(A,Fields)
1322 ; get_update_record_fields_type(B,Fields)).
1323
1324
1325 % check B type of a field for an SVG update or object
1326 check_attribute_type_only(Type,Name,DefName,Class,DefPos) :- alternative_spelling(Name,AName),!,
1327 check_attribute_type_only(Type,AName,DefName,Class,DefPos).
1328 check_attribute_type_only(Type,Name,DefName,Class,DefPos) :- Class \= unknown_svg_class,
1329 check_is_number_attribute(Name,Class,DefPos),!,
1330 (illegal_number_type(Type)
1331 -> ajoin(['Type of field ',Name,' of ', DefName, ' is not a number: '],Msg),
1332 pretty_type(Type,TS),
1333 add_warning(visb_visualiser,Msg,TS,DefPos)
1334 ; true).
1335 check_attribute_type_only(Type,Name,DefName,_,DefPos) :-
1336 is_svg_color_attribute(Name),!,
1337 (illegal_col_type(Type)
1338 -> ajoin(['Type of field ',Name,' of ', DefName, ' is not a color: '],Msg),
1339 pretty_type(Type,TS),
1340 add_warning(visb_visualiser,Msg,TS,DefPos)
1341 ; true).
1342 check_attribute_type_only(_Type,_Name,_DefName,_Class,_DefPos).
1343
1344 check_attribute_type(BType,AttrName,ID,Pos) :-
1345 (svg_id_has_class(ID,Class) -> true ; true),
1346 check_attribute_type_only(BType,AttrName,ID,Class,Pos).
1347 check_attribute_type_and_name(Type,AttrName,DefName,Class,DefPos) :-
1348 check_attribute_type_only(Type,AttrName,DefName,Class,DefPos),
1349 check_attribute_name_is_supported(Class,AttrName,DefPos).
1350
1351 illegal_nr_or_col_type(boolean).
1352 illegal_nr_or_col_type(set(_)).
1353 illegal_nr_or_col_type(seq(_)).
1354 illegal_nr_or_col_type(record(_)).
1355 illegal_nr_or_col_type(couple(A,B)) :- (illegal_nr_or_col_type(A) -> true ; illegal_nr_or_col_type(B)).
1356 % check if couple types can work by concatenating string representations
1357
1358 illegal_col_type(X) :- illegal_nr_or_col_type(X),!.
1359 illegal_col_type(real).
1360 % integer ? or can we map integers to colours using a color scheme like in DOT?
1361
1362 illegal_number_type(X) :- illegal_nr_or_col_type(X),!.
1363 %illegal_number_type(global(_)). % what if we use a global set with weird constants using back-quotes ?
1364
1365 % ----------------
1366
1367 % parse and load an individual JSON VisB item, e.g, :
1368 % { "name":"xscale",
1369 % "value" : "(100.0 / real(TrackElementNumber))"
1370 % }
1371
1372 process_json_definition(File,json(List)) :-
1373 (get_attr_true(ignore,List,File) -> true % ignore this definition
1374 ; process_json_def_lst(File,List)).
1375
1376
1377 process_json_def_lst(File,List) :-
1378 force_del_attr_with_pos(name,List,string(ID),L1,File,NamePos),
1379 force_del_attr_with_pos(value,L1,string(Formula),L2,File,VPos),
1380 debug_format(19,' Processing definition for ~w with value-formula (lines ~w):~w~n',[ID,VPos,Formula]),
1381 !,
1382 set_gen_parse_errors(L2,VPos,GenParseErrors), % treat optional attribute
1383 process_json_definition(ID,NamePos,Formula,VPos,GenParseErrors).
1384 process_json_def_lst(File,List) :- get_pos_from_list(List,File,Pos),
1385 add_error(visb_visualiser,'Illegal VisB Definition:',List,Pos).
1386
1387 % we could also load a classical B Definition file and process its definitions like this:
1388 process_json_definition(ID,NamePos,Formula,VPos,GenParseErrors) :-
1389 atom_codes(Formula,FCodes),
1390 set_error_context(visb_error_context(definition,ID,'value',VPos)),
1391 (b_get_definition_name_with_pos(ID,_Arity,_DefType,_DPos)
1392 -> add_warning(visb_visualiser,'Ignoring VisB definition, DEFINITION of the same name already exists:',ID,NamePos)
1393 ; parse_expr(FCodes,TypedExpr,GenParseErrors)
1394 -> add_visb_json_definition(ID,TypedExpr,VPos)
1395 ; GenParseErrors=false -> formatsilent('Ignoring optional VisB definition for ~w due to error in B formula~n',[ID])
1396 ; add_error(visb_visualiser,'Cannot parse or typecheck VisB formula for definition',ID,VPos)
1397 ),
1398 clear_error_context.
1399
1400 add_visb_json_definition(ID,TypedExpr,VPos) :-
1401 get_texpr_type(TypedExpr,Type),
1402 determine_type_of_visb_formula(TypedExpr,TIds,Class), %write(type(ID,Class,TIds)),nl,
1403 (try_eval_static_def(Class,'static definition', ID,TypedExpr,TIds,StaticValue,VPos)
1404 -> get_texpr_type(TypedExpr,Type),StaticValueExpr = b(value(StaticValue),Type,[]),
1405 assert_visb_json_definition(ID,Type,StaticValueExpr,TIds,Class,VPos)
1406 ; assert_visb_json_definition(ID,Type,TypedExpr,TIds,Class,VPos)
1407 ).
1408
1409 % assert a VisB definition stemming from the JSON definitions section:
1410 assert_visb_json_definition(DefName,Type,TypedExpr,UsedTIds,Class,DefPos) :-
1411 ? (is_special_visb_def_name(DefName,SpecialClass) -> true ; SpecialClass = regular_def),
1412 assertz(visb_definition(DefName,Type,TypedExpr,Class,DefPos,SpecialClass)),
1413 (SpecialClass \= regular_def
1414 -> formatsilent('Detected special ~w definition ~w (~w)~n',[SpecialClass,DefName,Class]),
1415 add_special_json_definition(SpecialClass,DefName,Type,TypedExpr,UsedTIds,Class,DefPos)
1416 ; true
1417 ).
1418
1419 % register some special definitions, in which a set of objects/hovers/updates can be specified
1420 % by a single definition returning a set of records
1421 add_special_json_definition(visb_updates,DefName,Type,TypedExpr,_,Class,DefPos) :- !,
1422 check_visb_update_type(visb_updates,Type,DefName,Class,DefPos),
1423 assertz(visb_special_definition(visb_updates,DefName,Type,TypedExpr,Class,DefPos)). % will be evaluated later
1424 add_special_json_definition(visb_contents,DefName,Type,TypedExpr,_,Class,DefPos) :- !,
1425 assertz(visb_special_definition(visb_contents,DefName,Type,TypedExpr,Class,DefPos)).
1426 add_special_json_definition(visb_box,DefName,Type,TypedExpr,_,Class,DefPos) :- !,
1427 assertz(visb_special_definition(visb_box,DefName,Type,TypedExpr,Class,DefPos)).
1428 add_special_json_definition(visb_objects,DefName,_Type,TypedExpr,_UsedTIds,_Class,DefPos) :- !,
1429 AllowSep=allow_separation, % TODO: we re-determine the class and used ids below:
1430 get_typed_static_definition_with_constants_state(DefName,TypedExpr,ResBody,DefPos,CS,no_inlining,AllowSep),
1431 (get_visb_DEFINITION_svg_object(visb_objects,DefName,ResBody,DefPos,CS,SVG_ID,AttrList,Desc,InnerPos),
1432 %formatsilent('Adding ~w : ~w (from ~w)~n',[visb_objects,SVG_ID,DefName]),
1433 add_visb_object_from_definition(visb_objects, SVG_ID, AttrList, Desc, DefName, InnerPos),
1434 fail ; true).
1435 add_special_json_definition(SpecialClass,DefName,_Type,TypedExpr,UsedTIds,Class,DefPos) :-
1436 (get_unique_initial_state_for_visb(Class,DefName,DefPos,UsedTIds,ConstantsState) -> true
1437 ; ConstantsState=[]),
1438 get_static_visb_state(ConstantsState,FullState), % add static VisB Defs
1439 (Class=requires_variables,
1440 (get_preference(visb_allow_variables_for_objects,false) ; ConstantsState=[])
1441 -> add_warning(visb_visualiser,'Ignoring VisB definition which requires variables:',DefName,DefPos)
1442 ; (get_visb_DEFINITION_svg_object(SpecialClass,DefName,TypedExpr,DefPos,FullState,SVG_ID,AttrList,Desc,InnerPos),
1443 %formatsilent('Adding ~w : ~w (from ~w)~n',[SpecialClass,SVG_ID,DefName]),
1444 add_visb_object_from_definition(SpecialClass, SVG_ID, AttrList, Desc, DefName, InnerPos),
1445 fail % backtrack to add all objects and all hover attributes
1446 ; true)
1447 ).
1448
1449 is_special_visb_def_name(DefName,visb_updates) :- atom_concat('VISB_SVG_UPDATES',_,DefName).
1450 is_special_visb_def_name(DefName,visb_hovers) :- atom_concat('VISB_SVG_HOVERS',_,DefName).
1451 is_special_visb_def_name(DefName,visb_objects) :- atom_concat('VISB_SVG_OBJECTS',_,DefName).
1452 is_special_visb_def_name(DefName,visb_contents) :- atom_concat('VISB_SVG_CONTENTS',_,DefName).
1453 is_special_visb_def_name(DefName,visb_events) :- atom_concat('VISB_SVG_EVENTS',_,DefName).
1454 is_special_visb_def_name('VISB_SVG_BOX',visb_box).
1455 % TODO: maybe provide VISB_HTML_CONTENTS which appear after the SVG
1456
1457 % evaluate static VisB defs only once or evaluate expressions in svg_objects and loop bounds
1458 eval_static_def(Class,VisBKind,ID,TypedExpr,UsedTIds,StaticValue,VPos) :-
1459 get_unique_initial_state_for_visb(Class,ID,VPos,UsedTIds,ConstantsState),
1460 evaluate_static_visb_formula(TypedExpr,VisBKind,ID,ConstantsState,StaticValue,VPos).
1461
1462 % only evaluate statically if possible; no warnings/messages if not possible (just fail)
1463 try_eval_static_def(requires_variables,_VisBKind,_ID,_TypedExpr,_UsedTIds,_StaticValue,_VPos) :- !,
1464 fail. % do not try to evaluate statically; events will change the variable values anyway
1465 try_eval_static_def(Class,VisBKind,ID,TypedExpr,UsedTIds,StaticValue,VPos) :-
1466 % TODO: disable messages for multiple constants values; just fail
1467 eval_static_def(Class,VisBKind,ID,TypedExpr,UsedTIds,StaticValue,VPos).
1468
1469 % check if a unique constant value exists for a model
1470 get_unique_initial_state_for_visb(requires_nothing,_,_,_,State) :- !, State=[].
1471 get_unique_initial_state_for_visb(Class,DefName,DefPos,_,State) :-
1472 \+ multiple_concrete_constants_exist,
1473 is_concrete_constants_state_id(StateID),!,
1474 (Class=requires_variables
1475 -> get_preference(visb_allow_variables_for_objects,true),
1476 unique_initialisation_id_exists_from(StateID,DefName,DefPos,State)
1477 % Note: variable can still be modified by other events later, e.g., in ProB2-UI
1478 ; visited_expression(StateID,StateTerm),
1479 state_corresponds_to_set_up_constants_only(StateTerm,State)).
1480 get_unique_initial_state_for_visb(requires_constants,DefName,DefPos,Ids,State) :- Ids \= all,
1481 % try and see if all values for Ids are the same; TODO: also support requires_variables
1482 is_concrete_constants_state_id(StateID),!,
1483 visited_expression(StateID,StateTerm),
1484 state_corresponds_to_set_up_constants_only(StateTerm,State),
1485 (member(C,Ids), get_texpr_id(C,CstID),
1486 other_constant_value_exists_for(CstID,State,StateID)
1487 -> ajoin(['Multiple values for constant ',CstID, ' in context of'],Msg),
1488 add_visb_lint_message(Msg,DefName,DefPos),fail
1489 ; true).
1490
1491
1492 get_a_constants_state(State,StateID,inline_objects(SingleStateId)) :-
1493 get_constants_state_id_for_id(SingleStateId,StateID),!,
1494 visited_expression(StateID,StateTerm),
1495 state_corresponds_to_set_up_constants_only(StateTerm,State).
1496 get_a_constants_state(State,StateID,InlineObjects) :-
1497 check_inlining(InlineObjects),
1498 is_concrete_constants_state_id(StateID),!,
1499 visited_expression(StateID,StateTerm),
1500 state_corresponds_to_set_up_constants_only(StateTerm,State).
1501
1502 check_inlining(no_inlining) :- !.
1503 check_inlining(inline_objects(_SingleStateId)) :- !.
1504 check_inlining(X) :- add_internal_error('Invalid inlining value:',check_inlining(X)).
1505
1506
1507 other_constant_value_exists_for(CstID,State,StateId) :-
1508 lookup_value_for_existing_id(CstID,State,Val1),
1509 is_concrete_constants_state_id(StateId2), StateId2 \= StateId,
1510 visited_expression(StateId2,StateTerm2),
1511 state_corresponds_to_set_up_constants_only(StateTerm2,State2),
1512 lookup_value_for_existing_id(CstID,State2,Val2),
1513 Val2 \= Val1.
1514
1515
1516 unique_initialisation_id_exists_from(FromStateId,DefName,DefPos,State) :-
1517 transition(FromStateId,_,StateID),!,
1518 (transition(FromStateId,_,StateID2),
1519 StateID2 \= StateID
1520 -> ajoin(['Multiple initialisations exists (state ids ',StateID,',',StateID2,') for'],Msg),
1521 add_visb_lint_message(Msg,DefName,DefPos),fail
1522 ; true),
1523 visited_expression(StateID,StateTerm),
1524 state_corresponds_to_initialised_b_machine(StateTerm,State).
1525
1526 parse_expr_for_visb(Formula,JsonList,Pos,TypedExpr) :-
1527 set_gen_parse_errors(JsonList,Pos,GenParseErrors),
1528 parse_expr(Formula,TypedExpr,GenParseErrors).
1529
1530 set_gen_parse_errors(JSonList,Pos,GenParseErrors) :-
1531 (is_optional_json_item(JSonList)
1532 -> GenParseErrors=false
1533 ; %get_pos_from_list(JSonList,File,Position),
1534 GenParseErrors=gen_parse_errors_for(Pos)).
1535
1536 % ----------------
1537
1538 % parse and load an individual JSON SVG object listed under svg_objects, e.g, :
1539 % {
1540 % "svg_class":"rect",
1541 % "id":"train_rect",
1542 % "x":"0",
1543 % "y":"0",
1544 % "comment":"..."
1545 % },
1546 process_json_svg_object(File,Json) :-
1547 process_repeat(0,Json,File,add_visb_json_svg_object).
1548
1549 add_visb_json_svg_object(File,json(List)) :- get_attr_true(ignore,List,File), !. % ignore this item
1550 add_visb_json_svg_object(File,json(List)) :-
1551 (del_attr_with_pos(svg_class,List,string(SVG_Class),L1,File,Pos1) -> true % shape: line, rect, ...
1552 ; infer_svg_class(List,SVG_Class,File,Pos1)
1553 -> add_visb_lint_message('Inferred svg_class',SVG_Class,Pos1),L1=List
1554 ; force_del_attr_with_pos(svg_class,List,string(SVG_Class),L1,File,Pos1) % generates error message
1555 ),
1556 force_del_attr(id,L1,string(ID),L2,File),
1557 (del_attr(comment,L2,string(Desc),JsonList3) -> true ; Desc = '', JsonList3=L2),
1558 debug_format(19,' Creating new SVG object ~w with id:~w~n',[SVG_Class,ID]),
1559 add_debug_message(visb_visualiser,'New object: ',ID,Pos1),
1560 % TODO: we could pre-process all attributes not depending on %0, %1, ...
1561 maplist(get_svg_attr(ID,SVG_Class,File),JsonList3,AttrList),
1562 %formatsilent('Adding SVG Object ~w with ID ~w (~w): ~w~n',[SVG_Class,ID,Desc,AttrList]),
1563 assert_visb_svg_object(ID,SVG_Class,AttrList,Desc,'JSON',Pos1),
1564 !.
1565 add_visb_json_svg_object(File,Json) :-
1566 !,
1567 get_pos_from_list(Json,File,Pos),
1568 add_error(visb_visualiser,'Illegal VisB additional SVG object:',Json,Pos).
1569
1570 infer_svg_class(List,line,File,Pos) :- get_attr_with_pos(x1,List,_,File,Pos),!.
1571 infer_svg_class(List,circle,File,Pos) :- get_attr_with_pos(r,List,_,File,Pos),!.
1572 infer_svg_class(List,rect,File,Pos) :- get_attr_with_pos(height,List,_,File,Pos),!.
1573 infer_svg_class(List,rect,File,Pos) :- get_attr_with_pos(width,List,_,File,Pos),!.
1574 infer_svg_class(List,ellipse,File,Pos) :- get_attr_with_pos(rx,List,_,File,Pos),!. % could also be rect
1575 infer_svg_class(List,text,File,Pos) :- get_attr_with_pos(text,List,_,File,Pos),!.
1576 infer_svg_class(List,g,File,Pos) :- get_attr_with_pos(children,List,_,File,Pos),!. % group + foreignObject have children
1577 infer_svg_class(List,use,File,Pos) :- get_attr_with_pos(href,List,_,File,Pos),!. % href can also be used with image, pattern, ...
1578
1579 % assert a new SVG object to be created upon SETUP_CONSTANTS/INITIALISATION:
1580 assert_visb_svg_object(ID,SVG_Class,_AttrList,_Desc,DefName,Pos1) :-
1581 visb_svg_object(ID,_,_,_,_Pos0),!,
1582 ajoin(['SVG object (from ',DefName,', svg_class=',SVG_Class,') with same id already created: '],Msg),
1583 add_warning(visb_visualiser,Msg,ID,Pos1).
1584 assert_visb_svg_object(_ID,_SVG_Class,AttrList,_Desc,_DefName,Pos1) :-
1585 \+ (AttrList=[] ; AttrList=[_|_]),!,
1586 add_error(visb_visualiser,'SVG objects attributes are not a list:',AttrList,Pos1).
1587 assert_visb_svg_object(ID,SVG_Class,AttrList,Desc,DefName,Pos1) :-
1588 add_visb_debug_infos(ID,SVG_Class,AttrList,AttrList2,DefName,Pos1),
1589 sort(AttrList2,SAttrList),
1590 get_all_children(SAttrList,Children,RestAttrs,DefName,Pos1),
1591 (Children = [] -> true
1592 ? ; svg_shape_can_have_children(SVG_Class) -> true % g, foreignObject, symbol, HTML tags
1593 ; Children = [C1_ID|_], visb_svg_object(C1_ID,CAttr,_,_,_),
1594 attribute_automatically_creates_children(CAttr) % e.g., title object created for title attribute of ID
1595 -> true
1596 ; add_visb_lint_message('Children attribute only useful for certain SVG classes such as groups (g) or foreignObject or when adding title objects (as tooltips)',Children,Pos1)
1597 ),
1598 ? (select(svg_attribute(group_id,ParentGroupId),RestAttrs,RestAttrs2)
1599 -> assert_visb_svg_child(ParentGroupId,Pos1,ID) % register parent group id
1600 ; RestAttrs2=RestAttrs),
1601 check_svg_shape_class(ID,SVG_Class,Pos1), % after registering group_id, so that the parent is known
1602 compute_auto_attributes(RestAttrs2,RestAttrs2,SVG_Class,NewRestAttrs),
1603 assertz(visb_svg_object(ID,SVG_Class,NewRestAttrs,Desc,Pos1)),
1604 maplist(assert_visb_svg_child(ID,Pos1),Children). % add all children
1605
1606 % adapt visb_svg_object to provide developer debug infos, e.g., in title hover messages
1607 add_visb_debug_infos(ID,SVG_Class,AttrList,NewAttrList,DefName,Pos) :- SVG_Class \= title,
1608 get_preference(visb_show_debug_infos,true),
1609 (select(svg_attribute(title,Title),AttrList,RestAttrs) -> true
1610 ; Title='', RestAttrs=AttrList),
1611 !,
1612 extract_span_description_with_opts(Pos,PosStr,
1613 [relative_file_name,compact_pos_info,
1614 inner_context_separator('\n in ','\n at ')]),
1615 %extract_definition_call_stack_desc(Pos,CS), already displayed in inner context
1616 ajoin(['-- VISB_DEBUG_INFO --\nid=',ID, '\nsvg_class=',SVG_Class, '\nsrc=',DefName,'\n', PosStr],DebugInfo),
1617 (Title='' -> NewTitle=DebugInfo ; ajoin([Title,'\n',DebugInfo],NewTitle)),
1618 %format('~nDEBUG::~n~w~n~w~n---~n',[ID,DebugInfo]),
1619 assert(visb_svg_object_debug_info(ID,DebugInfo)),
1620 NewAttrList = [svg_attribute(title,NewTitle)|RestAttrs].
1621 add_visb_debug_infos(_,_,AttrList,AttrList,_,_).
1622
1623 % compute the value of VisB auto attributes, e.g., automatically
1624 % doing a layout of objects in a grid
1625 % TODO: SVG/special_svg_number already supports auto but not for x,y coordinates?
1626 % TODO: try and determine width of object automatically; keep track of auto objects max height per row
1627 % TODO: support cx,cy
1628 compute_auto_attributes([],_,_,[]).
1629 compute_auto_attributes([svg_attribute(Attr,Val)|T],AllAttrs,SVG_Class,
1630 [svg_attribute(Attr,NewVal)|NT]) :-
1631 (Val=auto, compute_auto_attribute(Attr,AllAttrs,SVG_Class,NewVal)
1632 -> true
1633 ; NewVal=Val),
1634 compute_auto_attributes(T,AllAttrs,SVG_Class,NT).
1635
1636 :- dynamic next_auto_x/1, next_auto_y/2.
1637 next_auto_x(0).
1638 next_auto_y(0,0). % y coordinate and height of the current row of automatically placed objects
1639 reset_auto_attrs :- retractall(next_auto_x(_)),
1640 retractall(next_auto_y(_,_)),
1641 assertz(next_auto_x(0)),
1642 assertz(next_auto_y(0,0)).
1643
1644 get_x_auto_limit(Limit) :- visb_empty_svg_box_height_width(_H,W,_ViewBox),!,
1645 Limit = W. %TODO: inspect viewBox
1646 get_x_auto_limit(400).
1647
1648 compute_auto_attribute(id,_,_,GenID) :- !, gensym(svg_id,GenID).
1649 % Note: x/cx is computed before y/cy (attributes are sorted), so that y/cy uses the row chosen here
1650 compute_auto_attribute(Attr,AllAttrs,SVG_Class,Val) :-
1651 auto_compute_x_coordinate(Attr,Kind),!,
1652 (get_width(SVG_Class,AllAttrs,Width) -> true ; Width=10),
1653 (get_height(SVG_Class,AllAttrs,Height) -> true ; Height=10),
1654 Padding = 2, % TODO: make customisable
1655 get_x_auto_limit(Lim),
1656 retract(next_auto_x(XV)),
1657 retract(next_auto_y(Y,RowHeight)),
1658 (XV > 0, XV+Width > Lim % object does not fit into the current row: start a new row
1659 -> X=0, NewY is Y+RowHeight, NewRowHeight is Height+Padding
1660 ; X=XV, NewY=Y, NewRowHeight is max(RowHeight,Height+Padding)),
1661 NewXV is X+Width+Padding,
1662 assertz(next_auto_x(NewXV)),
1663 assertz(next_auto_y(NewY,NewRowHeight)),
1664 (Kind=left -> Val=X ; Val is X + Width/2).
1665 compute_auto_attribute(Attr,AllAttrs,SVG_Class,Val) :- auto_compute_y_coordinate(Attr,Kind),
1666 next_auto_y(YV,_),
1667 (Kind = top -> Val = YV
1668 ; get_height(SVG_Class,AllAttrs,Height) -> Val is YV + Height/2
1669 ; Val is YV+5).
1670
1671 auto_compute_x_coordinate(x,left). % rect, ..
1672 auto_compute_x_coordinate(cx,center). % circle, ellipse
1673 auto_compute_y_coordinate(y,top). % rect, ..
1674 auto_compute_y_coordinate(cy,center). % circle, ellipse
1675
1676 get_width(rect,Attrs,Wid) :- get_number_attr(width,Attrs,Wid).
1677 get_width(circle,Attrs,Wid) :- get_number_attr(r,Attrs,Radius), Wid is 2*Radius.
1678 get_width(ellipse,Attrs,Wid) :- get_number_attr(rx,Attrs,Radius), Wid is 2*Radius.
1679 get_width(line,Attrs,Wid) :- get_number_attr(x1,Attrs,X1),
1680 get_number_attr(x2,Attrs,X2), Wid is abs(X2-X1).
1681
1682 get_height(rect,Attrs,Wid) :- get_number_attr(height,Attrs,Wid).
1683 get_height(circle,Attrs,Wid) :- get_number_attr(r,Attrs,Radius), Wid is 2*Radius.
1684 get_height(ellipse,Attrs,Wid) :- get_number_attr(ry,Attrs,Radius), Wid is 2*Radius.
1685 get_height(line,Attrs,Wid) :- get_number_attr(y1,Attrs,X1),
1686 get_number_attr(y2,Attrs,X2), Wid is abs(X2-X1).
1687
1688 % get the value of an attribute as a number; fails for values such as 50% or auto
1689 get_number_attr(Attr,Attrs,Nr) :- member(svg_attribute(Attr,Val),Attrs),
1690 (number(Val) -> Nr=Val
1691 ; atom(Val), atom_codes(Val,Codes), catch(number_codes(Nr,Codes),_,fail)).
1692
1693
1694 % register an SVG ID as child of a group
1695 assert_visb_svg_child(_ParentId,Pos,ChildID) :- visb_svg_child(ChildID,OldParent),!,
1696 ajoin(['SVG object already child of group ',OldParent,': '],Msg),
1697 add_warning(visb_visualiser,Msg,ChildID,Pos).
1698 assert_visb_svg_child(ParentId,Pos,ChildID) :- visb_svg_child(ParentId,ChildID),!, % TODO: full cycle detection
1699 ajoin(['SVG object already parent of group ',ParentId,' (cycle): '],Msg),
1700 add_warning(visb_visualiser,Msg,ChildID,Pos).
1701 assert_visb_svg_child(ParentId,_Pos,ChildID) :-
1702 assertz(visb_svg_child(ChildID,ParentId)),
1703 assertz(visb_svg_parent(ParentId,ChildID)).
1704
1705
1706 % we need to translate a title attribute to a separate title object and add it as a child
1707 % see also post_process_visb_item which dispatches title updates to the child object
1708 get_all_children(SAttrList,Children,Rest,DefName,Pos) :-
1709 ? select(svg_attribute(title,Title),SAttrList,SList2),!,
1710 add_virtual_title_object(Title,DefName,Pos,TitleID),
1711 Children=[TitleID|TChilds],
1712 get_children_attribute(SList2,TChilds,Rest,Pos).
1713 get_all_children(SAttrList,Children,Rest,_,Pos) :-
1714 % get the children explicitly defined by the user:
1715 get_children_attribute(SAttrList,Children,Rest,Pos).
1716
1717 add_virtual_title_object(Title,DefName,Pos,TitleID) :-
1718 gensym(visb_title,TitleID),
1719 assert_visb_svg_object(TitleID,'title',[svg_attribute(text,Title)],'',DefName,Pos),
1720 add_debug_message(visb_visualiser,'Adding virtual title child object: ',TitleID,Pos).
1721
1722 get_children_attribute(SAttrList,Children,Rest,Pos) :-
1723 ? select(svg_attribute(children,C),SAttrList,R),!,
1724 (is_list(C) -> Children=C
1725 ; add_warning(visb_visualiser,'Children attribute should be a list (of SVG ids): ',C,Pos),
1726 Children=[]),
1727 Rest=R.
1728 get_children_attribute(SAttrList,[],SAttrList,_).
1729
1730
1731 svg_shape_can_have_children(defs).
1732 svg_shape_can_have_children(foreignObject).
1733 svg_shape_can_have_children(g).
1734 svg_shape_can_have_children(pattern).
1735 svg_shape_can_have_children(symbol).
1736 svg_shape_can_have_children(X) :- is_html_tag(X).
1737
1738
1739 % check whether svg_class value seems valid
1740 check_svg_shape_class(ID,Shape,Pos) :-
1741 (is_svg_shape_class(Shape) -> true
1742 ; is_html_tag(Shape) ->
1743 (visb_svg_child(ID,ParentId),
1744 visb_svg_object(ParentId,ParClass,_,_,_),
1745 (is_html_tag(ParClass) -> true ; ParClass=foreignObject)
1746 -> add_debug_message(visb_visualiser,'HTML tag used as svg_class in foreignObject child: ',Shape,Pos)
1747 ; ajoin(['SVG object ', ID, ' has HTML tag as svg_class (and should probably be a child of a foreignObject)'],Msg),
1748 add_visb_lint_message(Msg,Shape,Pos) % probably should be child of foreignObject
1749 )
1750 ; dotshape2svg_class(Shape,Class)
1751 -> ajoin(['SVG object ', ID, ' has unknown svg_class (did you mean ',Class,' ?): '],Msg),
1752 add_warning(visb_visualiser,Msg,Shape,Pos)
1753 ; get_all_svg_classes(Classes),
1754 get_possible_fuzzy_matches_and_completions_msg(Shape,Classes,FMsg)
1755 -> ajoin(['SVG object ', ID, ' has unknown svg_class (did you mean ',FMsg,' ?): '],Msg),
1756 add_warning(visb_visualiser,Msg,Shape,Pos)
1757 ; ajoin(['SVG object ', ID, ' has unknown svg_class'],Msg),
1758 add_visb_lint_message(Msg,Shape,Pos)
1759 ).
1760
1761 % get SVG attribute for a JSON svg_object:
1762 get_svg_attr(ID,SVG_Class,File,'='(Attr,AttrVal,Pos),svg_attribute(Attr,Val)) :- !,
1763 construct_prob_pos_term(Pos,File,PosTerm),
1764 get_svg_static_attribute_value(Attr,ID,SVG_Class,PosTerm,AttrVal,Val),
1765 check_attribute_name_is_supported(SVG_Class,Attr,PosTerm).
1766 get_svg_attr(_,_,File,Json,_) :-
1767 get_pos_from_list([Json],File,Position),
1768 add_error(visb_visualiser,'Illegal SVG object attribute',Json,Position),fail.
1769
1770 get_svg_static_attribute_value(Attr,ID,SVG_Class,Pos,AttrVal,Nr) :-
1771 check_is_number_attribute(Attr,SVG_Class,Pos),
1772 \+ is_special_svg_number_form(AttrVal),
1773 !,
1774 (get_number_value(AttrVal,Nr,Attr,Pos) -> true
1775 ; ajoin(['Illegal number value for attribute ',Attr, ' of svg_object ',ID,':'],Msg),
1776 add_error(visb_visualiser,Msg,AttrVal,Pos), Nr=0).
1777 get_svg_static_attribute_value(_,_,_,_,string(Val),Res) :- !, Res=Val.
1778 % for children we get something like array([string(button1_1),string(button1_2)])
1779 get_svg_static_attribute_value(Attr,ID,SVG_Class,Pos,array(List),ResList) :- !,
1780 maplist(get_svg_static_attribute_value(Attr,ID,SVG_Class,Pos),List,ResList).
1781 get_svg_static_attribute_value(Attr,ID,_,Pos,Val,Val) :-
1782 ajoin(['Illegal value for attribute ',Attr, ' of svg_object ',ID,':'],Msg),
1783 add_warning(visb_visualiser,Msg,Val,Pos).
1784
1785
1786 % detect special SVG number forms, like 50%
1787 is_special_svg_number_form(string(Atom)) :-
1788 atom_codes(Atom,Codes),
1789 special_svg_number(Codes,[]).
1790
1791
1792 :- assert_must_succeed(visb_visualiser:special_svg_number("10%",[])).
1793 :- assert_must_succeed(visb_visualiser:special_svg_number("10 %",[])).
1794 :- assert_must_succeed(visb_visualiser:special_svg_number(" 99em ",[])).
1795 :- assert_must_succeed(visb_visualiser:special_svg_number("1.0em",[])).
1796 :- assert_must_fail(visb_visualiser:special_svg_number("10",[])).
1797 :- assert_must_fail(visb_visualiser:special_svg_number("10+nrcols+%0",[])).
1798 special_svg_number --> " ",!, special_svg_number.
1799 special_svg_number --> [X], {digit(X)},!, special_svg_number2.
1800 special_svg_number --> "auto".
1801 special_svg_number2 --> [X], {digit(X)},!, special_svg_number2.
1802 special_svg_number2 --> ".",!, special_svg_number2. % TODO: only allow one dot; + accept e notation?
1803 ?special_svg_number2 --> optws,!, svg_unit,!, optws.
1804
1805 % using info from https://oreillymedia.github.io/Using_SVG/guide/units.html
1806 svg_unit --> "%".
1807 svg_unit --> "ch".
1808 svg_unit --> "cm".
1809 svg_unit --> "em".
1810 svg_unit --> "ex".
1811 svg_unit --> "in".
1812 svg_unit --> "mm".
1813 svg_unit --> "pc".
1814 svg_unit --> "pt".
1815 svg_unit --> "px".
1816 svg_unit --> "deg". % angle units
1817 svg_unit --> "grad".
1818 svg_unit --> "rad".
1819 svg_unit --> "turn".
1820 svg_unit --> "rem". % root em
1821 svg_unit --> "vh". % viewport height unit (1%)
1822 svg_unit --> "vw". % viewport width unit (1%)
1823 svg_unit --> "vmin". % min ov vh, vw
1824 svg_unit --> "vmax". % max ov vh, vw
1825
1826
1827 optws --> " ",!,optws.
1828 optws --> [].
1829
1830 digit(X) :- X >= 48, X =< 57.
1831
1832 is_svg_number_attribute(Attr) :- is_svg_number_attribute(Attr,_).
1833
1834
1835 % some checks to see whether the attribute is supported by the given SVG class
1836
1837 check_attribute_is_supported(SVG_Class,DefPos,svg_attribute(Name,_V)) :- !,
1838 (nonvar(SVG_Class), check_is_number_attribute(Name,SVG_Class,DefPos) -> true
1839 % TODO: check color and other attributes, see also check_attribute_type
1840 ; check_attribute_name_is_supported(SVG_Class,Name,DefPos)).
1841 check_attribute_is_supported(S,DefPos,F) :-
1842 add_internal_error('Illegal call:',check_attribute_is_supported(S,DefPos,F)).
1843
1844 % if these special attributes occur here, something went wrong, e.g.,
1845 % when separating static/dynamic parts
1846 check_unsupported_special_visb_attribute(visb_objects,_,_) :- !,fail. % here we can process everything
1847 check_unsupported_special_visb_attribute(SpecialClass,DefPos,svg_attribute(Name,_)) :-
1848 special_visb_attribute_not_allowed_in_dynamic_objects(Name,SpecialClass,Kind),
1849 ajoin(['The VisB special attribute ',Name,' cannot be used here (',SpecialClass,' use separate ',Kind,' definition): '],Msg),
1850 add_warning(visb_visualiser,Msg,Name,DefPos).
1851
1852 special_visb_attribute_not_allowed_in_dynamic_objects(hover,SC,'VISB_SVG_HOVERS') :- SC \= visb_hovers.
1853 special_visb_attribute_not_allowed_in_dynamic_objects(hovers,SC,'VISB_SVG_HOVERS') :- SC \= visb_hovers.
1854 special_visb_attribute_not_allowed_in_dynamic_objects(event,SC,'VISB_SVG_EVENTS') :- SC \= visb_events.
1855 special_visb_attribute_not_allowed_in_dynamic_objects(events,SC,'VISB_SVG_EVENTS') :- SC \= visb_events.
1856 special_visb_attribute_not_allowed_in_dynamic_objects(predicate,SC,'VISB_SVG_EVENTS') :- SC \= visb_events.
1857 special_visb_attribute_not_allowed_in_dynamic_objects(predicates,SC,'VISB_SVG_EVENTS') :- SC \= visb_events.
1858 special_visb_attribute_not_allowed_in_dynamic_objects(preds,SC,'VISB_SVG_EVENTS') :- SC \= visb_events.
1859 special_visb_attribute_not_allowed_in_dynamic_objects(execution_kind,SC,'VISB_SVG_EVENTS') :- SC \= visb_events.
1860
1861
1862 check_attribute_name_is_supported(SVG_Class,Name,DefPos) :-
1863 nonvar(SVG_Class),
1864 is_html_tag(SVG_Class),
1865 \+ is_svg_shape_class(SVG_Class), % script and title can be both SVG and HTML
1866 !,
1867 (is_html_attribute(Name)
1868 -> true % TODO: we could check whether attribute appropriate for Tag
1869 ; is_virtual_svg_attribute(Name) -> true % Virtual VisB attribute like children, text, ...
1870 ; ajoin(['Unknown HTML attribute used inside HTML tag ',SVG_Class],Msg),
1871 add_visb_lint_message(Msg,Name,DefPos)
1872 ).
1873 check_attribute_name_is_supported(_SVG_Class,Name,DefPos) :-
1874 check_attribute_name_is_supported(Name,DefPos).
1875
1876 check_attribute_name_is_supported(Name,DefPos) :-
1877 ? \+ is_svg_attribute(Name),
1878 !,
1879 (fix_svg_attribute(Name,GraphVizTranslName)
1880 -> ajoin(['Unknown SVG attribute (maybe you want to use ',GraphVizTranslName,' ?): '],Msg),
1881 add_warning(visb_visualiser,Msg,Name,DefPos)
1882 ; get_all_svg_attributes(Attrs),
1883 (get_possible_fuzzy_matches_and_completions_msg(Name,Attrs,FMsg)
1884 -> ajoin(['Unknown SVG attribute (did you mean ',FMsg,' ?): '],Msg),
1885 add_warning(visb_visualiser,Msg,Name,DefPos) % create warning, as it is likely we have made an error
1886 ; is_dot_attribute(Name)
1887 -> add_warning(visb_visualiser,'This Dot/Graphviz attribute is not a valid SVG attribute: ',Name,DefPos)
1888 ; get_preference(visb_strict_checking,false)
1889 -> add_visb_lint_message('Unknown SVG attribute',Name,DefPos)
1890 ; add_warning(visb_visualiser,'Unknown SVG attribute (set VISB_LINT to FALSE to avoid warning): ',Name,DefPos)
1891 )
1892 ).
1893 check_attribute_name_is_supported(_,_).
1894
1895 % add message or warning, depending on VISB_LINT setting
1896 add_visb_lint_message(Msg,Obj,Pos) :- get_preference(visb_strict_checking,false), !,
1897 ajoin([Msg,': '],Msg2),
1898 add_message(visb_visualiser,Msg2,Obj,Pos).
1899 add_visb_lint_message(Msg,Obj,Pos) :-
1900 ajoin([Msg,' (set VISB_LINT to FALSE to avoid warning): '],Msg2),
1901 add_warning(visb_visualiser,Msg2,Obj,Pos).
1902
1903
1904 % succeed if attribute is a number attribute and perform a check the attribute is supported by the class
1905 check_is_number_attribute(Attr,Class,Pos) :-
1906 is_svg_number_attribute(Attr,ClassList),
1907 (nonvar(Class), nonmember(Class,ClassList)
1908 -> (equivalent_attribute(Attr,List2,NewAttr), member(Class,List2)
1909 -> ajoin(['The number attribute ',Attr,' is not supported for SVG class ',Class,', use: '],Msg),
1910 add_warning(visb_visualiser,Msg,NewAttr,Pos)
1911 ; ajoin(['The number attribute ',Attr,' is not supported for this SVG class: '],Msg),
1912 add_warning(visb_visualiser,Msg,Class,Pos)
1913 )
1914 ; true
1915 ).
1916
1917 % optionally rewrite record field to alternative spelling
1918 opt_rewrite_field_name(Name,Res) :-
1919 (alternative_spelling(Name,Alt) -> Res=Alt ; Res=Name).
1920
1921 % these alternatives are useful e.g. in TLA+ without backquote:
1922 alternative_spelling(colour,'color').
1923 alternative_spelling(fill_opacity,'fill-opacity').
1924 alternative_spelling(fill_rule,'fill-rule').
1925 alternative_spelling(font_family,'font-family').
1926 alternative_spelling(font_size,'font-size').
1927 alternative_spelling(font_style,'font-style').
1928 alternative_spelling(font_variant,'font-variant').
1929 alternative_spelling(font_weight,'font-weight').
1930 alternative_spelling(marker_end,'marker-end').
1931 alternative_spelling(marker_start,'marker-start').
1932 alternative_spelling(stroke_opacity,'stroke-opacity').
1933 alternative_spelling(stroke_dasharray,'stroke-dasharray').
1934 alternative_spelling(stroke_linecap,'stroke-linecap').
1935 alternative_spelling(stroke_linejoin,'stroke-linejoin').
1936 alternative_spelling(stroke_width,'stroke-width').
1937
1938
1939 % used to suggest fixing unsupported attributes in SVG objects:
1940 equivalent_attribute(cx,[rect],x).
1941 equivalent_attribute(cy,[rect],y).
1942 equivalent_attribute(x,[circle,ellipse],cx).
1943 equivalent_attribute(x,[line],x1).
1944 equivalent_attribute(y,[circle,ellipse],cy).
1945 equivalent_attribute(y,[line],y1).
1946 equivalent_attribute(r,[rect],rx). % ry also exists
1947 equivalent_attribute(rx,[circle],r).
1948 equivalent_attribute(width,[circle],r).
1949 equivalent_attribute(width,[ellipse],rx).
1950 equivalent_attribute(height,[ellipse],ry).
1951
1952
1953 % ----------------
1954
1955 % parse and load an individual JSON VisB item, e.g, :
1956 % {
1957 % "id":"train_info_text",
1958 % "attr":"x",
1959 % "value":"real(train_rear_end)*100.0/real(TrackElementNumber+1)",
1960 % "comment":"move info field above train"
1961 % },
1962 process_visb_json_item(File,Json) :-
1963 process_repeat(0,Json,File,add_visb_json_item).
1964
1965 add_visb_json_item(File,json(List)) :-
1966 get_attr_true(ignore,List,File), % ignore this item
1967 !.
1968 add_visb_json_item(File,json(List)) :-
1969 del_attr_with_pos(id,List,string(ID),L1,File,PosStartOfItem),
1970 !,
1971 process_visb_json_item_id(File,L1,ID,PosStartOfItem).
1972 add_visb_json_item(File,Json) :-
1973 !,
1974 get_pos_from_list(Json,File,Pos),
1975 add_error(visb_visualiser,'VisB Item has no id attribute:',Json,Pos).
1976
1977 process_visb_json_item_id(File,L1,ID,PosStartOfItem) :-
1978 del_attr_with_pos(attr,L1,string(Attr),L2,File,AttrPos), % VisB has attr and value infos
1979 force_del_attr_with_pos(value,L2,string(Formula),L3,File,PosFormula),
1980 !,
1981 ? (del_attr(comment,L3,string(Desc),L4) -> true ; Desc = '',L4=L3),
1982 debug_format(19,' Processing id:~w attr:~w : value:~w desc:~w~n',[ID,Attr,Formula,Desc]),
1983 actually_add_visb_json_item(ID,Attr,AttrPos,Formula,Desc,L4,PosStartOfItem,PosFormula,File).
1984 process_visb_json_item_id(File,L1,ID,PosStartOfItem) :-
1985 get_attr_with_pos(SomeSVGAttr,L1,_V,File,_),
1986 ? is_svg_attribute(SomeSVGAttr),
1987 debug_format(19,'Detected new style VisB JSON declaration with id:~w and SVG attr:~w~n',[ID,SomeSVGAttr]),
1988 !,
1989 % object is in new format with multiple attributes "x":"0" rather than "attr":"x", "value":"0"
1990 (del_attr(comment,L1,string(Desc),L2) -> true ; Desc = '',L2=L1),
1991 ? (non_det_del_attr_with_pos(SVGATTR,L2,string(Formula),L3,File,PosFormula),
1992 is_visb_item_svg_attribute(PosFormula,SVGATTR,Formula),
1993 debug_format(19,' -> Processing new style id:~w attr:~w : value:~w desc:~w~n',[ID,SVGATTR,Formula,Desc]),
1994 actually_add_visb_json_item(ID,SVGATTR,PosFormula,Formula,Desc,L3,PosStartOfItem,PosFormula,File),
1995 fail
1996 ;
1997 true).
1998 process_visb_json_item_id(_File,_JsonList,ID,PosStartOfItem) :-
1999 !,
2000 add_error(visb_visualiser,'Illegal VisB Item without attr and value fields:',ID,PosStartOfItem).
2001
2002 is_visb_item_svg_attribute(Pos,Attr,Val) :-
2003 Attr \= override, % so we ignore a possible override attribute
2004 ? (is_svg_attribute(Attr) -> true
2005 ; add_warning(visb_visualiser,'Ignoring unknown attribute: ',Attr=Val,Pos),
2006 fail
2007 ).
2008
2009
2010 actually_add_visb_json_item(ID,Attr,AttrPos,Formula,Desc,JsonList,PosStartOfItem,PosFormula,File) :-
2011 set_error_context(visb_error_context(item,ID,Attr,PosFormula)),
2012 check_attribute_name_is_supported(Attr,AttrPos),
2013 atom_codes(Formula,FormulaC),
2014 (parse_expr_for_visb(FormulaC,JsonList,PosFormula,TypedExpr)
2015 % TO DO: if optional attribute present: avoid generating errors in b_parse_machine_expression_from_codes
2016 -> get_texpr_type(TypedExpr,BType),
2017 check_attribute_type(BType,Attr,ID,PosFormula),
2018 assert_visb_item(ID,Attr,TypedExpr,Desc,PosStartOfItem,JsonList,File)
2019 ; is_optional_json_item(JsonList,File,Pos)
2020 -> formatsilent('Ignoring optional VisB item for ~w (lines ~w) due to error in B formula~n',[ID,Pos])
2021 ; ajoin(['Cannot parse or typecheck VisB formula for ',ID,' and attribute ',Attr,':'],Msg),
2022 add_error(visb_visualiser,Msg,Formula,PosFormula)
2023 ), clear_error_context.
2024
2025
2026 % assert a VISB item (aka VISB_SVG_UPDATE)
2027 assert_visb_item(ID,Attr,_TypedExpr,_Desc,PosStartOfItem,_,_) :-
2028 visb_item(ID,Attr,_,_,_,Pos2,Meta2),
2029 !,
2030 get_file_name_msg(PosStartOfItem,From1,File1),
2031 (member(override,Meta2)
2032 -> ajoin(['Overriding VisB item',From1,File1,' for same SVG ID ',ID,' and attribute '],Msg),
2033 add_debug_message(visb_visualiser,Msg,Attr,Pos2)
2034 ; ajoin(['Overriding VisB item',From1,File1,' for same SVG ID ',ID,
2035 '. Add an \"override\" attribute to remove this warning. Overriden attribute '],Msg),
2036 add_warning(visb_visualiser,Msg,Attr,Pos2),
2037 % error_context already includes attribute and ID
2038 add_message(visb_visualiser,'Overriding this stored formula for ',ID,PosStartOfItem) % provide detailed info
2039 ).
2040 assert_visb_item(ID,Attr,_TypedExpr,_Desc,PosStartOfItem,_JsonList,_File) :-
2041 illegal_attribute_for_visb_item(ID,Attr),!,
2042 add_warning(visb_visualiser,'This attribute cannot be modified in a VisB item: ',Attr,PosStartOfItem).
2043 assert_visb_item(ID,Attr,TypedExpr,Desc,PosStartOfItem,JsonList,File) :-
2044 (get_attr_true(override,JsonList,File) -> Meta = [override] ; Meta=[]),
2045 % determine_type_of_visb_formula(TypedExpr,_,Class) now does take definitions into account; TODO: save info
2046 find_identifier_uses(TypedExpr,[],UsedIds),
2047 assertz(visb_item(ID,Attr,TypedExpr,UsedIds,Desc,PosStartOfItem,Meta)).
2048
2049 illegal_attribute_for_visb_item(_,children).
2050 illegal_attribute_for_visb_item(_,group_id).
2051 illegal_attribute_for_visb_item(_,id).
2052 illegal_attribute_for_visb_item(_,svg_class).
2053
2054
2055 get_file_name_msg(Pos1,' from ',File1) :- extract_tail_file_name(Pos1,File1),!.
2056 get_file_name_msg(_,'',''). % no file info
2057
2058 % ----------------
2059 % FOR-LOOP / REPEAT processing
2060
2061 %process_repeat(RepCounter, Json, FinalCall to be executed repeatedly for each instance with File and Json, File)
2062 % RepCounter tells us which of %0, %1, ... should be replaced next
2063
2064 process_repeat(RepCount,json(JsonList),File,FinalCall) :-
2065 ? non_det_del_attr_with_pos(FOR_REP,JsonList,Value,RestJsonList,File,ForRepPos),
2066 % find first for or repeat and process it
2067 (FOR_REP=for ; FOR_REP=repeat),
2068 !,
2069 process_first_repeat(FOR_REP,Value,ForRepPos,RepCount,json(RestJsonList),File,FinalCall).
2070 process_repeat(_RepCount,Json,File,FinalCall) :-
2071 % we have processed all for/repeat loops: now perform the final call on the transformed codes
2072 call(FinalCall,File,Json).
2073
2074 process_first_repeat(for,json(ForList),ForPos,RepCount,JsonData,File,FinalCall) :-
2075 !, % we have a for loop like "for": {"from":1, "to":4}
2076 force_get_attr_nr(from,ForList,From,File),
2077 force_get_attr_nr(to,ForList,To,File),
2078 (get_attr_with_pos(step,ForList,StepAttrVal,File,StepPos),
2079 get_number_value(StepAttrVal,Step,step,StepPos)
2080 -> true % there is an explicit step attribute
2081 ; Step=1),
2082 debug_format(19,' -> Iterating %~w from ~w to ~w with step ~w~n',[RepCount,From,To,Step]),
2083 R1 is RepCount+1,
2084 number_codes(RepCount,Pat),
2085 check_between(From,To,Step,ForPos), % check whether step value makes sense
2086 ? ( between(From,To,Step,IterElem),
2087 number_codes(IterElem,IterElemC), % we will replace the text %Pat by the text of the number IterElem
2088 replace_in_json(Pat,IterElemC,JsonData,ReplacedJsonData), % replace within other for/repeat loops, but also other attributes
2089 process_repeat(R1,ReplacedJsonData,File,FinalCall), % now process next loop or finish
2090 fail
2091 ; true).
2092 process_first_repeat(repeat,array(RepList),RepPos,RepCount,JsonData,File,FinalCall) :-
2093 !, % we have a repetition like "repeat": ["tr1","tr2"] or "repeat": [ ["1","2"] , ...]
2094 (RepList=[array(L1)|_], length(L1,Len)
2095 -> NewRepCount is RepCount+Len, % we have a multi-repeat with list of values to be replaced
2096 LastRepCount is NewRepCount-1,
2097 debug_format(19,' -> Iterating repeat (%~w,...,%~w) over ~w~n',[RepCount,LastRepCount,RepList]),
2098 check_all_same_length_json(RepList,Len,RepPos)
2099 ; NewRepCount is RepCount+1, % next replacement starts at $(RepCount+1)
2100 debug_format(19,' -> Iterating repeat (%~w) over ~w~n',[RepCount,RepList])
2101 ),
2102 ? ( member(IterElem,RepList),
2103 multi_replace_in_json(IterElem,RepCount,JsonData,ReplacedJsonData), % replace within other for/repeat loops, but also other attributes
2104 process_repeat(NewRepCount,ReplacedJsonData,File,FinalCall), % now process next loop or finish
2105 fail
2106 ; true).
2107
2108 check_between(_,_,Step,ForPos) :- Step =< 0, !,
2109 add_error(visb_visualiser,'The step of a for loop must be positive:',Step,ForPos),fail.
2110 check_between(From,To,Step,ForPos) :- Iters is (To-From)/Step,
2111 Iters > 100000,!,
2112 add_error(visb_visualiser,'Very large for loop:',Iters,ForPos),fail.
2113 check_between(_,_,_,_).
2114
2115
2116 % ----------------
2117
2118 % parse and load VisB events
2119 % A VisB Event looks like:
2120 % {
2121 % "id": "button",
2122 % "event": "toggle_button",
2123 % "execution_kind": "random", (optional: default, first, random or heuristic)
2124 % "hovers": [{ "attr":"stroke-width", "enter":"6", "leave":"1"},
2125 % { "attr":"opacity", "enter":"0.8", "leave":"1.0"}]
2126 % }
2127 process_json_event(File,Json) :-
2128 process_repeat(0,Json,File,add_visb_json_event).
2129
2130 add_visb_json_event(File,json(List)) :- get_attr_true(ignore,List,File), !. % ignore this item
2131 add_visb_json_event(File,json(List)) :-
2132 get_pos_from_list(List,File,Pos),
2133 force_del_attr(id,List,string(ID),L0,File), % for items it could make sense to not specify an id
2134 (del_attr_with_pos(event,L0,EvTerm,L1,File,EvPos)
2135 -> (check_json_string(EvTerm,event,EvPos,Event) -> true ; Event='') % TODO: accept multiple events
2136 ; Event='', L1=L0),
2137 (del_attr_with_pos(predicates,L1,array(Preds),L2,File,PredPos) -> true
2138 ; del_attr_with_pos(preds,L1,array(Preds),L2,File,PredPos) -> true
2139 ; Preds=[], L2=L1, PredPos=unknown),
2140 (del_attr(execution_kind,L2,EKindTerm,L2b)
2141 -> (check_json_string(EKindTerm,execution_kind,Pos,EKind)
2142 -> check_execution_kind(EKind,ID,Pos,ExecKind)
2143 ; default_execution_kind(ExecKind))
2144 ; default_execution_kind(ExecKind), L2b=L2),
2145 ? (del_attr(hovers,L2b,array(Hovers),L3) -> true ; Hovers=[],L3=L2b),
2146 (del_attr(items,L3,array(EI),L4)
2147 -> (empty_b_event(Event)
2148 -> EnableItems=[],
2149 add_warning(visb_visualiser,'Ignoring enable items; you need to provide an event name:',ID,Pos)
2150 ; EnableItems=EI)
2151 ; EnableItems=[],L4=L3),
2152 debug_format(19,' Processing id:~w event:~w preds:~w kind:~w enable:~w hovers:~w~n',[ID,Event,Preds,ExecKind,EnableItems,Hovers]),
2153 !,
2154 actually_add_visb_json_event(ID,Event,Preds,ExecKind,Hovers,EnableItems,L4,File,Pos,PredPos).
2155 add_visb_json_event(File,Json) :-
2156 !,
2157 get_pos_from_list(Json,File,Pos),
2158 add_error(visb_visualiser,'Illegal VisB Event:',Json,Pos).
2159
2160 actually_add_visb_json_event(ID,Event,JsonPreds,ExecKind,JsonHovers,JsonEnableItems,JsonList,File,Pos,PredPos) :-
2161 check_additional_args(JsonList,File),
2162 maplist(process_json_pred,JsonPreds,Preds),
2163 maplist(process_json_enabling_item(ID,Event,File),JsonEnableItems,EnableItems),
2164 maplist(process_json_hover(ID,File),JsonHovers,Hovers),
2165 add_visb_event(ID,Event,Preds,ExecKind,Hovers,EnableItems,File,Pos,PredPos,JsonList).
2166
2167 % the execution kind determines which transition is executed when a VisB event is clicked
2168 % default means that the preference visb_default_execution_kind is used (looked up when the event is executed)
2169 execution_kind(default).
2170 execution_kind(first).
2171 execution_kind(random).
2172 execution_kind(heuristic).
2173 default_execution_kind(default).
2174
2175 check_execution_kind(Kind,_,_,Res) :- execution_kind(Kind), !, Res=Kind.
2176 check_execution_kind(Kind,ID,Pos,Res) :-
2177 default_execution_kind(Res),
2178 ajoin(['Unknown execution_kind ',Kind,' (must be default, first, random or heuristic) using ',Res,' for VisB event of SVG ID: '],Msg),
2179 add_warning(visb_visualiser,Msg,ID,Pos).
2180
2181 % get the execution kind to be used when executing a VisB event
2182 get_actual_execution_kind(default,Kind) :- !, get_preference(visb_default_execution_kind,Kind).
2183 get_actual_execution_kind(Kind,Kind).
2184
2185
2186 add_visb_event(ID,Event,Preds,ExecKind,Hovers,EnableItems,File,Pos,PredPos,JsonList) :-
2187 (retract(visb_event(ID,OldEvent,OldPreds,OldExecKind,OldTypedPred,OldFile,OldPos))
2188 -> (get_attr_true(override,JsonList,File)
2189 -> ajoin(['Overriding VisB event from file ',OldFile,' for ID: '],Msg),
2190 add_debug_message(visb_visualiser,Msg,ID,Pos),
2191 retractall(visb_hover(ID,_,_,_,_,_))
2192 ; (OldFile=File
2193 -> ajoin(['No override attribute: adding VisB event ',Event,
2194 ' (first event ',OldEvent,') for ID: '],Msg)
2195 ; ajoin(['No override attribute: adding VisB event ',Event,
2196 ' (first event ',OldEvent,' in file ',OldFile,') for ID: '],Msg)
2197 ),
2198 add_debug_message(visb_visualiser,Msg,ID,Pos),
2199 assertz(auxiliary_visb_event(ID,OldEvent,OldPreds,OldExecKind,OldTypedPred,OldPos))
2200 ),
2201 assert_visb_event(ID,Event,Preds,ExecKind,Hovers,EnableItems,File,Pos,PredPos)
2202 % ProB2-UI cannot currently support multiple events associated with the same id
2203 % TODO: merge visb_events if we have no override attribute
2204 ? ; (xtl_mode ; b_is_operation_name_or_external_subst(Event)) ->
2205 assert_visb_event(ID,Event,Preds,ExecKind,Hovers,EnableItems,File,Pos,PredPos)
2206 ; (empty_b_event(Event) ; \+ b_or_z_mode)
2207 -> % for empty event we just want hovers
2208 (Preds = [] -> true
2209 ; add_warning(visb_visualiser,'Ignoring preds for VisB event for SVG ID: ',ID,Pos)
2210 ),
2211 assert_visb_event(ID,Event,[],ExecKind,Hovers,EnableItems,File,Pos,PredPos)
2212 ; is_optional_json_item(JsonList,File,Pos) ->
2213 formatsilent('Ignoring optional VisB event for ~w (lines ~w) due to unknown B operation ~w~n',[ID,Pos,Event])
2214 ; detect_op_call_with_paras(Event,OpName) -> % check if we have a name with parameters inside
2215 add_warning(visb_visualiser,'Ignoring parameters provided in event; use the "predicate" attribute to specify parameters: ',Event,Pos),
2216 assert_visb_event(ID,OpName,Preds,ExecKind,Hovers,EnableItems,File,Pos,PredPos)
2217 ; atom(Event), atom_prefix('{',Event) ->
2218 ajoin(['Unknown B operation ', Event, ' as VisB event for SVG ID (be sure to use the "events" attribute for multiple events): '],Msg),
2219 add_warning(visb_visualiser,Msg,ID,Pos)
2220 ; ajoin(['Unknown B operation ', Event, ' as VisB event for SVG ID: '],Msg),
2221 add_warning(visb_visualiser,Msg,ID,Pos)
2222 ).
2223
2224 b_is_operation_name_or_external_subst(OpName) :- b_is_operation_name(OpName).
2225 b_is_operation_name_or_external_subst(Special) :- special_operation(Special).
2226
2227 % maybe we should provide those via the external substitution interface:
2228 % these are all schedulers which choose among existing enabled operations in some manner
2229 special_operation('MCTS_AUTO_PLAY').
2230 special_operation('RANDOM_ANIMATE').
2231 special_operation('RANDOM_ANIMATE_UNTIL_LTL').
2232 special_operation(F) :- external_fun_as_special_operation(F).
2233 external_fun_as_special_operation('STORE_VALUE').
2234 external_fun_as_special_operation('SET_PREF').
2235 % maybe other special names like random_animate with nr of steps, LTL until, ...
2236 special_operation_enabled('MCTS_AUTO_PLAY',_,_,_) :- mcts_auto_play_available.
2237 special_operation_enabled('RANDOM_ANIMATE',_,_,_). % TODO: check if a transition exists
2238 special_operation_enabled('RANDOM_ANIMATE_UNTIL_LTL',_,_,_).
2239 special_operation_enabled(F,_,_,_) :- external_fun_as_special_operation(F).
2240
2241 % detect simple operation call with parameters
2242 detect_op_call_with_paras(Event,OpName) :- atom(Event),
2243 atom_codes(Event,Codes), parse_op_call_name(OpC,Codes,_),!,
2244 atom_codes(OpName,OpC),
2245 b_is_operation_name_or_external_subst(OpName).
2246
2247 parse_op_call_name([X|T]) --> alpha(X), parse_op_call_name2(T).
2248 parse_op_call_name2([]) --> "(".
2249 parse_op_call_name2([H|T]) --> alphadigit(H), parse_op_call_name2(T).
2250
2251 alpha(X) --> [X],({X>=97, X=<122} ; {X>=65, X=<90}).
2252 alphadigit(X) --> [X], ({X>=97,X=<122} ; {X>=65, X=<90} ; {X=95} ; {X>=48, X=<57}).
2253
2254 empty_b_event('').
2255
2256 assert_visb_event(ID,Event,Preds,ExecKind,Hovers,EnableItems,File,Pos,PredPos) :-
2257 construct_pre_post_predicate(ID,Event,Preds,TypedPred,PredPos),
2258 assertz(visb_event(ID,Event,Preds,ExecKind,TypedPred,File,Pos)),
2259 % process hovers
2260 ? (member(HoverElement,Hovers),
2261 (HoverElement=visb_hover(HoverID,Attr,EnterVal,ExitVal) -> true ; add_error(visb_visualiser,'Illegal hover term:',HoverElement),fail),
2262 assert_visb_hover(ID,HoverID,Attr,EnterVal,ExitVal,Pos),fail
2263 ; true),
2264 % now process any additional items with disabled and enabled values
2265 assert_visb_enabling_item_list(Event,TypedPred,EnableItems,File,Pos).
2266
2267 process_json_pred(string(P),R) :- !, R=P.
2268 process_json_pred(Json,_) :- !, add_error(visb_visualiser,'Illegal json term for predicate:',Json), fail.
2269
2270 process_json_hover(OriginID,File,json(Hover),visb_hover(ID,Attr,EnterVal,LeaveVal)) :-
2271 force_del_attr(attr,Hover,string(Attr),H2,File),
2272 force_del_attr_with_pos(enter,H2,EV,H3,File,EnterPos),
2273 force_del_attr_with_pos(leave,H3,LV,H4,File,ExitPos),
2274 (is_svg_number_attribute(Attr) % we evaluate number attributes
2275 -> get_number_value(EV,EnterVal,Attr,EnterPos),
2276 get_number_value(LV,LeaveVal,Attr,ExitPos)
2277 ; string(EnterVal)=EV, string(LeaveVal)=LV % return string as is; TODO: also evaluate other attributes
2278 ),
2279 (get_attr(id,H4,string(ID)) -> true ; ID=OriginID).
2280
2281
2282 assert_visb_hover(TriggerID,SVG_ID,Attr,EnterVal,ExitVal,Pos) :-
2283 assert(visb_hover(TriggerID,SVG_ID,Attr,EnterVal,ExitVal,Pos)),
2284 (visb_has_hovers(TriggerID) -> true ; assert(visb_has_hovers(TriggerID))),
2285 (visb_has_visibility_hover(SVG_ID) -> true
2286 ; Attr=visibility -> assert(visb_has_visibility_hover(SVG_ID)) % object SVG_ID can be made visible by hover
2287 ; true),
2288 debug_format(19,'Adding hover for ~w : ~w.~w -> ~w <- ~w~n',[TriggerID,SVG_ID,Attr,EnterVal,ExitVal]).
2289
2290
2291 % process items in event which get translated to enabling/disabling predicates
2292 % "items":[ {"attr":"stroke-width","enabled":"10", "disabled":"1"} ]
2293 process_json_enabling_item(ID,Event,File,json(JsonList),
2294 visb_enable_item(SvgID,Attr,EnabledValExpr,DisabledValExpr,PosAttr)) :-
2295 force_del_attr_with_pos(attr,JsonList,string(Attr),L2,File,PosAttr),
2296 force_del_attr_with_pos('enabled',L2,string(EnVal),L3,File,EPos),
2297 force_del_attr_with_pos('disabled',L3,string(DisVal),L4,File,DPos),
2298 (del_attr(id,L4,string(SvgID),L5) -> true ; SvgID=ID, L4=L5),
2299 atom_codes(EnVal,EC),
2300 parse_expr_for_visb(EC,L5,EPos,EnabledValExpr),
2301 atom_codes(DisVal,DC),
2302 parse_expr_for_visb(DC,L5,DPos,DisabledValExpr),
2303 (debug_mode(off) -> true
2304 ; translate_bexpression_with_limit(EnabledValExpr,30,EVS),
2305 translate_bexpression_with_limit(DisabledValExpr,30,DVS),
2306 formatsilent('Enabling item for event ~w and SVG ID ~w and attribute ~w : ~w <-> ~w~n',[Event,SvgID,Attr,EVS,DVS])
2307 ).
2308
2309 assert_visb_enabling_item_list(Event,TypedPred,EnableList,File,Pos) :-
2310 maplist(check_enabling_item(Event),EnableList),
2311 (EnableList \= []
2312 -> (debug_mode(off)
2313 -> true
2314 ; format('Associating enabling/disabling list with event ~w and predicate: ',[Event]), translate:print_bexpr(TypedPred),nl),
2315 assert(visb_event_enable_list(Event,TypedPred,EnableList,File,Pos))
2316 ; true).
2317
2318 check_enabling_item(Event,visb_enable_item(SvgID,Attr,_,_,PosAttr)) :-
2319 (visb_item(SvgID,Attr,_,_,_Desc,DuplicatePos,_)
2320 -> (extract_span_description(DuplicatePos,DuplPos) -> true; DuplPos=DuplicatePos),
2321 ajoin(['VisB conflict for SVG ID ',SvgID,' and attribute ',Attr,
2322 '. Conflict between VisB item (',DuplPos,') and VisB enable/disable entry for event: '],Msg),
2323 add_warning(visb_visualiser,Msg,Event,PosAttr)
2324 ; true).
2325
2326 valid_additional_attributes(comment).
2327 valid_additional_attributes(description).
2328 valid_additional_attributes(optional).
2329 valid_additional_attributes(override).
2330
2331 % check if there are additional unprocessed args and generate a warning
2332 check_additional_args(JsonList,File) :-
2333 get_attr_with_pos(Attr,JsonList,_,File,Pos),
2334 \+ valid_additional_attributes(Attr),
2335 add_warning(visb_visualiser,'Ignoring unknown JSON field: ',Attr,Pos),
2336 fail.
2337 check_additional_args(_,_).
2338
2339
2340 % construct a pre-post typed predicate for a VisB Event
2341 construct_pre_post_predicate(SvgID,OpName,Preds,TypedPred,Pos) :-
2342 set_error_context(visb_error_context(event,SvgID,OpName,Pos)),
2343 maplist(construct_pre_post_pred(OpName,Pos),Preds,TL),
2344 clear_error_context,
2345 conjunct_predicates(TL,TypedPred).
2346
2347
2348 construct_pre_post_pred(OpName,Pos,PredStr,TypedPred) :- empty_b_event(OpName),!,
2349 add_error(visb_visualiser,'Cannot parse VisB event predicate without event name:',PredStr,Pos),
2350 TypedPred = b(truth,pred,[]).
2351 construct_pre_post_pred(OpName,Pos,String,TypedExpr) :- special_operation(OpName),!,
2352 % we have a special operation; allow to provide an expression with options/parameters
2353 construct_pre_post_for_special(Pos,String,TypedExpr).
2354 construct_pre_post_pred(OpName,Pos,String,TypedPred) :-
2355 atom_codes(String,Codes), replace_special_patterns(Codes,RCodes,Pos), %format("Replaced : ~S~n",[RCodes]),
2356 get_visb_extra_scope([identifier(VisBExtraScope)]),
2357 visb_click_meta_b_type(MetaRecType),
2358 ExtraScope = [identifier([b(identifier('VISB_CLICK_META_INFOS'),MetaRecType,[visb_generated])|VisBExtraScope])],
2359 (b_parse_machine_operation_pre_post_predicate(RCodes,ExtraScope,TypedPred,OpName,gen_parse_errors_for(Pos))
2360 -> true
2361 ; add_error(visb_visualiser,'Error for VisB event predicate:',String,Pos),
2362 TypedPred = b(truth,pred,[])
2363 ).
2364
2365 construct_pre_post_for_special(Pos,Pred,TypedExpr) :- member(Pred,[btrue,bfalse]), !,
2366 add_warning(visb_visualiser,'Special operations require an expression specifying the parameter values, not a predicate: ',Pred,Pos),
2367 TypedExpr = b(integer(0),integer,[]).
2368 construct_pre_post_for_special(Pos,String,TypedExpr) :-
2369 atom_codes(String,Codes),
2370 GenParseErrors=gen_parse_errors_for(Pos),
2371 parse_expr(Codes,TypedExpr,GenParseErrors). % TODO: should we require a predicate instead, and provide arg names?
2372
2373 % replace special patterns by dummy values to avoid error messages in ProB2-UI
2374 replace_special_patterns([],[],_Pos) :- !.
2375 replace_special_patterns([37|T],Res,Pos) :- !, % percentage sign %
2376 replace_aux(T,Res,Pos).
2377 replace_special_patterns([H|T],[H|RT],Pos) :- !, replace_special_patterns(T,RT,Pos).
2378
2379 replace_aux(Input,Res,Pos) :-
2380 replace_pat(PAT,Replacement),
2381 append(PAT,T,Input),
2382 !,
2383 atom_codes(PatAtom,PAT),
2384 ajoin(['The %',PatAtom,' pattern is obsolete. Please use VISB_CLICK_META_INFOS\'',PatAtom,' instead.'],Msg),
2385 add_warning(visb_visualiser,Msg,'',Pos),
2386 append(Replacement,ResT,Res),
2387 replace_special_patterns(T,ResT,Pos).
2388 replace_aux(Input,[37|Res],Pos) :- !, replace_special_patterns(Input,Res,Pos).
2389
2390 replace_pat("shiftKey","VISB_CLICK_META_INFOS'shiftKey").
2391 replace_pat("metaKey","VISB_CLICK_META_INFOS'metaKey").
2392 replace_pat("pageX","VISB_CLICK_META_INFOS'pageX").
2393 replace_pat("pageY","VISB_CLICK_META_INFOS'pageY").
2394
2395
2396 % small utils:
2397 % ----------------
2398
2399 % a variation of between/3 with a step argument
2400 between(From,To,_,_) :- From > To, !,fail.
2401 between(From,_,_,From).
2402 ?between(From,To,Step,Elem) :- F1 is From+Step, between(F1,To,Step,Elem).
2403
2404
2405 parse_expr(ExprCodes,TypedExpr,_GenParseErrors) :-
2406 is_simple_classical_b_identifier_codes(ExprCodes),
2407 atom_codes(DefID,ExprCodes),
2408 visb_definition(DefID,Type,_DefFormula,Class,_VPos,_),
2409 !,
2410 TypedExpr = b(identifier(DefID),Type,[type_of_formula(Class)]).
2411 parse_expr(ExprCodes,TypedExpr,GenParseErrors) :-
2412 temporary_set_preference(allow_arith_operators_on_reals,true,CHNG),
2413 get_visb_extra_scope(ExtraScope),
2414 % formatsilent('Parsing: ~s (extra ids: ~w)~n',[ExprCodes,ExtraScope]), start_ms_timer(T1),
2415 call_cleanup(bmachine:b_parse_machine_expression_from_codes_with_prob_ids(ExprCodes,ExtraScope,TypedExpr,
2416 GenParseErrors),
2417 reset_temporary_preference(allow_arith_operators_on_reals,CHNG)).
2418 %stop_ms_walltimer_with_msg(T1,'parsing: ').
2419 % TODO: we could share a machine parameter M like in prob2_interface
2420 % so that b_type_expression_for_full_b_machine will load machine only once
2421 % cf b_type_open_predicate_for_full_b_machine
2422
2423 determine_type_of_visb_formula(b(_,_,[type_of_formula(Class)]),all,Res) :- !,
2424 Res = Class. % generated by VisB, TODO: store UsedTIds in type_of_formula(.)
2425 determine_type_of_visb_formula(TypedExpr,TIds,Class) :-
2426 determine_type_of_formula_with_visb_defs(TypedExpr,TIds,Class).
2427
2428 %check for used_ids inside TypedExpr an see what type of visb_definition are used
2429 determine_type_of_formula_with_visb_defs(TypedExpr,TIds,ResClass) :-
2430 determine_type_of_formula(TypedExpr,TIds,Class1), % compute class w/o knowledge of VisB definitions
2431 ? (class_more_general_than(Class2,Class1),
2432 ? required_by_visb_def(TIds,Class2) -> Class=Class2
2433 ; Class=Class1),
2434 (Class=requires_variables -> ResClass=Class
2435 ; expression_requires_state(TypedExpr) -> ResClass = requires_variables
2436 ; ResClass=Class).
2437
2438
2439 expression_requires_state(TypedExpr) :-
2440 (map_over_bexpr(sub_expression_requires_state,TypedExpr) -> true ; fail).
2441 % TODO: should we also check is_not_declarative/1 ? e.g., for RANDOM or CHANGED
2442 sub_expression_requires_state(external_pred_call(P,_)) :- external_function_requires_state(P).
2443 sub_expression_requires_state(external_function_call(P,_)) :- external_function_requires_state(P).
2444
2445
2446 class_more_general_than(requires_variables,requires_nothing).
2447 class_more_general_than(requires_variables,requires_constants).
2448 class_more_general_than(requires_constants,requires_nothing).
2449
2450 required_by_visb_def(TIds,Class) :-
2451 ? member(TID,TIds), get_texpr_id(TID,ID),visb_definition(ID,_,_,Class,_,_).
2452
2453
2454 get_visb_extra_scope([identifier(ExtraIDs)]) :-
2455 get_visb_extra_identifiers(ExtraIDs).
2456 get_visb_extra_identifiers(ExtraIDs) :-
2457 findall(b(identifier(ID),Type,[visb_generated]),visb_definition(ID,Type,_,_,_,_),ExtraIDs).
2458
2459
2460 force_get_attr_nr(Attr,List,Nr,File) :-
2461 force_del_attr_with_pos(Attr,List,AttrVal,_,File,Pos),!,
2462 get_number_value(AttrVal,Nr,Attr,Pos).
2463
2464 get_number_value(AttrVal,Nr,Attr,Pos) :-
2465 (AttrVal = number(Nr) -> true
2466 ; AttrVal = string(StrAttrVal),
2467 atom_codes(StrAttrVal,AttrValC),
2468 parse_expr(AttrValC,TypedExpr,gen_parse_errors_for(Pos)),
2469 determine_type_of_visb_formula(TypedExpr,UsedTIds,Class)
2470 % Class = requires_nothing, requires_constants, requires_variables
2471 ->
2472 (Class = requires_variables
2473 -> ajoin(['The JSON formula for the attribute"',Attr,
2474 '" must not depend on variables:'],Msg),
2475 add_error(visb_visualiser,Msg,StrAttrVal,Pos),fail
2476 ; Class = requires_constants,
2477 \+ is_concrete_constants_state_id(_)
2478 -> ajoin(['The JSON formula for the attribute"',Attr,
2479 '" depends on constants which have not been set up (via SETUP_CONSTANTS):'],Msg),
2480 add_error(visb_visualiser,Msg,StrAttrVal,Pos),fail
2481 ; get_texpr_type(TypedExpr,Type),
2482 is_number_type(Type)
2483 -> (eval_static_def(Class,'VisB attribute',Attr,TypedExpr,UsedTIds,ResVal,Pos)
2484 -> get_number_from_bvalue(ResVal,Nr)
2485 ; Class = requires_constants,
2486 multiple_concrete_constants_exist
2487 -> ajoin(['The JSON formula for the attribute"',Attr,
2488 '" depends on constants and multiple solutions for SETUP_CONSTANTS exist:'],Msg),
2489 add_warning(visb_visualiser,Msg,StrAttrVal,Pos),fail
2490 )
2491 ; get_texpr_type(TypedExpr,Type),
2492 % it could produce a string which is a number; we could try and convert to a number
2493 ajoin(['The type ',Type,' of the JSON value formula for the attribute "',Attr,
2494 '" is not a number:'],Msg),
2495 add_error(visb_visualiser,Msg,StrAttrVal,Pos),fail
2496 )
2497 ; ajoin(['The JSON value for the attribute "',Attr,'" cannot be parsed as a number:'],Msg),
2498 add_error(visb_visualiser,Msg,AttrVal,Pos),fail).
2499
2500 is_number_type(integer).
2501 is_number_type(real).
2502
2503
2504 get_number_from_bvalue(int(Nr),Res) :- !, Res=Nr.
2505 get_number_from_bvalue(Term,Res) :- is_real(Term,Nr),!,Res=Nr.
2506
2507 % ----------------------------------
2508
2509
2510 % computing updates to SVG for a given StateId:
2511 % predicate to obtain required setAttribute changes to visualise StateId
2512 % looks up visb_item facts but also evaluates VISB_SVG_UPDATE definitions
2513 get_change_attribute_for_state(StateId,SvgID,SvgAttribute,Value) :-
2514 visb_state_can_be_visualised(StateId),
2515 set_error_context(checking_context('VisB:','computing attributes for state')),
2516 set_context_state(StateId,get_change_attribute_for_state), % also sets it for external functions
2517 % TODO: also set history so that get_current_history in external_functions is more reliable
2518 ? call_cleanup((get_expanded_bstate(StateId,ExpandedBState), % inside call_cleanup: contexts cleared if it fails
2519 get_change_aux(StateId,ExpandedBState,SvgID,SvgAttribute,Value)),
2520 (clear_context_state,clear_error_context)).
2521
2522 get_change_aux(StateId,ExpandedBState,SvgID,SvgAttribute,Value) :-
2523 ? get_aux2(StateId,ExpandedBState,SvgID0,SvgAttribute0,Value0,DefPos),
2524 post_process_visb_item(SvgID0,SvgAttribute0,Value0,DefPos,SvgID,SvgAttribute,Value).
2525 get_aux2(StateId,ExpandedBState,SvgID,SvgAttribute,Value,DefPos) :-
2526 % first get updates from DEFINITIONS which usually return sets of records
2527 ? get_svg_object_updates_from_definitions(ExpandedBState,SvgID,SvgAttribute,Value,DefPos),
2528 debug_format(19,' VISB_SVG_UPDATE ~w:~w = ~w (in state ~w)~n',[SvgID,SvgAttribute,Value,StateId]).
2529 get_aux2(StateId,ExpandedBState,SvgID,SvgAttribute,Value,Pos) :-
2530 % TODO: check if visibility item exists, and if value is false: do not compute the rest
2531 ? get_visb_item_formula(SvgID,SvgAttribute,Formula,Pos,ExpandedBState),
2532 % TODO: if UsedIds=[] only set attribute once, if it requires_only_constants : ditto
2533 evaluate_visb_formula(Formula,'VisB item',SvgID,ExpandedBState,FValue,Pos),
2534 %b_value_to_string(FValue,Value),
2535 extract_attr_value(SvgAttribute,FValue,Value,Pos),
2536 debug_format(19,' VisB item ~w:~w = ~w (in state ~w)~n',[SvgID,SvgAttribute,Value,StateId]).
2537
2538 % post-process certain items, like title to adapt the value and id:
2539 % SVG does not support a title attribute; we need a title child and set its text attribute
2540 post_process_visb_item(SvgID,title,Value,Pos,NewID,text,NewValue) :-
2541 (visb_svg_object_debug_info(SvgID,DebugInfo) -> ajoin([Value,'\n',DebugInfo],NewValue) ; NewValue=Value),
2542 ? (visb_svg_parent(SvgID,TitleID),
2543 visb_svg_object(TitleID,title,_,_,_)
2544 -> NewID=TitleID,
2545 debug_format(9,'Redirecting title update for ~w to child object ~w~n',[SvgID,TitleID])
2546 ; visb_svg_object(SvgID,_SVG_Class,_RestAttrs,_Desc,_Pos1)
2547 -> % this is an object we created
2548 add_warning(visb_visualiser,'Could not find title child object, be sure to define a static title attribute for SVG object: ',SvgID,Pos),
2549 fail
2550 ; add_warning(visb_visualiser,'SVG does not natively support title attributes: ',SvgID,Pos),
2551 fail
2552 ),!.
2553 post_process_visb_item(SvgID,Attr,Value,_,SvgID,Attr,Value).
2554
2555 % ------------------
2556
2557 % convert B value to Json String
2558 b_value_to_string(string(SValue),Res) :- !, Res=SValue. % What about quoting,...?
2559 b_value_to_string(FValue,Res) :-
2560 translate_bvalue_to_codes(FValue,ValCodes),
2561 atom_codes(Res,ValCodes).
2562
2563 % attributes for which we convert pairs to the concatenation of strings
2564 is_id_or_text_attribute(id).
2565 is_id_or_text_attribute('group_id'). % works much like a parent_id; also can be used to attach animate to e.g. a rect
2566 is_id_or_text_attribute('trigger_id').
2567 is_id_or_text_attribute('trigger-id').
2568 is_id_or_text_attribute(A) :- is_text_attribute(A).
2569 % children
2570 is_text_attribute('text').
2571 is_text_attribute('transform').
2572 is_text_attribute('event'). % for VISB_SVG_EVENTS
2573 is_text_attribute('predicate'). % for VISB_SVG_EVENTS
2574 is_text_attribute('svg_class').
2575
2576
2577 b_value_to_id_string(Val,String) :- b_val_to_id_string_aux(Val,add_underscore,String).
2578 b_value_to_attr_string(Val,String) :- b_val_to_id_string_aux(Val,no_underscore,String).
2579
2580 % a special string conversion which e.g., allows to use things like ("ab",2) as id which gets translated to ab2
2581 % also useful for text attributes
2582 b_val_to_id_string_aux(string(SValue),_,Res) :- !, Res=SValue.
2583 b_val_to_id_string_aux((A,B),Add,Res) :- !,
2584 b_val_to_id_string_aux(A,Add,VA), b_val_to_id_string_aux(B,Add,VB),
2585 (Add=add_underscore
2586 -> atom_concat('_',VB,VB1) % introduce separator; in case we have things like numbers so that (12,3) /= (1,23)
2587 ; VB1=VB % for fields like predicate, transform, ... we do not want additional underscores
2588 ),
2589 atom_concat(VA,VB1,Res).
2590 b_val_to_id_string_aux(Set,Add,Res) :- singleton_set(Set,El),!, b_val_to_id_string_aux(El,Add,Res).
2591 % TODO: maybe convert sequence of values using conc
2592 b_val_to_id_string_aux(FValue,_,Res) :- b_value_to_string(FValue,Res).
2593
2594
2595
2596 % get either a regular VisB item or an item associated with a VisB event and its enabled status
2597 get_visb_item_formula(SvgID,SvgAttribute,Formula,Pos,_ExpandedBState) :-
2598 ? visb_item(SvgID,SvgAttribute,Formula,_UsedIds,_Desc,Pos,_). % regular VisB item
2599 get_visb_item_formula(SvgID,SvgAttribute,Formula,Pos,ExpandedBState) :- % item within VisB event
2600 visb_event_enable_list(OpName,TypedPred,EnableList,_,ItemPos),
2601 debug_format(19,'Checking enabledness of VisB event ~w~n',[OpName]),
2602 (check_enabled(OpName,TypedPred,ExpandedBState,ItemPos)
2603 -> Formula=EnabledValExpr
2604 ; Formula=DisabledValExpr
2605 ),
2606 member(visb_enable_item(SvgID,SvgAttribute,EnabledValExpr,DisabledValExpr,Pos), EnableList).
2607
2608 check_enabled(OpName,Pred,ExpandedBState,Pos) :-
2609 special_operation(OpName),!,
2610 special_operation_enabled(OpName,Pred,ExpandedBState,Pos).
2611 check_enabled(OpName,Pred,ExpandedBState,Pos) :-
2612 b_is_operation_name(OpName),
2613 % TODO: see if we can use GUARD without execution
2614 % TODO: not yet working for XTL mode
2615 execute_operation_by_predicate_in_state_with_pos(ExpandedBState,OpName,Pred,_OpTerm,_NewState,Pos,_).
2616
2617
2618 get_expanded_bstate(StateId,ExpandedBState) :-
2619 \+ b_or_z_mode,!,
2620 StateId \= root,
2621 % in XTL mode we need to access the state via external functions like STATE_PROPERTY
2622 get_static_visb_state([],ExpandedBState).
2623 get_expanded_bstate(StateId,ExpandedBState) :-
2624 visited_expression(StateId,State),
2625 state_corresponds_to_initialised_b_machine(State,BState),
2626 debug_format(19,'Adding VisB definitions to state: ~w~n',[StateId]),
2627 add_prob_deferred_set_elements_to_store(BState,BState2,visible), % add prob_ids
2628 findall(visb_definition(DefID,Type,DefFormula,Class,VPos,Special),
2629 visb_definition(DefID,Type,DefFormula,Class,VPos,Special),DefList),
2630 eval_defs(DefList,BState2,ExpandedBState).
2631
2632
2633 evaluate_static_visb_formula(Formula,Kind,Nr,InState,ResValue,Pos) :-
2634 get_static_visb_state(InState,StaticState),
2635 evaluate_visb_formula(Formula,Kind,Nr,StaticState,ResValue,Pos).
2636
2637 evaluate_visb_formula(Formula,Kind,Nr,BState2,ResValue,Pos) :-
2638 % add_message(visb,'Evaluating: ',Kind:Nr,Pos), debug:nl_time, %set_prolog_flag(profiling,on), print_profile,
2639 ? (b_compute_explicit_epression_no_wf(Formula,[],BState2,ResValue,Kind,Nr,Pos) -> true
2640 ; add_error(visb_visualiser,'Evaluation of VisB formula failed:',Formula,Pos),
2641 fail).
2642
2643 % evaluate VisB definitions in order one by one, adding values to state
2644 eval_defs([],State,State).
2645 eval_defs([visb_definition(ID,_,TypedExpr,_Class,VPos,Special)|T],State0,State2) :-
2646 (Special \= regular_def
2647 -> State1=State0 % ignore special defs; they are processed separately
2648 ; b_compute_expression_nowf(TypedExpr,[],State0,ResValue,'VisB definition',ID,VPos),
2649 State1 = [bind(ID,ResValue)|State0], % store value for later definitions and VisB items
2650 (debug_level_inactive_for(19)-> true
2651 ; translate_bvalue_to_codes_with_limit(ResValue,2000,RC),
2652 formatsilent(' VisB definition ~w == ~s~n',[ID,RC]))
2653 ),
2654 eval_defs(T,State1,State2).
2655
2656 % check if we have a static value for a VisB definition:
2657 visb_def_static_value(DefID,Value) :-
2658 ? visb_definition(DefID,_,b(value(Value),_,_),Class,_,SpecialClass),
2659 SpecialClass = regular_def,
2660 (Class = requires_nothing ; Class = requires_constants). % probably we do not need to check this, as we have value/1
2661
2662 get_static_visb_state(InState,FullState) :-
2663 findall(bind(DefID,Value),visb_def_static_value(DefID,Value),FullState,InState).
2664 % TO DO: get state with deferred_set_elements,...
2665
2666 % get a definition either from B DEFINITIONS section, or if not present from the VisB JSON special definition list
2667 get_and_eval_special_definition(Prefix,DefName,JSONBackup,DefPos,ResValue,InlineObjects) :-
2668 (b_sorted_b_definition_prefixed(expression,Prefix,DefName,DefPos),
2669 ? b_get_typed_definition(DefName,[variables_and_additional_defs],TBody)
2670 ;
2671 visb_special_definition(JSONBackup,DefName,_Type,TBody,_Class,DefPos)
2672 ),
2673 % TODO: warn if we use static SVG
2674 ? get_typed_static_definition_with_constants_state(DefName,TBody,
2675 Body,DefPos,ConstantsState,InlineObjects,no_separation),
2676 evaluate_visb_formula(Body,DefName,'',ConstantsState,ResValue,DefPos).
2677
2678 % check if we have a DEFINITION with the indicated prefix
2679 special_definition_exists(Prefix) :-
2680 b_sorted_b_definition_prefixed(expression,Prefix,_DefName,_DefPos).
2681
2682 % --------------------------
2683
2684 % debugging features to inspect all created SVG objects
2685 tcltk_get_visb_objects(list([Header|AllObjects])) :-
2686 Header = list(['SVG-ID','svg_class','Attribute','Value']),
2687 findall(list([SvgID,SvgClass,Attr,Value]),
2688 (visb_svg_obj_with_parent(SvgID,SvgClass,AttrList,true),
2689 (member(svg_attribute(Attr,Value),AttrList) ;
2690 visb_svg_parent(SvgID,Value), Attr=children)
2691 ), Childs),
2692 findall(list([SvgID,SvgClass,Attr,Value]),
2693 (visb_svg_obj_with_parent(SvgID,SvgClass,AttrList,false),
2694 member(svg_attribute(Attr,Value),AttrList)
2695 ),AllObjects, Childs).
2696
2697 % debugging features to inspect all created SVG objects
2698 tcltk_get_visb_hovers(list([Header|AllObjects])) :-
2699 Header = list(['SVG-ID','ID','Attribute','EnterValue', 'ExitValue']),
2700 findall(list([SvgID,ID,Attr,EnterVal,ExitVal]),
2701 visb_hover(SvgID,ID,Attr,EnterVal,ExitVal,_Pos), AllObjects).
2702
2703 % debugging features to inspect items
2704 tcltk_get_visb_items(Res) :-
2705 current_state_id(ID),
2706 tcltk_get_visb_items(ID,Res).
2707 tcltk_get_visb_items(_StateId,Res) :-
2708 \+ visb_file_is_loaded(_),!,
2709 Res = list(['No VisB JSON file loaded']).
2710 tcltk_get_visb_items(StateId,list([Header|AllItems])) :-
2711 Header = list(['SVG-ID','Attribute','Value']),
2712 get_preference(translation_limit_for_table_commands,Limit),
2713 findall(list([SvgID,SvgAttribute,Val]),
2714 get_change_attribute_for_state(StateId,SvgID,SvgAttribute,Val),
2715 Items),
2716 findall(list([DefID,'VisB Definition',DValS]),
2717 (get_expanded_bstate(StateId,ExpandedBState),
2718 reverse(ExpandedBState,RS),
2719 member(bind(DefID,DVal),RS),
2720 visb_definition(DefID,_,_,_,_,_),
2721 translate_bvalue_with_limit(DVal,Limit,DValS)),
2722 AllItems, Items).
2723
2724 :- use_module(probsrc(translate),[translate_event_with_limit/3]).
2725 tcltk_get_visb_events(Res) :-
2726 current_state_id(ID),
2727 tcltk_get_visb_events(ID,Res).
2728 tcltk_get_visb_events(_StateId,Res) :-
2729 \+ visb_file_is_loaded(_),!,
2730 Res = list(['No VisB JSON file loaded']).
2731 tcltk_get_visb_events(StateId,list([Header|AllItems])) :-
2732 Header = list(['SVG-ID','Event','Target']),
2733 get_preference(translation_limit_for_table_commands,Limit),
2734 findall(list([SvgID,EventStr,NewStateId]),
2735 (perform_visb_click_event(SvgID,[],StateId,_Event,OpTerm,NewStateId,Res),
2736 (Res=[] -> EventStr='disabled' ; translate_event_with_limit(OpTerm,Limit,EventStr))
2737 ),
2738 AllItems).
2739 % --------------------------
2740
2741 % for ProB2-UI
2742
2743 :- use_module(probsrc(translate),[translate_bexpression_with_limit/3]).
2744 get_visb_items(List) :-
2745 findall(visb_item(SvgID,Attr,TS,Desc,Pos),
2746 (visb_item(SvgID,Attr,TypedExpr,_Used,Desc,Pos,_),
2747 translate_bexpression_with_limit(TypedExpr,1000,TS)), List).
2748
2749 get_visb_attributes_for_state(StateId,List) :-
2750 % some external functions like ENABLED are evaluated in current state; it might be useful to set_current_state
2751 (visb_file_is_loaded(_) -> true
2752 ; add_warning(visb_visualiser,'No VisB visualisation loaded, cannot get attributes for state: ',StateId)),
2753 findall(set_attr(SvgID,SvgAttribute,Val),
2754 get_change_attribute_for_state(StateId,SvgID,SvgAttribute,Val),
2755 List).
2756
2757 % for ProB2-UI
2758 get_visb_click_events(List) :-
2759 findall(execute_event(SvgID,Event,Preds),
2760 get_visb_event(SvgID,Event,Preds), List).
2761 get_visb_event(SvgID,Event,Preds) :- visb_event(SvgID,Event,Preds,_,_,_File,_Pos).
2762 get_visb_event(SvgID,'',[]) :- % add a virtual empty event for SVG Ids with hovers but no events
2763 visb_has_hovers(SvgID),
2764 \+ visb_event(SvgID,_,_,_,_,_File,_Pos).
2765
2766
2767 :- use_module(probsrc(state_space), [extend_trace_by_transition_ids/1]).
2768 % perform a click on a VisB SVG ID in the animator
2769 tcltk_perform_visb_click_event(SvgID) :- current_state_id(StateId),
2770 (perform_visb_click_event(SvgID,[],StateId,TransitionIDs)
2771 -> formatsilent('Clicking on ~w in state ~w resulted in transition id sequence ~w~n',[SvgID,StateId,TransitionIDs]),
2772 extend_trace_by_transition_ids(TransitionIDs)
2773 ; add_error(visb_visualiser,'Cannot perform VisB click on: ',SvgID)
2774 ).
2775
2776 % computes the effect of clicking on an SvgID in StateId
2777 % it returns either empty list if the visb_event is not feasible or a list of transition ids
2778 % it fails if SvgID has no events associated with it
2779 perform_visb_click_event(SvgID,MetaInfos,StateId,ResTransitionIds) :-
2780 perform_visb_click_event(SvgID,MetaInfos,StateId,_,_,_,ResTransitionIds).
2781 perform_visb_click_event(SvgID,MetaInfos,StateId,OpName,OpTerm,NewStateId,ResTransitionIds) :-
2782 get_svgid_with_events(SvgID,_), % check before setting contexts, which would otherwise not be cleared
2783 set_error_context(checking_context('VisB:','performing click event')),
2784 set_context_state(StateId,perform_visb_click_event),
2785 call_cleanup((get_expanded_bstate(StateId,ExpandedBState),
2786 perform_aux(SvgID,StateId,ExpandedBState,OpName,OpTerm,NewStateId,ResTransitionIds,MetaInfos)),
2787 (clear_context_state,clear_error_context)).
2788
2789 get_svgid_with_events(SvgID,FirstOpName) :- visb_event(SvgID,FirstOpName,_,_,_,_File,_Pos).
2790
2791
2792 :- use_module(probsrc(tools),[safe_read_term_from_atom/2]).
2793 :- use_module(probsrc(state_space),[transition/4]).
2794 perform_aux(SvgID,StateId,_ExpandedBState,OpName,OpTerm,NewStateId,ResTransitionIds,_MetaInfos) :-
2795 \+ b_or_z_mode,
2796 % just look up transition/4 entries in state space
2797 % TODO: maybe provide this possibility for B models, even if using explicit parameter values is more robust
2798 get_visb_event_operation(SvgID,OpName,Preds,_ExecKind,_Pred,Pos),
2799 safe_read_term_from_atom(OpName,OpTerm0),
2800 (xtl_mode -> check_xtl_direct_transition_candidate(OpTerm0,Preds,Pos), OpTerm=OpTerm0 ; true), !,
2801 % for XTL: use exec by pred for arity 0, otherwise try to find transition term in state space
2802 if((transition(StateId,OpTerm,TransID,NewStateId),
2803 match_operation(OpTerm,OpName)), % match is relevant if \+xtl_mode
2804 ResTransitionIds = [TransID],
2805 return_disabled(SvgID,StateId,OpName,OpTerm,NewStateId,ResTransitionIds)
2806 ).
2807 perform_aux(SvgID,StateId,ExpandedBState,OpName,OpTerm,NewStateId,ResTransitionIds,MetaInfos) :-
2808 %state_space:portray_state_space,
2809 if(execute_visb_event_by_predicate(SvgID,StateId,ExpandedBState,OpName,OpTerm,NewStateId,TransIds,MetaInfos),
2810 ResTransitionIds = TransIds,
2811 return_disabled(SvgID,StateId,OpName,OpTerm,NewStateId,ResTransitionIds)).
2812
2813 check_xtl_direct_transition_candidate(OpTerm,Preds,Pos) :-
2814 nonvar(OpTerm), % upper case event names are interpreted as Prolog variables -> use exec by pred (or FIXME)
2815 functor(OpTerm,_,Ar), Ar>0, % use exec by pred for arity 0, otherwise try to find transition term in state space
2816 (Preds \= []
2817 -> add_warning(visb_visualiser,'Ignoring predicates for VisB XTL event with explicit transition term (use predicate to provide parameter values): ',OpTerm,Pos)
2818 ; true).
2819
2820 return_disabled(SvgID,StateId,OpName,OpTerm,NewStateId,ResTransitionIds) :-
2821 OpTerm = disabled, NewStateId = StateId,
2822 get_svgid_with_events(SvgID,OpName),
2823 ResTransitionIds = []. % visb_event not enabled
2824
2825 :- use_module(probsrc(tools_strings),[match_atom/2]).
2826 :- use_module(probsrc(specfile),[get_operation_name/2]).
2827 match_operation(OpTerm,OpTerm) :- !.
2828 match_operation(OpTerm,OpName) :- atom(OpName),
2829 (get_operation_name(OpTerm,OpName)
2830 ; match_atom(OpName,OpTerm) % match an atom string with a compound term
2831 ),!.
2832
2833 execute_visb_event_by_predicate(SvgID,StateId,ExpandedBState,OpName,OpTerm,NewStateId,TransIds,MetaInfos) :-
2834 get_visb_event_operation(SvgID,OpName,_,EventExecKind,Pred,Pos),
2835 get_actual_execution_kind(EventExecKind,ExecKind),
2836 build_visb_click_meta_object(MetaInfos,BMetaInfos),
2837 bsyntaxtree:replace_id_by_expr(Pred,'VISB_CLICK_META_INFOS',BMetaInfos,NPred),
2838 exec_aux(SvgID,StateId,ExpandedBState,OpName,ExecKind,NPred,Pos,OpTerm,NewStateId,TransIds).
2839
2840 build_visb_click_meta_object(MetaInfos,BMetaInfos) :-
2841 (member(alt_key,MetaInfos) -> AltKey = pred_true ; AltKey = pred_false),
2842 (member(ctrl_key,MetaInfos) -> CtrlKey = pred_true ; CtrlKey = pred_false),
2843 (member(meta_key,MetaInfos) -> MetaKey = pred_true ; MetaKey = pred_false),
2844 (member(pageX(PageX),MetaInfos) -> true ; PageX = 0), % fallback
2845 (member(pageY(PageY),MetaInfos) -> true ; PageY = 0), % fallback
2846 (member(shift_key,MetaInfos) -> ShiftKey = pred_true ; ShiftKey = pred_false),
2847 findall(','(string(Var),string(VarValue)), member(js_var(Var,VarValue), MetaInfos), JSVars),
2848
2849 visb_click_meta_b_type(BType),
2850 BMetaInfos = b(value(rec([
2851 field(altKey,AltKey),
2852 field(ctrlKey,CtrlKey),
2853 field(jsVars,JSVars),
2854 field(metaKey,MetaKey),
2855 field(pageX,int(PageX)),
2856 field(pageY,int(PageY)),
2857 field(shiftKey,ShiftKey)
2858 ])),BType,[visb_generated]).
2859
2860 visb_click_meta_b_type(record([
2861 field(altKey,boolean),
2862 field(ctrlKey,boolean),
2863 field(jsVars,set(couple(string,string))),
2864 field(metaKey,boolean),
2865 field(pageX,integer),
2866 field(pageY,integer),
2867 field(shiftKey,boolean)
2868 ])).
2869
2870 :- use_module(probsrc(tcltk_interface),[compute_all_transitions_if_necessary/2]).
2871 :- use_module(library(random),[random_member/2]).
2872 :- use_module(extrasrc(mcts_game_play), [mcts_auto_play/4, mcts_auto_play_available/0]).
2873 :- use_module(probsrc(tools_timeout), [time_out_with_factor_call/3]).
2874 :- use_module(probsrc(external_functions),[call_external_function/7]).
2875 :- use_module(probsrc(bsyntaxtree), [is_truth/1]).
2876 :- use_module(probsrc(specfile), [get_specific_max_operations_for_operation/2]).
2877 exec_aux(SvgID,StateId,_ExpandedBState,OpName,ExecKind,Pred,_Pos,OpTerm,NewStateId,[TransId]) :-
2878 choose_among_transitions_kind(ExecKind),
2879 is_truth(Pred),
2880 b_is_operation_name(OpName),
2881 \+ get_specific_max_operations_for_operation(OpName,0), % otherwise execute by predicate should be done
2882 % TODO: check max_reached_or_timeout_for_node?
2883 % TODO: further execution kinds like tcltk_random_perform2: explore_open, min_out_degree
2884 !,
2885 write(random_click_for(SvgID,StateId,OpName,ExecKind)),nl,
2886 compute_all_transitions_if_necessary(StateId,false),
2887 findall(rtr(OpTerm,TransId,NewStateId),
2888 (transition(StateId,OpTerm,TransId,NewStateId),
2889 match_operation(OpTerm,OpName)),
2890 List),
2891 choose_transition(ExecKind,List,rtr(OpTerm,TransId,NewStateId)).
2892 exec_aux(SvgID,StateId,ExpandedBState,OpName,_,Pred,Pos,OpTerm,NewStateId,[TransId]) :-
2893 (csp_with_bz_mode -> fail % execute by predicate does not work properly yet with csp_and_b states
2894 ; b_or_z_mode -> b_is_operation_name(OpName)
2895 ; true), !, % note: ignores special definitions below if not B mode
2896 time_out_with_factor_call(
2897 exec_op_by_pred_aux(StateId,ExpandedBState,OpName,Pred,OpTerm,NewState,Pos,TransInfo),
2898 5, % min_max_time_out(10,100,15000)
2899 (ajoin(['TIME-OUT for VisB click on SVG ID ', SvgID,' while executing operation by predicate: '],Msg),
2900 add_warning(visb_visualiser,Msg,OpName,Pos),fail)),
2901 patch_state(NewState,StateId,PatchedNewState),
2902 % add transition to state space:
2903 tcltk_interface:add_trans_id_infos(StateId,OpTerm,PatchedNewState,NewStateId,TransId,TransInfo).
2904 %translate:print_bstate(PatchedNewState),nl.
2905 exec_aux(SvgID,StateId,_ExpandedBState,'MCTS_AUTO_PLAY',_,_Pred,Pos,OpTerm,NewStateId,[TransId]) :- !,
2906 mcts_auto_play_available,
2907 % TODO: allow to set SimRuns, TimeOut in Pred
2908 add_debug_message(visb_visualiser,'Performing MCTS_AUTO_PLAY for click on: ',SvgID,Pos),
2909 mcts_auto_play(StateId,OpTerm,TransId,NewStateId).
2910 % TODO: more general call via call_external_substitution(FunName,Args,InState,OutState,Info,WF)
2911 exec_aux(_SvgID,StateId,_ExpandedBState,'RANDOM_ANIMATE',_,_Pred,_Pos,OpTerm,NewStateId,[TransId]) :- !,
2912 compute_all_transitions_if_necessary(StateId,false),
2913 findall(rtr(OpTerm,TransId,NewStateId),transition(StateId,OpTerm,TransId,NewStateId),List),
2914 random_member(rtr(OpTerm,TransId,NewStateId),List).
2915 % see tcltk_interface:tcltk_random_perform2 and allow Kind: fully_random, no_self_loops, heuristics, ...
2916 % split with ; ?
2917 exec_aux(SvgID,StateId,ExpandedBState,'RANDOM_ANIMATE_UNTIL_LTL',_,LTLExpr,Pos,OpTerm,NewStateId,TransIds) :- !,
2918 OpTerm = 'RANDOM_ANIMATE_UNTIL_LTL',
2919 (get_texpr_type(LTLExpr,string),
2920 evaluate_visb_formula(LTLExpr,'RANDOM_ANIMATE_UNTIL_LTL',SvgID,ExpandedBState,Value,Pos),
2921 Value = string(LTLFormula)
2922 -> tcltk_interface:tcltk_animate_until(LTLFormula,StateId,1000,ltl_state_property,_Steps,_Res,NewStateId,TransIds)
2923 ; add_error(visb_visualiser,'Argument to RANDOM_ANIMATE_UNTIL_LTL must provide LTL formula as string:',StateId),
2924 fail
2925 ).
2926 exec_aux(SvgID,StateId,ExpandedBState,ExternalFun,_,ArgEXPR,Pos,OpTerm,NewStateId,TransIds) :-
2927 external_fun_as_special_operation(ExternalFun),!,
2928 NewStateId=StateId, TransIds=[],
2929 evaluate_visb_formula(ArgEXPR,ExternalFun,1,ExpandedBState,ArgValue,Pos),
2930 convert_arg_value_to_list(ArgEXPR,ArgValue,UnEvalArgs,EvaluatedArgs),
2931 OpTerm =.. [ExternalFun|EvaluatedArgs],
2932 debug_format(9,'VisB event for ~w: calling external function ~w with ~w~n',[SvgID,ExternalFun,EvaluatedArgs]),
2933 % Note: we ignore the return value
2934 call_external_function(ExternalFun,UnEvalArgs,EvaluatedArgs,_Value,any,Pos,no_wf_available).
2935
2936 :- use_module(probsrc(state_space_exploration_modes),[heuristic_function_active/0,
2937 compute_heuristic_function_for_state_id/2]).
2938 % execution kinds which choose among the transitions already computed for the operation
2939 choose_among_transitions_kind(random).
2940 choose_among_transitions_kind(heuristic).
2941
2942 choose_transition(heuristic,List,Choice) :- heuristic_function_active,
2943 findall(H-RTR,(member(RTR,List), RTR=rtr(_,_,Dest),
2944 compute_heuristic_function_for_state_id(Dest,int(H))),HList),
2945 keysort(HList,[MinH-_|_]),!,
2946 findall(RTR,member(MinH-RTR,HList),MinList), % choose randomly among those with minimal heuristic value
2947 random_member(Choice,MinList).
2948 choose_transition(_,List,Choice) :- random_member(Choice,List).
2949
2950 % convert single argument expression to list; better would be to use a predicate with argument names
2951 convert_arg_value_to_list(b(couple(A,B),_,_),(VA,VB),[A|T],[VA|VT]) :- !, convert_arg_value_to_list(B,VB,T,VT).
2952 convert_arg_value_to_list(Arg,Val,[Arg],[Val]).
2953
2954 :- use_module(probsrc(b_state_model_check),[execute_operation_by_predicate_in_state_with_pos/7,
2955 xtl_execute_operation_by_predicate_in_state/8]).
2956 exec_op_by_pred_aux(StateId,ExpandedBState,OpName,Pred,OpTerm,NewState,Pos,TransInfo) :- xtl_mode,!,
2957 visited_expression(StateId,RawState),
2958 xtl_execute_operation_by_predicate_in_state(ExpandedBState,RawState,OpName,Pred,OpTerm,NewState,Pos,TransInfo).
2959 exec_op_by_pred_aux(_StateId,ExpandedBState,OpName,Pred,OpTerm,NewState,Pos,TransInfo) :-
2960 execute_operation_by_predicate_in_state_with_pos(ExpandedBState,OpName,Pred,OpTerm,NewState,Pos,TransInfo).
2961
2962 :- use_module(probsrc(bmachine), [b_is_variable/1,b_machine_has_constants/0]).
2963
2964 is_var_binding(bind(Var,_)) :- b_or_z_mode, b_is_variable(Var).
2965
2966
2967 % remove constants and VisB Definition entries from state:
2968 patch_state(NewState,_StateId,PatchedState) :-
2969 \+ b_or_z_mode, !, PatchedState=NewState. % XTL state is raw state
2970 patch_state(NewState,StateId,PatchedState) :-
2971 get_constants_id_for_state_id(StateId,ConstId),!,
2972 PatchedState = const_and_vars(ConstId,VarsState),
2973 include(is_var_binding,NewState,VarsState).
2974 patch_state(NewState,StateId,VarsState) :-
2975 (b_machine_has_constants
2976 -> add_error(visb_visualiser,'Could not extract constants id for state:',StateId),
2977 VarsState=NewState
2978 ; include(is_var_binding,NewState,VarsState)).
2979
2980 % the next predicate gets all operations/events associated with SvgID in the order in which they were found
2981 % note that there is only one visb_event (the last one); all others are auxiliary
2982 get_visb_event_operation(SvgID,OpName,Preds,ExecKind,TypedPred,Pos) :- % check if there are more events registered:
2983 auxiliary_visb_event(SvgID,OpName,Preds,ExecKind,TypedPred,Pos).
2984 get_visb_event_operation(SvgID,OpName,Preds,ExecKind,TypedPred,Pos) :-
2985 visb_event(SvgID,OpName,Preds,ExecKind,TypedPred,_File,Pos).
2986
2987 % ---------------
2988
2989 get_visb_hovers(List) :-
2990 findall(hover(SvgID,ID,Attr,EnterVal,ExitVal),
2991 visb_hover(SvgID,ID,Attr,EnterVal,ExitVal,_), List).
2992
2993 get_visb_svg_objects(List) :-
2994 % TODO: remove objects inlined in get_visb_default_svg_file_contents
2995 topological_sort_svg_objects(SvgIDs),
2996 findall(visb_svg_object(SvgID,SvgClass,AttrList),
2997 (member(SvgID,SvgIDs),
2998 visb_svg_obj_with_parent(SvgID,SvgClass,AttrList,_)), List).
2999
3000 visb_svg_obj_with_parent(SvgID,SvgClass,AttrList,IsChild) :-
3001 visb_svg_object(SvgID,SvgClass,List,_Desc,_Pos),
3002 (visb_svg_child(SvgID,ParentId)
3003 -> IsChild=true, AttrList = [svg_attribute(parentId,ParentId)|List] % add parentId attribute for ProB2-UI
3004 ; IsChild=false, AttrList = List).
3005
3006
3007 :- use_module(probsrc(tools),[top_sort/3]).
3008 :- use_module(library(ugraphs),[vertices_edges_to_ugraph/3]).
3009 topological_sort_svg_objects(SortedSVGObjects) :-
3010 findall(SvgID,visb_svg_object(SvgID,_,_,_,_),Vertices),
3011 findall(V-W,visb_svg_parent(V,W),Edges),
3012 vertices_edges_to_ugraph(Vertices,Edges,Graph),
3013 (top_sort(Graph,SortedSVGObjects,UnsortedNr)
3014 -> (UnsortedNr = 0 -> true
3015 ; add_warning(visb_visualiser,'Cycle in SVG_OBJECTS parent/child graph, unsorted objects: ',UnsortedNr)
3016 )
3017 ; SortedSVGObjects=Vertices,
3018 add_error(visb_visualiser,'Topological sorting of SVG_OBJECTS failed: ',Vertices)
3019 ).
3020
3021 % ------------------
3022
3023 :- use_module(probsrc(bmachine),[b_absolute_file_name_relative_to_main_machine/2]).
3024 extended_static_check_default_visb_file :-
3025 (get_default_visb_file(VisBFile,Pos)
3026 % TODO: we could check if we have an visb_history(JSONFile,_,_) option
3027 -> extended_static_check_visb_file(VisBFile,Pos)
3028 ; true).
3029
3030 extended_static_check_visb_file('',_InclusionPos) :- !,
3031 load_visb_file('').
3032 extended_static_check_visb_file(VisBFile,InclusionPos) :-
3033 b_absolute_file_name_relative_to_main_machine(VisBFile,AFile),
3034 (file_exists(AFile)
3035 -> load_visb_file(AFile)
3036 ; add_warning(lint,'VISB_JSON_FILE does not exist: ',AFile,InclusionPos)
3037 ).
3038
3039 % ---------------------------
3040
3041 % portray all VisB items in DEFINITION B syntax
3042 get_visb_ids(SIds) :-
3043 findall(Id,visb_id(Id),Ids),
3044 sort(Ids,SIds).
3045
3046 visb_id(ID) :- visb_svg_object(ID,_,_,_,_). % see svg_id_exists
3047 visb_id(ID) :- visb_item(ID,_,_,_,_,_,_), \+ visb_svg_object(ID,_,_,_,_).
3048 visb_id(ID) :- visb_event(ID,_,_,_,_,_,_), \+ visb_svg_object(ID,_,_,_,_).
3049 visb_id(ID) :- visb_hover(ID,_,_,_,_,_), \+ visb_svg_object(ID,_,_,_,_).
3050
3051 :- public portray_visb/0.
3052 portray_visb:-
3053 get_visb_ids(SIds), nth1(Nr,SIds,ID),
3054 %visb_svg_object(ID,SVG_Class,NewRestAttrs,Desc,Pos1),
3055 findall(field(Attr,b(value(StaticVal),any,[])),
3056 (visb_svg_object(ID,SVG_Class,StaticAttrs,Desc,_Pos1),
3057 ( Attr=svg_class,StaticVal=string(SVG_Class)
3058 ; Attr=comment,StaticVal=string(Desc)
3059 ; member(svg_attribute(Attr,StaticVal),StaticAttrs))
3060 ),
3061 StaticValues),
3062 findall(field(Attr,TypedExpr),
3063 visb_item(ID,Attr,TypedExpr,_UsedIds,_Desc,_PosStartOfItem,_Meta), % meta can contain override
3064 Updates,StaticValues),
3065 % TODO: incorporate events and hovers
3066 UpdateRec = b(rec(Fields),any,[]),
3067 gen_string(ID,IDS),
3068 Fields = [field(id,IDS) | Updates],
3069 (StaticValues=[] -> format('~nVISB_SVG_UPDATES~w == ',[Nr]) ; format('~nVISB_SVG_OBJECTS~w == ',[Nr])),
3070 translate:print_bexpr(UpdateRec), write(';'), nl,
3071 fail.
3072 portray_visb.
3073 gen_string(ID,b(string(ID),string,[])).
3074 % use_module(visbsrc(visb_visualiser)), visb_visualiser:portray_visb.