| 1 | % (c) 2021-2026 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, | |
| 2 | % Heinrich Heine Universitaet Duesseldorf | |
| 3 | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) | |
| 4 | ||
| 5 | :- module(simb_parser,[load_simb_file/1, | |
| 6 | get_default_simb_json_file/1, extended_static_check_default_simb_file/0, | |
| 7 | simb_activation/3, | |
| 8 | simb_file_loaded/1, simb_activations_loaded/1, | |
| 9 | simb_activation_id/1, simb_activation_priority/2, simb_activation_kind/2, | |
| 10 | simb_activation_effect/6, | |
| 11 | simb_activation_params/2, simb_activation_options/2, | |
| 12 | triggers_activation/2]). | |
| 13 | ||
| 14 | ||
| 15 | :- use_module(probsrc(error_manager)). | |
| 16 | :- use_module(probsrc(debug)). | |
| 17 | :- use_module(probsrc(tools_json)). | |
| 18 | :- use_module(extrasrc(json_parser),[json_parse_file/3]). | |
| 19 | :- use_module(library(lists)). | |
| 20 | :- use_module(probsrc(tools),[safe_number_codes/2,ajoin/2]). | |
| 21 | :- use_module(probsrc(bsyntaxtree), [conjunct_predicates_with_pos_info/3,def_get_texpr_ids/2]). | |
| 22 | :- use_module(probsrc(tools_lists),[include_maplist/3]). | |
| 23 | ||
| 24 | ||
| 25 | :- use_module(probsrc(eventhandling),[register_event_listener/3]). | |
| 26 | :- register_event_listener(reset_specification,reset_simb,'Reset SimB information'). | |
| 27 | :- register_event_listener(reset_prob,reset_simb,'Reset SimB information'). | |
| 28 | ||
| 29 | ||
| 30 | % use_module(extrasrc(simb_simulator)), load_simb_file('RandomWalk_simulation.json') | |
| 31 | % probsli RandomWalk.mch -simulate RandomWalk_simulation.json 20 | |
| 32 | ||
| 33 | ||
| 34 | :- dynamic simb_file_loaded/1, simb_activation/3, simb_activation_pos/3, simb_activation_effect/6, | |
| 35 | simb_activation_options/2, simb_activation_params/2. | |
| 36 | ||
| 37 | simb_activations_loaded(Nr) :- findall(ID,simb_activation_id(ID),L), length(L,Nr). | |
| 38 | ||
| 39 | simb_activation_id(ID) :- simb_activation(ID,_Prio,_). | |
| 40 | simb_activation_priority(ID,Prio) :- simb_activation(ID,Prio,_). | |
| 41 | simb_activation_kind(ID,ActKind) :- simb_activation(ID,_,ActKind). | |
| 42 | simb_activation_pos(ID,Pos) :- simb_activation_pos(ID,Pos,_ActPos). | |
| 43 | ||
| 44 | reset_simb :- | |
| 45 | retractall(simb_file_loaded(_)), | |
| 46 | retractall(simb_activation(_,_,_)), | |
| 47 | retractall(simb_activation_pos(_,_,_)), | |
| 48 | retractall(simb_activation_effect(_,_,_,_,_,_)), | |
| 49 | retractall(simb_activation_options(_,_)), | |
| 50 | retractall(simb_activation_params(_,_)). | |
| 51 | ||
| 52 | :- use_module(probsrc(bmachine),[set_additional_filename_as_parsing_default/3, reset_filename_parsing_default/2]). | |
| 53 | ||
| 54 | load_simb_file(FileName) :- | |
| 55 | reset_simb, | |
| 56 | json_parse_file(FileName,Term,[rest(_),position_infos(true),strings_as_atoms(true)]), | |
| 57 | add_message(simb,'Parsed JSON: ',FileName), | |
| 58 | assert(simb_file_loaded(FileName)), | |
| 59 | set_additional_filename_as_parsing_default(FileName,NewNr,OldNr), | |
| 60 | call_cleanup(load_simb_json_term(Term,FileName), reset_filename_parsing_default(NewNr,OldNr)). | |
| 61 | ||
| 62 | load_simb_json_term(json(Objects),FileName) :- | |
| 63 | %TODO: get_json_key_object(version,) | |
| 64 | get_json_key_list(activations,Objects,List), | |
| 65 | %write(List),nl, | |
| 66 | (maplist(load_activation(FileName),List) | |
| 67 | -> simb_static_check | |
| 68 | ; add_warning(simb,'Loading SimB activations failed: ',FileName) | |
| 69 | ). | |
| 70 | % TODO: get_json_key_list(listeners,Objects,List), | |
| 71 | ||
| 72 | ||
| 73 | % load a single activation | |
| 74 | /* Example activations: | |
| 75 | { | |
| 76 | "id": "inc", | |
| 77 | "execute": "inc", | |
| 78 | "after": 500, | |
| 79 | "activating": "move" | |
| 80 | }, | |
| 81 | ||
| 82 | { | |
| 83 | "id": "reverse_lift_up_2", | |
| 84 | "execute": "reverse_lift_up", | |
| 85 | "activationKind": "single:max", | |
| 86 | "additionalGuards": "card(call_buttons \\/ inside_buttons) > 0 & min(call_buttons \\/ inside_buttons) > cur_floor", | |
| 87 | "after": 100, | |
| 88 | "activating": ["move_up_2",{"id":"other", "params":{"x":"1"}, "probability":"0.2"}] | |
| 89 | "priority": 3 | |
| 90 | }, | |
| 91 | ||
| 92 | some older SimB files define probabilisticVariables to be uniform instead of transitionSelection attribute | |
| 93 | ||
| 94 | */ | |
| 95 | ||
| 96 | activation_kinds([multi,single,'single:max','single:min']). | |
| 97 | ||
| 98 | load_activation(File,json(AttrList)) :- | |
| 99 | get_optional_json_string_attribute_with_pos(id,File,ID,IPos,AttrList,AttrList0), | |
| 100 | get_optional_json_strings_attribute_with_pos(params,File,Params,ParamsPos,AttrList0,AttrList1), | |
| 101 | maplist(create_param_tid(ID,ParamsPos),Params,TParams), | |
| 102 | activation_kinds(AllowedKinds), | |
| 103 | get_optional_json_enum_attribute(activationKind,File,AllowedKinds,ActKind,AttrList1,AttrList2), | |
| 104 | get_optional_json_array_attribute_with_pos(activating,File,ActivatingObjects,APos,AttrList2,AttrList3), | |
| 105 | include_maplist(get_activating_entry(TParams,File,APos),ActivatingObjects,Activating), | |
| 106 | % TODO: move activation entries with probabilities to the end or group them | |
| 107 | get_optional_json_strings_attribute_with_pos(execute,File,OpNames,_EPos,AttrList3,AttrList4), | |
| 108 | get_optional_json_enum_attribute(transitionSelection,File,[first,uniform],TransSelection,AttrList4,AttrList5), | |
| 109 | get_optional_json_number_formula_attribute_with_pos(after,File,0,TParams,After,_AftPos,AttrList5,AttrList6), | |
| 110 | get_optional_json_number_formula_attribute_with_pos(priority,File,1,TParams,Prio,_,AttrList6,AttrList7), | |
| 111 | get_optional_json_attribute_with_pos(chooseActivation,File,CA,CAPos,AttrList7,AttrList8), | |
| 112 | findall(triggerActivation(ActivationID,[],Probability), | |
| 113 | getChooseActivationEntry(CA,File,TParams,ActivationID,Probability),CAList), | |
| 114 | (CAList \= [], | |
| 115 | add_message(simb,'chooseActivation deprecated, use probability in activating:','',CAPos), | |
| 116 | get_probability_sum(CAList,0,ProbSum), Epsilon = 0.0001, | |
| 117 | (ProbSum < 1.0-Epsilon ; ProbSum > 1.0 + Epsilon) | |
| 118 | -> add_message(simb,'Probabilities do not add up to 1.0: ',ProbSum,CAPos) | |
| 119 | ; true | |
| 120 | ), | |
| 121 | (CAList = [] -> ActivationList = Activating | |
| 122 | ; Activating = [] -> ActivationList=CAList | |
| 123 | ; add_message(simb,'Combining activating and chooseActivation attributes: ',Activating,APos), | |
| 124 | append(Activating,CAList,ActivationList) % may be problematic if both use probabilties | |
| 125 | ), | |
| 126 | (get_optional_json_string_attribute_with_pos(predicate,File,GuardS,GPos,AttrList8,AttrList9), | |
| 127 | GuardS \= @(null) -> true | |
| 128 | ; get_optional_json_string_attribute_with_pos(additionalGuards,File,GuardS,GPos,AttrList8,AttrList9) | |
| 129 | -> (GuardS = @(null) -> true | |
| 130 | ; add_message(simb,'additionalGuards is deprecated; use predicate instead','',GPos) | |
| 131 | ) | |
| 132 | ), | |
| 133 | % predicate would be a better name than additionalGuards | |
| 134 | (GuardS = @(null) -> AddGuard0 = b(truth,pred,[]) | |
| 135 | ; parse_pred_for_ops(OpNames,TParams,GuardS,AddGuard0,GPos) -> true | |
| 136 | ; add_warning(simb,'additionalGuards cannot be parsed, discarding: ',GuardS,GPos), | |
| 137 | AddGuard0 = b(truth,pred,[]) | |
| 138 | ), | |
| 139 | maplist(check_param_type(ID,ParamsPos),TParams), | |
| 140 | get_optional_json_bool_attr(activatingOnlyWhenExecuted,true,File,[],Opts1,AttrList9,AttrList10), | |
| 141 | get_optional_json_bool_attr(errorWhenNotExecuted,false,File,Opts1,Options,AttrList10,AttrList11), | |
| 142 | get_optional_json_attribute_with_pos(fixedVariables,File,FixV,FixPos,AttrList11,AttrList12), | |
| 143 | findall('='(Var,Val),getFixedVariablesEntry(FixV,File,Var,Val),EqList), | |
| 144 | (EqList = [] -> AddGuard2 = AddGuard0 | |
| 145 | ; OpNames \= [_] | |
| 146 | -> add_warning(simb,'fixedVariables can only be used for a single operation in execute: ',OpNames,FixPos), | |
| 147 | AddGuard2 = AddGuard0 | |
| 148 | ; OpNames = [OpName], GP=gen_parse_errors_for(FixPos), | |
| 149 | (tcltk_interface:parse_tclk_parameter_values_and_pred(OpName,EqList,'btrue',AddGuard1,GP) %TODO: move to module | |
| 150 | % TODO: make TParams available | |
| 151 | -> conjunct_predicates_with_pos_info(AddGuard0,AddGuard1,AddGuard2) | |
| 152 | ; add_warning(simb,'fixedVariables cannot be parsed, discarding: ',EqList,FixPos), | |
| 153 | AddGuard2 = AddGuard0 | |
| 154 | ) | |
| 155 | ), | |
| 156 | (get_json_attribute_with_pos(Attr,AttrList12,File,_,Pos), | |
| 157 | nonmember(Attr,[comment]), | |
| 158 | add_warning(simb,'Unknown/unsupported SimB attribute: ',Attr,Pos), | |
| 159 | fail ; true), | |
| 160 | assert_simb_activation(ID,TParams,Prio,ActKind,OpNames,AddGuard2,TransSelection,After, | |
| 161 | ActivationList,Options,IPos,APos). | |
| 162 | ||
| 163 | get_optional_json_enum_attribute(Attr,File,AllowedChoices,Res,ObjList,RemObjList) :- | |
| 164 | get_optional_json_string_attribute_with_pos(Attr,File,Value,VPos,ObjList,RemObjList), | |
| 165 | (Value = @(null) -> AllowedChoices = [Res|_] % take first choice as default | |
| 166 | ; member(Value,AllowedChoices) -> Res=Value | |
| 167 | ; add_warning(simb,'Illegal value for attribute: ',Value,VPos), | |
| 168 | AllowedChoices = [Res|_] | |
| 169 | ). | |
| 170 | ||
| 171 | % parse predicate for given 'execute' list of operations | |
| 172 | parse_pred_for_ops(_,_ActivationParams,'',AddGuard,_GPos) :- !, | |
| 173 | AddGuard = b(truth,pred,[]). | |
| 174 | parse_pred_for_ops([OpName],ActivationParams,GuardS,AddGuard,GPos) :- !, | |
| 175 | % we have a single operation and can make parameters of the operation available | |
| 176 | parse_pred_for_op(GuardS,OpName,ActivationParams,AddGuard,GPos). | |
| 177 | parse_pred_for_ops(_,ActivationParams,GuardS,AddGuard,_GPos) :- | |
| 178 | % multiple operations, only allow using variables and params | |
| 179 | get_param_extra_scope(ActivationParams,ExtraScope), | |
| 180 | b_parse_machine_predicate(GuardS,[variables|ExtraScope],AddGuard). | |
| 181 | ||
| 182 | :- use_module(probsrc(bmachine), [b_parse_machine_operation_pre_post_predicate/5, | |
| 183 | b_parse_machine_predicate/3, | |
| 184 | b_parse_machine_expression_from_codes_with_prob_ids/4]). | |
| 185 | ||
| 186 | % get optional boolean attribute and if true then add to options list: | |
| 187 | get_optional_json_bool_attr(Attr,Default,File,InOpts,OutOpts,ObjList,RemObjList) :- | |
| 188 | get_optional_json_string_attribute_with_pos(Attr,File,Val,Pos,ObjList,RemObjList), | |
| 189 | (Val=true -> OutOpts = [Attr|InOpts] | |
| 190 | ; Val = @(null), Default=true -> OutOpts = [Attr|InOpts] | |
| 191 | ; OutOpts = InOpts, | |
| 192 | (member(Val,[false,true,@(null)]) -> true | |
| 193 | ; add_warning(simb,'Illegal value for boolean attribute: ',Val,Pos)) | |
| 194 | ). | |
| 195 | ||
| 196 | get_optional_json_number_formula_attribute_with_pos(Attr,File,Default,TActivationParams, | |
| 197 | NrValue,Pos,ObjList,RemObjList) :- | |
| 198 | get_optional_json_attribute_with_pos(Attr,File,Value,Pos,ObjList,RemObjList), | |
| 199 | extract_json_number_or_formula(Value,Attr,Pos,Default,TActivationParams,NrValue). | |
| 200 | ||
| 201 | :- use_module(probsrc(bsyntaxtree),[get_texpr_type/2]). | |
| 202 | % extract_json_number_or_formula: | |
| 203 | % allows both JSON numbers and strings which are parsed as a B formula | |
| 204 | % if Default is none we also allow non-integer/real formulas | |
| 205 | extract_json_number_or_formula(number(S),_,_,_Default,_,Res) :- !, Res=S. | |
| 206 | extract_json_number_or_formula(@(null),_,_,Default,_,Res) :- Default \= no_default, !, Res = Default. | |
| 207 | extract_json_number_or_formula(string(S),Attr,Pos,Default,TActivationParams,Res) :- | |
| 208 | atom_codes(S,Codes), | |
| 209 | GenParseErrors=gen_parse_errors_for(Pos), | |
| 210 | get_param_extra_scope(TActivationParams,ExtraScope), | |
| 211 | b_parse_machine_expression_from_codes_with_prob_ids(Codes,ExtraScope,TypedExpr,GenParseErrors), | |
| 212 | get_texpr_type(TypedExpr,Type), | |
| 213 | !, | |
| 214 | (extract_number_literal(TypedExpr,ENr) -> Res=ENr | |
| 215 | ; Type = integer -> Res=TypedExpr | |
| 216 | ; Type = real -> Res=TypedExpr | |
| 217 | ; Type = any -> Res=TypedExpr, | |
| 218 | add_message(simb,'Type any for attribute: ',Attr) % probably due to params; try and fix | |
| 219 | ; \+ number(Default) -> Res=TypedExpr | |
| 220 | ; ajoin(['Type for attribute ',Attr,' must be integer or real but is:'],Msg), | |
| 221 | add_error(simb,Msg,Type,Pos), | |
| 222 | Res=Default | |
| 223 | ). | |
| 224 | extract_json_number_or_formula(Obj,Attr,Pos,Default,_,Res) :- | |
| 225 | add_error(simb,'Illegal JSON number / formula value for attribute: ',Attr,Pos), | |
| 226 | write(Obj),nl, | |
| 227 | Res=Default. | |
| 228 | ||
| 229 | ||
| 230 | :- use_module(probsrc(kernel_reals),[construct_real_number/2]). | |
| 231 | % convert if possible a static expression into a Prolog value; we could try and call b_compiler? | |
| 232 | % see evaluable_integer_expression and compute_static_int_texpression/ compute_static_expression | |
| 233 | extract_number_literal(b(integer(Nr),integer,_),Nr). | |
| 234 | extract_number_literal(b(real(R),real,_),Nr) :- construct_real_number(R,Nr). | |
| 235 | ||
| 236 | get_param_extra_scope(TActivationParams,ExtraScope) :- | |
| 237 | (TActivationParams=[] -> ExtraScope = [] ; ExtraScope = [identifier(TActivationParams)]). | |
| 238 | ||
| 239 | parse_pred_for_op(Pred,OpName,TActivationParams,TypedPred,Pos) :- | |
| 240 | get_param_extra_scope(TActivationParams,ExtraScope), | |
| 241 | b_parse_machine_operation_pre_post_predicate(Pred,ExtraScope,TypedPred,OpName,gen_parse_errors_for(Pos)). | |
| 242 | ||
| 243 | % crate a typed id for passing as ExtraScope for parsing | |
| 244 | create_param_tid(ActID,ParamsPos,ID,b(identifier(ID),_ANYTYPE,[activation_param(ActID),nodeid(ParamsPos)])). | |
| 245 | ||
| 246 | check_param_type(ActID,ParamsPos,b(identifier(ID),TYPE,_)) :- | |
| 247 | (ground(TYPE) -> true | |
| 248 | ; ajoin(['Type of parameter ',ID,' could not be determined from predicate for: '],Msg), | |
| 249 | add_warning(simb,Msg,ActID,ParamsPos) | |
| 250 | ). % TODO: instantiate | |
| 251 | :- use_module(probsrc(bmachine),[b_top_level_operation/1]). | |
| 252 | :- use_module(probsrc(tools_matching), [get_possible_operation_matches_msg/2]). | |
| 253 | ||
| 254 | special_operation('$setup_constants'). | |
| 255 | special_operation('$initialise_machine'). | |
| 256 | special_operation(skip). | |
| 257 | ||
| 258 | assert_simb_activation(ID,_,Prio,ActKind,OpNames,AddGuard,TransSelection,After,ActivationList,Options,IPos,_) :- | |
| 259 | (debug_mode(off) -> true | |
| 260 | ; format('Loaded Activation id=~w~n Priority: ~w~n Activation Kind: ~w~n Execute: ~w (~w)~n Guard: ',[ID,Prio,ActKind,OpNames,TransSelection]), | |
| 261 | translate:print_bexpr(AddGuard),nl, | |
| 262 | format(' After: ~w ms~n Activating: ~w~n Options: ~w~n~n',[After,ActivationList,Options]) | |
| 263 | ), | |
| 264 | member(OpName,OpNames), | |
| 265 | \+ special_operation(OpName), | |
| 266 | \+ b_top_level_operation(OpName), | |
| 267 | (get_possible_operation_matches_msg(OpName,FMsg) | |
| 268 | -> ajoin(['Unknown operation in SimB Activation ',ID,' (did you mean ',FMsg,' ?) :'], Msg), | |
| 269 | add_warning(simb,Msg,OpName,IPos) | |
| 270 | ; add_warning(simb,'Unknown top-level operation: ',OpName,IPos) | |
| 271 | ), | |
| 272 | fail. | |
| 273 | assert_simb_activation(ID,_,_Prio,_,_Execute,_,_,_After,_ActivationList,_,IPos,_) :- | |
| 274 | simb_activation_id(ID), | |
| 275 | add_warning(simb,'Duplicate definition of SimB Activation: ',ID,IPos), | |
| 276 | fail. | |
| 277 | assert_simb_activation(ID,Params,Prio,ActKind,Execute,AddGuard,TransSelection,After,ActivationList,Options,IPos,APos) :- | |
| 278 | assert(simb_activation(ID,Prio,ActKind)), | |
| 279 | assert(simb_activation_pos(ID,IPos,APos)), | |
| 280 | assert(simb_activation_effect(ID,Execute,AddGuard,TransSelection,After,ActivationList)), | |
| 281 | assert(simb_activation_params(ID,Params)), | |
| 282 | assert(simb_activation_options(ID,Options)). | |
| 283 | ||
| 284 | %:- use_module(probsrc(bsyntaxtree), [get_texpr_pos/2]). | |
| 285 | %get_activation_params_pos(ID,Pos) :- | |
| 286 | % simb_activation_params(ID,[TID|_]), | |
| 287 | % get_texpr_pos(TID,Pos). | |
| 288 | %get_activation_params_pos(_,unknown). | |
| 289 | ||
| 290 | % extract entries from RHS of something like: "chooseActivation": {"inc": "0.8", "dec": "1"} | |
| 291 | getChooseActivationEntry(json(CAL),File,TActivationParams,ActivationID,Probability) :- | |
| 292 | get_json_attribute_with_pos(ActivationID,CAL,File,PVal,Pos), | |
| 293 | extract_json_number_or_formula(PVal,probability,Pos,1,TActivationParams,Probability). | |
| 294 | ||
| 295 | atom_to_nr(Atom,Nr) :- atom_codes(Atom,CC), safe_number_codes(Nr,CC). | |
| 296 | ||
| 297 | get_probability_sum([],Acc,Acc). | |
| 298 | get_probability_sum([triggerActivation(_,_,Probability)|T],Acc,Res) :- number(Probability), % fails for formulas | |
| 299 | Acc2 is Acc+Probability, | |
| 300 | get_probability_sum(T,Acc2,Res). | |
| 301 | ||
| 302 | % extract a single activating entry: either activation id as string, or an object with id, params, ... attributes | |
| 303 | get_activating_entry(_,_,_,string(ActivationID),Res) :- !, % single activation id without parameter or probability | |
| 304 | Res = ActivationID. | |
| 305 | get_activating_entry(TParams,File,Pos,json(AttrList),Res) :- !, | |
| 306 | get_optional_json_string_attribute_with_pos(id,File,ActID,_IPos,AttrList,AttrList0), | |
| 307 | (ActID = @(null) | |
| 308 | -> add_error(simb,'Entries for activating need an id field:',AttrList,Pos), fail | |
| 309 | ; true | |
| 310 | ), | |
| 311 | get_optional_json_attribute_with_pos('params',File,ParaObj,_,AttrList0,AttrList1), | |
| 312 | findall(param(ParameterName,ParameterValue), | |
| 313 | getParameterValueEntry(ParaObj,File,TParams,ParameterName,ParameterValue),ParaList), | |
| 314 | (get_json_attribute_with_pos('probability',AttrList1,File,PVal,PPos) | |
| 315 | -> extract_json_number_or_formula(PVal,probability,PPos,1,TParams,Probability) | |
| 316 | ; Probability=none | |
| 317 | ), | |
| 318 | Res = triggerActivation(ActID,ParaList,Probability). | |
| 319 | get_activating_entry(_,_,Pos,Obj,_Res) :- | |
| 320 | add_error(simb,'Unknown activating entry:',Obj,Pos),fail. | |
| 321 | ||
| 322 | ||
| 323 | % extract ParameterName/Value pairs from a JSON object for params attribute: | |
| 324 | % example: "activating" : [{"id":"ENV_Turn_EngineOn_1","params":{"x":"1","y":"TRUE"}}] | |
| 325 | getParameterValueEntry(json(CAL),File,TActivationParams,ParameterName,ParameterValue) :- | |
| 326 | get_json_attribute_with_pos(ParameterName,CAL,File,PVal,Pos), | |
| 327 | extract_json_number_or_formula(PVal,parameter_value,Pos,no_default,TActivationParams,ParameterValue). | |
| 328 | ||
| 329 | % choose entries from RHS of something like: "fixedVariables": {"delta": "curDeadlines(blink_deadline) - curTime"}, | |
| 330 | getFixedVariablesEntry(json(FV),File,VariableParameterName,Value) :- | |
| 331 | get_json_attribute_with_pos(VariableParameterName,FV,File,PVal,Pos), | |
| 332 | extract_json_string(PVal,fixedVariables,Pos,Value). | |
| 333 | ||
| 334 | % -------------------- | |
| 335 | ||
| 336 | :- use_module(probsrc(pref_definitions),[b_get_definition_string_from_spec/3]). | |
| 337 | :- use_module(probsrc(bmachine),[bmachine_is_precompiled/0, b_absolute_file_name_relative_to_main_machine/2]). | |
| 338 | get_default_simb_json_file(FullPath) :- bmachine_is_precompiled, | |
| 339 | b_get_definition_string_from_spec('SIMB_JSON_FILE', _Pos, Path), | |
| 340 | b_absolute_file_name_relative_to_main_machine(Path,FullPath). | |
| 341 | ||
| 342 | extended_static_check_default_simb_file :- simb_file_loaded(_),!, % a file was already loaded | |
| 343 | simb_static_check. | |
| 344 | extended_static_check_default_simb_file :- | |
| 345 | (get_default_simb_json_file(SimBFile) | |
| 346 | -> load_simb_file(SimBFile) % will perform static check | |
| 347 | ; true). % nothing to check | |
| 348 | % -------------------- | |
| 349 | ||
| 350 | % linting / static checking | |
| 351 | ||
| 352 | :- dynamic called_activation_id/1. | |
| 353 | ||
| 354 | ||
| 355 | initial_activation('$setup_constants'). | |
| 356 | initial_activation('$initialise_machine'). | |
| 357 | ||
| 358 | :- use_module(library(ordsets),[ord_subtract/3]). | |
| 359 | :- use_module(probsrc(bmachine),[b_machine_has_constants_or_properties/0]). | |
| 360 | simb_static_check :- ID = '$initialise_machine', | |
| 361 | simb_activation_pos(ID,Pos,_), | |
| 362 | simb_activation_effect(ID,Execute,_,_,_,_), Execute = [ID], | |
| 363 | \+ simb_activation_id('$setup_constants'), | |
| 364 | b_machine_has_constants_or_properties, | |
| 365 | OpNames = ['$setup_constants'], Guard = b(truth,pred,[]), | |
| 366 | ActivationList = [ID], P = unknown, | |
| 367 | add_message(simb,'Adding $setup_constants activation to activate: ',ID,Pos), | |
| 368 | assert_simb_activation('$setup_constants',[],1,multi,OpNames,Guard,first,0,ActivationList,[],P,P), | |
| 369 | fail. | |
| 370 | simb_static_check :- | |
| 371 | retractall(called_activation_id(_)), | |
| 372 | simb_activation_pos(ID,_Pos,ActPos), | |
| 373 | simb_activation_effect(ID,_Execute,_AddGuard,_TransSelection,_After,ActivationList), | |
| 374 | triggers_activation(ActivationList,ID2,CallParams), | |
| 375 | (called_activation_id(ID2) -> true | |
| 376 | ; assert(called_activation_id(ID2)) | |
| 377 | ), | |
| 378 | (simb_activation_id(ID2) | |
| 379 | -> simb_activation_params(ID2,TParamIDs), | |
| 380 | def_get_texpr_ids(TParamIDs,ParamIds), sort(ParamIds,SParamIds), | |
| 381 | maplist(get_param_id,CallParams,CallParamIds), sort(CallParamIds,SCallParamIds), | |
| 382 | (ord_subtract(SCallParamIds,SParamIds,UnknownParas), | |
| 383 | UnknownParas = [_|_], | |
| 384 | ajoin(['Activation ',ID,' declares unknown parameters when activating ',ID2,': '],Msg), | |
| 385 | add_warning(simb,Msg,UnknownParas,ActPos) | |
| 386 | ; | |
| 387 | ord_subtract(SParamIds,SCallParamIds,ParasNotSet), | |
| 388 | ParasNotSet = [_|_], | |
| 389 | ajoin(['Activation ',ID,' does not define parameters when activating ',ID2,': '],Msg), | |
| 390 | add_warning(simb,Msg,ParasNotSet,ActPos) | |
| 391 | ) | |
| 392 | ; ajoin(['Activation ',ID,' triggers an unknown activation: '],Msg), | |
| 393 | add_warning(simb,Msg,ID2,ActPos) | |
| 394 | ), | |
| 395 | fail. | |
| 396 | simb_static_check :- | |
| 397 | simb_activation_pos(ID,Pos,_), | |
| 398 | \+ called_activation_id(ID), | |
| 399 | \+ initial_activation(ID), | |
| 400 | add_message(simb,'Activation is not triggered by any other activation: ',ID,Pos), | |
| 401 | fail. | |
| 402 | simb_static_check. | |
| 403 | ||
| 404 | get_param_id(param(ID,_),ID). | |
| 405 | ||
| 406 | triggers_activation(ActivationList,ID) :- triggers_activation(ActivationList,ID,_Params). | |
| 407 | ||
| 408 | triggers_activation(ActivationList,ID,Params) :- | |
| 409 | member(Act,ActivationList), | |
| 410 | (atomic(Act) -> ID=Act, Params = [] | |
| 411 | ; Act = triggerActivation(AID,Params,_Prob) -> ID=AID). |