def report(state): init_tran = trans_dict[state.sig][e.Initial] msg = None get_state = lambda sig: l.get_state_by_sig(sig, flat_state_list) is_child = lambda sg: state in l.get_path_from_root(get_state(sg))[:-1] if isinstance(init_tran, e._Local): msg = 'cannot use LocalTransition for initial' elif isinstance(init_tran, e._Internal): msg = 'cannot use InternalTransition for initial' elif isinstance(init_tran, e._Choice): if init_tran.default is None: msg = ('must declare default when using Choice as initial') elif get_state(init_tran.default) is None: msg = 'default points to nonexistent state' elif not is_child(init_tran.default): msg = 'default target must be a child state' elif any(get_state(s) is None for s in init_tran.switch.values()): msg = 'switch dict references nonexistent state' elif not all(is_child(sig) for sig in init_tran.switch.values()): msg = 'switch dict value not a child state' # at this point we know it is instance of regular Transition elif init_tran.target == st.sig: msg = 'initial transition cannot be a loop' elif get_state(init_tran.target) is None: msg = 'transition target points to nonexistent state' elif not is_child(init_tran.target): msg = 'target state must be a child state' elif init_tran.guard is not e.always_true: msg = 'initial transition cannot have a guard' return (state, msg) if msg else None
def visit(state, visited=set()): # instantiating should be ok in this case if state in visited: return set() visited.add(state) # if orthogonal is reachable, its states are automatically reachable if state.kind == 'orthogonal': [visit(st, visited) for st in state.states] # all state's parents are reachable # visit transition targets going out of every parent state for parent in l.get_path_from_root(state): visit(parent, visited) # visit transition targets going out of current state for tran in trans_dict.get(state.sig, {}).values(): if isinstance(tran, e._Choice): to_visit = [l.get_state_by_sig(sig, flat_state_list) for sig in tran.switch.values() + [tran.default]] else: to_visit = [l.get_state_by_sig(tran.target, flat_state_list)] # nonexistent states (None values in list) are checked elsewhere [visit(st, visited) for st in to_visit if st is not None] return visited
def find_invalid_local_transitions(flat_state_list, trans_dict): """ Returns list of 3-tuples (state_sig, event_type, transition). To be valid, local transition must be must be from superstate to substate or vice versa (source and target must be in parent-child relationship), and cannot be a self-loop. """ bad_sources = find_nonexistent_transition_sources(flat_state_list, trans_dict) bad_targets = find_nonexistent_transition_targets(flat_state_list, trans_dict) bad_state_sigs = bad_sources + bad_targets get_by_sig = lambda sig: l.get_state_by_sig(sig, flat_state_list) common_parent = lambda sig_a, sig_b: l.get_common_parent( get_by_sig(sig_a), get_by_sig(sig_b)).sig return [(st_sig, evt, tran.target) for st_sig, outgoing in trans_dict.items() for evt, tran in outgoing.items() if st_sig not in bad_state_sigs and isinstance(tran, e._Local) and ( st_sig == tran.target or # loop common_parent(st_sig, tran.target) not in [st_sig, tran.target])]