Esempio n. 1
0
    def clear(self):
        # Database: [DoorID] [Address] [Label]
        #
        # The database is represented by a dictionary that maps:
        #
        #               DoorID --> tuple(Address, Label)
        #
        self.__d2la = TypedDict(DoorID, AddressLabelPair)

        # Track all generated DoorID objects with 2d-dictionary that maps:
        #
        #          StateIndex --> ( DoorSubIndex --> DoorID )
        #
        # Where the DoorID has the 'state_index' and 'door_index' equal to
        # 'StateIndex' and 'DoorSubIndex'.
        #
        self.__door_id_db = {}  # TypedDict(long, dict)

        # Track addresses which are subject to 'goto' and those which need to
        # be routed.
        self.__gotoed_address_set = TypedSet(long)
        self.__routed_address_set = TypedSet(long)

        # Address counter to generate unique addresses
        self.__address_i = long(-1)

        # Unique incidence id inside a mode
        self.__incidence_id_i = long(-1)

        # Mapping from incidence_id to terminal state index
        self.__map_incidence_id_to_state_index = {}
Esempio n. 2
0
    def clear(self):
        # Database: [DoorID] [Address] [Label] 
        # 
        # The database is represented by a dictionary that maps:
        #
        #               DoorID --> tuple(Address, Label)
        #
        self.__d2la = TypedDict(DoorID, AddressLabelPair)

        # Track all generated DoorID objects with 2d-dictionary that maps:
        #
        #          StateIndex --> ( DoorSubIndex --> DoorID )
        #
        # Where the DoorID has the 'state_index' and 'door_index' equal to
        # 'StateIndex' and 'DoorSubIndex'.
        #
        self.__door_id_db = {} # TypedDict(long, dict)
       
        # Track addresses which are subject to 'goto' and those which need to
        # be routed.
        self.__gotoed_address_set = TypedSet(long)
        self.__routed_address_set = TypedSet(long)

        # Address counter to generate unique addresses
        self.__address_i = long(-1)

        # Unique incidence id inside a mode
        self.__incidence_id_i = long(-1)

        # Mapping from incidence_id to terminal state index
        self.__map_incidence_id_to_state_index = {}
Esempio n. 3
0
    def __init__(self, StateIndex, DoorId_OpList):
        self.state_index = StateIndex
        root_child_set = set(door_id for door_id, cl in DoorId_OpList)
        self.root = Door(dial_db.new_door_id(StateIndex), [], None,
                         root_child_set)
        self.door_db = TypedDict(DoorID, Door)  # {}   # map: DoorID --> Door
        #                       #  ... with ALL Door-s related to the 'problem'

        # map: Shared Tail --> SharedTailCandidateSet
        self._tail_db = {}  # map: command list tail --> those who share it
        self._candidate_db = {}  # map: DoorID --> Door
        #                       #  ... but only with those DoorID-s that are
        #                       #      subject to shared tail investigation.

        # Doors which cannot extract a tail are dropped from consideration.
        # (See discussion at end [DROP_NON_SHARER])
        # BUT: First the WHOLE SET of Door-s must be considered!
        # If a Door shares from the beginning => into 'good_set'.
        # Else, look into _tail_db if it finally shared something.
        good_set = set()

        for door_id, command_list in DoorId_OpList:
            door = Door(door_id, command_list, self.root, set())
            if self._tail_db_enter(door_id, door):
                good_set.add(door_id)
            self._candidate_db[door_id] = door
            self.door_db[door_id] = door

        # Drop non-sharing Door-s.
        # (See [DROP_NON_SHARER])
        for door_id in self._candidate_db.keys():
            if door_id in good_set: continue
            elif self._tail_db_has_door_id(door_id): continue
            del self._candidate_db[door_id]
Esempio n. 4
0
class DialDB(object):
    __slots__ = ("__d2la", "__door_id_db", "__gotoed_address_set",
                 "__routed_address_set", "__address_i", "__incidence_id_i",
                 "__map_incidence_id_to_state_index")

    def __init__(self):
        self.clear()

    def __debug_address_generation(self, DoorId, Address, *SuspectAdrList):
        """Prints the callstack if an address of SuspectAdrList is generated.
        """
        if Address not in SuspectAdrList:
            return
        print "#DoorID %s <-> Address %s" % (DoorId, Address)
        print_callstack()

    def __debug_address_usage(self, Address, *SuspectAdrList):
        """Prints the callstack if an address of SuspectAdrList is generated.
        """
        if Address not in SuspectAdrList:
            return
        print "#Used %s" % Address
        print_callstack()

    def __debug_incidence_generation(self, IncidenceId, StateIndex):
        print "#Generated: %s -> state: %s" % (IncidenceId, StateIndex)
        print_callstack()

    def __debug_gotoed_address(self, Address, *SuspectAdrList):
        if Address not in SuspectAdrList:
            return
        print "#Gotoed Address: %s" % Address
        print_callstack()

    def clear(self):
        # Database: [DoorID] [Address] [Label]
        #
        # The database is represented by a dictionary that maps:
        #
        #               DoorID --> tuple(Address, Label)
        #
        self.__d2la = TypedDict(DoorID, AddressLabelPair)

        # Track all generated DoorID objects with 2d-dictionary that maps:
        #
        #          StateIndex --> ( DoorSubIndex --> DoorID )
        #
        # Where the DoorID has the 'state_index' and 'door_index' equal to
        # 'StateIndex' and 'DoorSubIndex'.
        #
        self.__door_id_db = {}  # TypedDict(long, dict)

        # Track addresses which are subject to 'goto' and those which need to
        # be routed.
        self.__gotoed_address_set = TypedSet(long)
        self.__routed_address_set = TypedSet(long)

        # Address counter to generate unique addresses
        self.__address_i = long(-1)

        # Unique incidence id inside a mode
        self.__incidence_id_i = long(-1)

        # Mapping from incidence_id to terminal state index
        self.__map_incidence_id_to_state_index = {}

    def routed_address_set(self):
        return self.__routed_address_set

    def gotoed_address_set(self):
        return self.__gotoed_address_set

    def label_is_gotoed(self, Label):
        address = self.get_address_by_label(Label)
        return address in self.__gotoed_address_set

    def __new_entry(self, StateIndex=None, DoorSubIndex=None):
        """Create a new entry in the database. First, a DoorID is generated.
        Then a new set of address and label is linked to it. The link between
        DoorID and AddressLabelPair is stored in '.__d2la'. A list of existing
        DoorID-s is maintained in '.__door_id_db'.
        """
        def specify(StateIndex, DoorSubIndex):
            if StateIndex is None:
                state_index = sm_index.get()  # generate a new StateIndex
            else:
                state_index = StateIndex
            if DoorSubIndex is None:
                door_sub_index = self.max_door_sub_index(state_index) + 1
            else:
                door_sub_index = DoorSubIndex
            return state_index, door_sub_index

        state_index, door_sub_index = specify(StateIndex, DoorSubIndex)
        door_id = DoorID(state_index, door_sub_index, PlainF=True)
        address_label_pair = self.register_door_id(door_id)

        return door_id, address_label_pair

    def max_door_sub_index(self, StateIndex):
        """RETURN: The greatest door sub index for a given StateIndex. 
                   '-1' if not index has been used yet.
        """
        result = -1
        sub_db = self.__door_id_db.get(StateIndex)
        if sub_db is None: return result

        for dsi in (x for x in sub_db.iterkeys()
                    if isinstance(x, (int, long))):
            if dsi > result: result = dsi
        return result

    def register_door_id(self, DoorId):
        self.__address_i += 1
        address_label_pair = AddressLabelPair(self.__address_i,
                                              "_%i" % self.__address_i)

        if False:  # True/False activates debug messages
            self.__debug_address_generation(DoorId, self.__address_i, 18)

        self.__d2la[DoorId] = address_label_pair

        sub_db = self.__door_id_db.get(DoorId.state_index)
        if sub_db is None:
            self.__door_id_db[DoorId.state_index] = {DoorId.door_index: DoorId}
        else:
            assert DoorId.door_index not in sub_db  # Otherwise, it would not be new
            sub_db[DoorId.door_index] = DoorId

        return address_label_pair

    def access_door_id(self, StateIndex, DoorSubIndex):
        """Try to get a DoorID from the set of existing DoorID-s. If a DoorID
        with 'StateIndex' and 'DoorSubIndex' does not exist yet, then create it.
        """
        sub_db = self.__door_id_db.get(StateIndex)
        if sub_db is None:
            result = self.new_door_id(StateIndex, DoorSubIndex)
        else:
            door_id = sub_db.get(DoorSubIndex)
            if door_id is None:
                result = self.new_door_id(StateIndex, DoorSubIndex)
            else:
                result = door_id
        return result

    def new_incidence_id(self):
        self.__incidence_id_i += 1
        return self.__incidence_id_i

    def new_door_id(self, StateIndex=None, DoorSubIndex=None):
        door_id, alp = self.__new_entry(StateIndex, DoorSubIndex)
        return door_id

    def new_address(self, StateIndex=None):
        door_id, alp = self.__new_entry(StateIndex)
        return alp.address

    def new_label(self, StateIndex=None):
        door_id, alp = self.__new_entry(StateIndex)
        return alp.label

    def get_label_by_door_id(self, DoorId, GotoedF=False):
        assert DoorId in self.__d2la, "%s" % str(DoorId)
        address, label = self.__d2la[DoorId]
        if GotoedF:
            self.mark_address_as_gotoed(address)
        return label

    def get_address_by_door_id(self, DoorId, RoutedF=False):
        address = self.__d2la[DoorId][0]
        if RoutedF:
            self.mark_address_as_routed(address)
        return address

    def get_door_id_by_address(self, Address):
        for door_id, info in self.__d2la.iteritems():
            if info[0] == Address: return door_id
        return None

    def get_label_by_address(self, Address, GotoedF=False):
        for address, label in self.__d2la.itervalues():
            if address == Address:
                self.mark_address_as_gotoed(address)
                return label
        return None

    def get_door_id_by_label(self, Label):
        for door_id, info in self.__d2la.iteritems():
            if info[1] == Label: return door_id
        return None

    def get_address_by_label(self, Label):
        for door_id, info in self.__d2la.iteritems():
            if info[1] == Label: return info[0]
        return None

    def mark_address_as_gotoed(self, Address):
        if False:
            self.__debug_gotoed_address(Address, 39)
        self.__gotoed_address_set.add(Address)

    def mark_label_as_gotoed(self, Label):
        self.mark_address_as_gotoed(self.get_address_by_label(Label))

    def mark_door_id_as_gotoed(self, DoorId):
        self.mark_address_as_gotoed(self.get_address_by_door_id(DoorId))

    def mark_address_as_routed(self, Address):
        self.__routed_address_set.add(Address)
        # Any address which is subject to routing is 'gotoed', at least inside
        # the router (e.g. "switch( ... ) ... case AdrX: goto LabelX; ...").
        self.mark_address_as_gotoed(Address)

    def mark_label_as_routed(self, Label):
        self.mark_address_as_routed(self.get_address_by_label(Label))

    def mark_door_id_as_routed(self, DoorId):
        self.mark_address_as_routed(self.get_address_by_door_id(DoorId))

    def map_incidence_id_to_state_index(self, IncidenceId):
        assert isinstance(IncidenceId, (int, long)) or IncidenceId in E_IncidenceIDs, \
               "Found <%s>" % IncidenceId

        index = self.__map_incidence_id_to_state_index.get(IncidenceId)
        if index is None:
            index = sm_index.get()
            self.__map_incidence_id_to_state_index[IncidenceId] = index

        if False:
            self.__debug_incidence_generation(IncidenceId, index)
        return index
Esempio n. 5
0
class Entry(object):
    """________________________________________________________________________

    An Entry object stores commands to be executed at entry into a state
    depending on a particular source state; and may be also depending on
    the particular trigger.
    
    BASICS _________________________________________________________________

    To keep track of entry actions, OpList-s need to 
    be associated with a TransitionID-s, i.e. pairs of (state_index,
    from_state_index). This happens in the member '.__db', i.e.
    
       .action_db:    TransitionID --> TransitionAction

    where a TransitionID consists of: .from_state_index
                                      .state_index
                                      .trigger_id 
 
    and a TransitionAction consists of: .door_id
                                        .command_list
                                        
    where '.door_id'  identifies a specific door  of the  entry into  the state.
    It is distinctly associated with a list of commands '.command_list'. The 
    commands of '.command_list' are executed if the state is entered by
    a transition given with the key's TransitionID. A call to 

                            action_db.categorize()

    ensures that
                                  1     1
                        DoorID  <---------> OpList
   
    In words: 
    
       -- each TransitionAction *has a* valid door_id. That is, every list of
          commands is identified with a door_id.

       -- The door_id *distinctly* determines the command list (in the entry). 
          That is, door_id-s of TransitionAction-s differ if and only if their
          command lists are different.
                  
    Later on in the code generation, a 'door tree' is generated to produce
    optimized code which profits from common commands in command lists. But,
    for now, it is important to remember:

              .---------------------------------------------------.
              |  A DoorID distinctly identifies a OpList to  |
              |       be executed the at entry of a state.        |
              '---------------------------------------------------'
    """

    __slots__ = ("__db", "__largest_used_door_sub_index", "__trigger_id_db", "dial_db")

    def __init__(self, dial_db):
        self.__db                          = TypedDict(TransitionID, TransitionAction)
        self.__largest_used_door_sub_index = 0    # '0' is used for 'Door 0', i.e. reload entry
        self.__trigger_id_db               = {}   # (ToState, FromState) --> max. used trigger_id
        self.dial_db                     = dial_db

    def get(self, TheTransitionID):
        return self.__db.get(TheTransitionID)

    def get_command_list(self, StateIndex, FromStateIndex, TriggerId=0):
        action = self.__db.get(TransitionID(StateIndex, FromStateIndex, TriggerId))
        if action is None: return None
        else:              return action.command_list

    def get_door_id(self, StateIndex, FromStateIndex, TriggerId=0):
        """RETURN: DoorID of the door which implements the transition 
                          (FromStateIndex, StateIndex).
                   None,  if the transition is not implemented in this
                          state.
        """
        action = self.__db.get(TransitionID(StateIndex, FromStateIndex, TriggerId))
        if action is None: return None
        else:              return action.door_id

    def get_transition_id_list(self, DoorId):
        return [ 
           transition_id for transition_id, action in self.__db.iteritems()
                         if action.door_id == DoorId 
        ]

    def absorb(self, Other):
        """Absorbs all, but the 'reload transitions'.
        """
        for tid, action in Other.__db.iteritems():
            self.enter(tid.target_state_index, tid.source_state_index, action)

        if self.__largest_used_door_sub_index < Other.__largest_used_door_sub_index:
            self.__largest_used_door_sub_index = Other.__largest_used_door_sub_index

    @typed(Cl=OpList)
    def enter_OpList(self, ToStateIndex, FromStateIndex, Cl):
        return self.enter(ToStateIndex, FromStateIndex, TransitionAction(Cl))

    @typed(TheAction=TransitionAction)
    def enter(self, ToStateIndex, FromStateIndex, TheAction):
        assert isinstance(TheAction, TransitionAction)
        #!! It is ABSOLUTELY essential, that the OpList-s related to actions are
        #!! independent! Each transition must have its OWN OpList!
        assert all(id(TheAction.command_list) != id(action.command_list) 
                   for transition_id, action in self.__db.iteritems())

        trigger_id    = self.__get_trigger_id(ToStateIndex, FromStateIndex)
        transition_id = TransitionID(ToStateIndex, FromStateIndex, trigger_id)
        self.__db[transition_id] = TheAction
        return transition_id

    @typed(ta=TransitionAction)
    def enter_state_machine_entry(self, SM_id, ToStateIndex, ta):
        ta.door_id = DoorID.state_machine_entry(SM_id, self.dial_db)
        return self.enter(ToStateIndex, E_StateIndices.BEFORE_ENTRY, ta)

    def __get_trigger_id(self, ToStateIndex, FromStateIndex):
        ft = (ToStateIndex, FromStateIndex)
        tmp = self.__trigger_id_db.get(ft)
        # "FromStateIndex == E_StateIndices.BEFORE_ENTRY" indicates the entry into the 
        # state machine. There cannot be more than one entry into the state 
        # machine. Thus, it cannot appear twice.
        assert FromStateIndex != E_StateIndices.BEFORE_ENTRY or tmp is None

        if tmp is None: self.__trigger_id_db[ft]  = 0; tmp = 0;
        else:           self.__trigger_id_db[ft] += 1
        return tmp

    def size(self):
        return len(self.__db)

    def add_Accepter_on_all(self, AccConditionID, AcceptanceID):
        """Add an acceptance at the top of each accepter at every door. If there
           is no accepter in a door it is created.
        """
        for ta in self.__db.itervalues():
            # Catch the accepter, if there is already one, if not create one.
            ta.command_list.access_accepter().add(AccConditionID, AcceptanceID)

    @typed(AccConditionSet=tuple)
    def add_StoreInputPosition(self, StateIndex, FromStateIndex, AccConditionSet, PositionRegister, Offset):
        """Add 'store input position' to specific door. See 'SeStoreInputPosition'
           comment for the reason why we do not store pre-context-id.
        """
        command_list = self.__db[TransitionID(StateIndex, FromStateIndex, 0)].command_list
        cmd          = Op.StoreInputPosition(AccConditionSet, PositionRegister, Offset)
        # Make sure it is the first!
        command_list.insert(0, cmd)
        # Never store twice in the same position register! 
        # => Make sure, that there is no second of the same kind!
        i = len(command_list) - 1
        while i >= 1: # leave 'i=0' which has just been inserted!
            if command_list[i] == cmd:
                del command_list[i]
            i -= 1

    def has_transition(self, ToStateIndex, FromStateIndex):
        for key in self.__db.iterkeys():
            if key.target_state_index == ToStateIndex and key.source_state_index == FromStateIndex:
                return True
        return False

    def has_command(self, OpId):
        assert OpId in E_Op
        for action in self.__db.itervalues():
            if action.command_list.has_command_id(OpId): 
                return True
        return False

    def door_id_set(self):
        """RETURNS: The door ids of this entry.

        In the frame of a CommandTree, this set is the set of 'leaf door ids'.
        """
        return set(action.door_id 
                   for action in self.__db.itervalues()
                   if action.door_id is not None)

    def get_state_machine_entry_door_id(self):
        """RETURNS: DoorID, if the entry contains THE entry into the analyzer.
                    None, if not.
        """
        for transition_id, action in self.__db.iteritems():
            if transition_id.source_state_index != E_StateIndices.BEFORE_ENTRY: 
                continue
            assert action.door_id.door_index == E_DoorIdIndex.STATE_MACHINE_ENTRY
            return action.door_id
        return None

    def delete(self, StateIndex, FromStateIndex, TriggerId=0):
        del self.__db[TransitionID(StateIndex, FromStateIndex, 0)]

    def replace_position_registers(self, PositionRegisterMap):
        """Originally, each pattern gets its own position register if it is
        required to store/restore the input position. The 'PositionRegisterMap'
        is a result of an analysis which tells whether some registers may
        actually be shared. This function does the replacement of positioning
        registers based on what is given in 'PositionRegisterMap'.
        """
        for action in self.__db.itervalues():
            action.command_list.replace_position_registers(PositionRegisterMap)

        return

    def delete_superfluous_commands(self):
        for action in self.__db.itervalues():
            action.command_list.delete_superfluous_commands()
        return

    def itervalues(self):
        for ta in self.__db.itervalues():
            assert ta.door_id is not None, ".categorize() needs to be called before this!"
            yield ta

    def iteritems(self):
        return self.__db.iteritems()

    def __setitem__(self, Key, Value):
        #!! It is ABSOLUTELY essential, that the OpList-s related to actions are
        #!! independent! Each transition must have its OWN OpList!
        for action in self.__db.itervalues():
            assert id(Value.command_list) != id(action.command_list)

        self.__db[Key] = Value
        return Value

    def categorize(self, StateIndex):
        """
        This function considers TransitionActions where '.door_id is None' and
        assigns them a DoorID.  A DoorID identifies (globally) the OpList
        and the state which they enter. In other words, if two transition actions
        of a state have the same command lists, they have the same DoorID. 

        RETURNS: List of newly assigned pairs of (TransitionID, DoorID)s.
        """
        #!! It is ABSOLUTELY essential, that the OpList-s related to actions are
        #!! independent! Each transition must have its OWN OpList!
        cmd_list_ids = [ 
            id(action.command_list) for action in self.__db.itervalues() 
        ]
        assert len(cmd_list_ids) == len(set(cmd_list_ids)) # size(list) == size(unique set)

        work_list = [
            (transition_id, action) for transition_id, action in self.__db.iteritems()
                                    if action.door_id is None
        ]
        if not work_list: return

        command_list_db = dict(
            (action.command_list, action.door_id) for action in self.__db.itervalues()
                                                  if action.door_id is not None
        )

        def _get_door_id(CL):
            # If there was an action with the same command list, then assign
            # the same door id. Leave the action intact! May be, it is modified
            # later and will differ from the currently same action.
            new_door_id = command_list_db.get(CL)

            if new_door_id is not None: return new_door_id

            return self.dial_db.new_door_id(StateIndex)

        def sort_key(X):
            return (X[0].target_state_index, X[0].source_state_index, X[0].trigger_id)

        for transition_id, action in sorted(work_list, key=sort_key): # NOT: 'iteritems()'
            action.door_id                       = _get_door_id(action.command_list)
            command_list_db[action.command_list] = action.door_id

        assert self.check_consistency()

    def check_consistency(self):
        """Any two entries with the same DoorID must have the same command list
           associated with it.
        """
        check_db = {}
        for action in self.__db.itervalues():
            if action.door_id is None: 
                continue
            cmp_command_list = check_db.get(action.door_id)
            if cmp_command_list is None: 
                check_db[action.door_id] = action.command_list
            elif cmp_command_list != action.command_list:
                return False

        # Some commands shall never occur more than once in a command list:
        # --> Op.unique_set
        for transition_action in self.__db.itervalues():
            unique_found_set = set()
            for cmd in transition_action.command_list:
                if   cmd.id not in Op.unique_set: continue
                elif cmd.id in unique_found_set:       return False
                unique_found_set.add(cmd.id)
        return True

    def get_string(self):
        txt = []
        for tid, ta in self.__db.iteritems():
            txt.append("%s:%s: {\n" % (tid, ta.door_id))
            for command in ta.command_list:
                txt.append("    " + repr(command))
            txt.append("}\n")
        return "".join(txt)

    @property
    def action_db(self):
        return self.__db

    def __hash__(self):
        xor_sum = 0
        for door in self.__db.itervalues():
            xor_sum ^= hash(door.command_list)
        return xor_sum

    def __eq__(self, Other):
        assert False, "not used"

    def is_equal(self, Other):
        assert False, "not used"

    def __repr__(self):
        def get_accepters(AccepterList):
            if len(AccepterList) == 0: return []
            assert len(AccepterList) == 1
            return [ str(AccepterList[0]) ]

        def get_storers(StorerList):
            txt = [ 
                str(cmd)
                for cmd in sorted(StorerList, key=lambda cmd: (cmd.content.acceptance_condition_set, cmd.content.position_register))
            ]
            return txt

        def get_pre_context_oks(PCOKList):
            txt = [
                str(cmd)
                for cmd in sorted(PCOKList, key=lambda cmd: cmd.content.acceptance_condition_id)
            ]
            return txt

        def get_set_template_state_keys(TemplateStateKeySetList):
            txt = [
                str(cmd)
                for cmd in sorted(TemplateStateKeySetList, key=lambda cmd: cmd.content.state_key)
            ]
            return txt

        def get_set_path_iterator_keys(PathIteratorSetKeyList):
            def sort_key(Op):
                return (Op.content.path_walker_id, Op.content.path_id, Op.content.offset)
            txt = [
                str(cmd)
                for cmd in sorted(PathIteratorSetKeyList, key=sort_key)
            ]
            return txt

        result = []
        for transition_id, door in sorted(self.__db.iteritems(),key=lambda x: x[0].source_state_index):
            accept_command_list = []
            store_command_list  = []
            pcok_command_list   = []
            ssk_command_list    = []
            spi_command_list    = []
            for cmd in door.command_list:
                if   cmd.id == E_Op.Accepter:            accept_command_list.append(cmd)
                elif cmd.id == E_Op.PreContextOK:        pcok_command_list.append(cmd)
                elif cmd.id == E_Op.TemplateStateKeySet: ssk_command_list.append(cmd)
                elif cmd.id == E_Op.PathIteratorSet:     spi_command_list.append(cmd)
                elif cmd.id == E_Op.StoreInputPosition:  store_command_list.append(cmd)

            result.append("    .from %s:" % repr(transition_id.source_state_index).replace("L", ""))
            a_txt  = get_accepters(accept_command_list)
            s_txt  = get_storers(store_command_list)
            p_txt  = get_pre_context_oks(pcok_command_list)
            sk_txt = get_set_template_state_keys(ssk_command_list)
            pi_txt = get_set_path_iterator_keys(spi_command_list)
            content = "".join("%s\n" % x for x in a_txt + s_txt + p_txt + sk_txt + pi_txt)
            content = content.strip()
            if content.count("\n") == 0:
                # Append to same line
                content = " " + content
            else:
                # Indent properly
                content = "\n        " + content.replace("\n", "\n        ") 
            result.append("%s\n" % content)

        if len(result) == 0: return ""
        #return "-X--\n%s\n-X--" % "".join(result)
        return "".join(result)
Esempio n. 6
0
 def __init__(self, dial_db):
     self.__db                          = TypedDict(TransitionID, TransitionAction)
     self.__largest_used_door_sub_index = 0    # '0' is used for 'Door 0', i.e. reload entry
     self.__trigger_id_db               = {}   # (ToState, FromState) --> max. used trigger_id
     self.dial_db                     = dial_db
Esempio n. 7
0
class DialDB(object):
    __slots__ = ( "__d2la", "__door_id_db", "__gotoed_address_set", "__routed_address_set", "__address_i", "__incidence_id_i", "__map_incidence_id_to_state_index" )
    def __init__(self):
        self.clear()

    def __debug_address_generation(self, DoorId, Address, *SuspectAdrList):
        """Prints the callstack if an address of SuspectAdrList is generated.
        """
        if Address not in SuspectAdrList:
            return
        print "#DoorID %s <-> Address %s" % (DoorId, Address)
        print_callstack()

    def __debug_address_usage(self, Address, *SuspectAdrList):
        """Prints the callstack if an address of SuspectAdrList is generated.
        """
        if Address not in SuspectAdrList:
            return
        print "#Used %s" % Address
        print_callstack()

    def __debug_incidence_generation(self, IncidenceId, StateIndex):
        print "#Generated: %s -> state: %s" % (IncidenceId, StateIndex)
        print_callstack()

    def __debug_gotoed_address(self, Address, *SuspectAdrList):
        if Address not in SuspectAdrList:
            return
        print "#Gotoed Address: %s" % Address
        print_callstack()

    def clear(self):
        # Database: [DoorID] [Address] [Label] 
        # 
        # The database is represented by a dictionary that maps:
        #
        #               DoorID --> tuple(Address, Label)
        #
        self.__d2la = TypedDict(DoorID, AddressLabelPair)

        # Track all generated DoorID objects with 2d-dictionary that maps:
        #
        #          StateIndex --> ( DoorSubIndex --> DoorID )
        #
        # Where the DoorID has the 'state_index' and 'door_index' equal to
        # 'StateIndex' and 'DoorSubIndex'.
        #
        self.__door_id_db = {} # TypedDict(long, dict)
       
        # Track addresses which are subject to 'goto' and those which need to
        # be routed.
        self.__gotoed_address_set = TypedSet(long)
        self.__routed_address_set = TypedSet(long)

        # Address counter to generate unique addresses
        self.__address_i = long(-1)

        # Unique incidence id inside a mode
        self.__incidence_id_i = long(-1)

        # Mapping from incidence_id to terminal state index
        self.__map_incidence_id_to_state_index = {}

    def routed_address_set(self):
        return self.__routed_address_set

    def gotoed_address_set(self):
        return self.__gotoed_address_set

    def label_is_gotoed(self, Label):
        address = self.get_address_by_label(Label)
        return address in self.__gotoed_address_set

    def __new_entry(self, StateIndex=None, DoorSubIndex=None):
        """Create a new entry in the database. First, a DoorID is generated.
        Then a new set of address and label is linked to it. The link between
        DoorID and AddressLabelPair is stored in '.__d2la'. A list of existing
        DoorID-s is maintained in '.__door_id_db'.
        """
        def specify(StateIndex, DoorSubIndex):
            if StateIndex is None:   state_index = sm_index.get() # generate a new StateIndex
            else:                    state_index = StateIndex
            if DoorSubIndex is None: door_sub_index = self.max_door_sub_index(state_index) + 1
            else:                    door_sub_index = DoorSubIndex
            return state_index, door_sub_index

        state_index, door_sub_index = specify(StateIndex, DoorSubIndex)
        door_id                     = DoorID(state_index, door_sub_index, PlainF=True)
        address_label_pair          = self.register_door_id(door_id)

        return door_id, address_label_pair

    def max_door_sub_index(self, StateIndex):
        """RETURN: The greatest door sub index for a given StateIndex. 
                   '-1' if not index has been used yet.
        """
        result = - 1
        sub_db = self.__door_id_db.get(StateIndex)
        if sub_db is None: return result

        for dsi in (x for x in sub_db.iterkeys() if isinstance(x, (int, long))):
            if dsi > result: result = dsi
        return result

    def register_door_id(self, DoorId):
        self.__address_i += 1
        address_label_pair   = AddressLabelPair(self.__address_i, "_%i" % self.__address_i)

        if False: # True/False activates debug messages
            self.__debug_address_generation(DoorId, self.__address_i, 18)

        self.__d2la[DoorId] = address_label_pair

        sub_db = self.__door_id_db.get(DoorId.state_index)
        if sub_db is None:
            self.__door_id_db[DoorId.state_index] = { DoorId.door_index: DoorId }
        else:
            assert DoorId.door_index not in sub_db # Otherwise, it would not be new
            sub_db[DoorId.door_index] = DoorId

        return address_label_pair

    def access_door_id(self, StateIndex, DoorSubIndex):
        """Try to get a DoorID from the set of existing DoorID-s. If a DoorID
        with 'StateIndex' and 'DoorSubIndex' does not exist yet, then create it.
        """
        sub_db = self.__door_id_db.get(StateIndex)
        if sub_db is None:
            result = self.new_door_id(StateIndex, DoorSubIndex)
        else:
            door_id = sub_db.get(DoorSubIndex)
            if door_id is None:
                result = self.new_door_id(StateIndex, DoorSubIndex)
            else:
                result = door_id
        return result

    def new_incidence_id(self):
        self.__incidence_id_i += 1
        return self.__incidence_id_i

    def new_door_id(self, StateIndex=None, DoorSubIndex=None):
        door_id, alp = self.__new_entry(StateIndex, DoorSubIndex)
        return door_id

    def new_address(self, StateIndex=None):
        door_id, alp = self.__new_entry(StateIndex)
        return alp.address

    def new_label(self, StateIndex=None):
        door_id, alp = self.__new_entry(StateIndex)
        return alp.label

    def get_label_by_door_id(self, DoorId, GotoedF=False):
        assert DoorId in self.__d2la, "%s" % str(DoorId)
        address, label = self.__d2la[DoorId]
        if GotoedF:
            self.mark_address_as_gotoed(address)
        return label 

    def get_address_by_door_id(self, DoorId, RoutedF=False):
        address = self.__d2la[DoorId][0]
        if RoutedF:
            self.mark_address_as_routed(address)
        return address

    def get_door_id_by_address(self, Address):
        for door_id, info in self.__d2la.iteritems():
            if info[0] == Address: return door_id
        return None

    def get_label_by_address(self, Address, GotoedF=False):
        for address, label in self.__d2la.itervalues():
            if address == Address: 
                self.mark_address_as_gotoed(address)
                return label
        return None

    def get_door_id_by_label(self, Label):
        for door_id, info in self.__d2la.iteritems():
            if info[1] == Label: return door_id
        return None

    def get_address_by_label(self, Label):
        for door_id, info in self.__d2la.iteritems():
            if info[1] == Label: return info[0]
        return None

    def mark_address_as_gotoed(self, Address):
        if False:
            self.__debug_gotoed_address(Address, 39)
        self.__gotoed_address_set.add(Address)

    def mark_label_as_gotoed(self, Label):
        self.mark_address_as_gotoed(self.get_address_by_label(Label))

    def mark_door_id_as_gotoed(self, DoorId):
        self.mark_address_as_gotoed(self.get_address_by_door_id(DoorId))

    def mark_address_as_routed(self, Address):
        self.__routed_address_set.add(Address)
        # Any address which is subject to routing is 'gotoed', at least inside
        # the router (e.g. "switch( ... ) ... case AdrX: goto LabelX; ...").
        self.mark_address_as_gotoed(Address)

    def mark_label_as_routed(self, Label):
        self.mark_address_as_routed(self.get_address_by_label(Label))

    def mark_door_id_as_routed(self, DoorId):
        self.mark_address_as_routed(self.get_address_by_door_id(DoorId))

    def map_incidence_id_to_state_index(self, IncidenceId):
        assert isinstance(IncidenceId, (int, long)) or IncidenceId in E_IncidenceIDs, \
               "Found <%s>" % IncidenceId

        index = self.__map_incidence_id_to_state_index.get(IncidenceId)
        if index is None:
            index = sm_index.get()
            self.__map_incidence_id_to_state_index[IncidenceId] = index

        if False:
            self.__debug_incidence_generation(IncidenceId, index)
        return index
Esempio n. 8
0
 def __init__(self):
     self.__db                          = TypedDict(TransitionID, TransitionAction)
     self.__largest_used_door_sub_index = 0    # '0' is used for 'Door 0', i.e. reload entry
     self.__trigger_id_db               = {}   # (ToState, FromState) --> max. used trigger_id
Esempio n. 9
0
class Entry(object):
    """________________________________________________________________________

    An Entry object stores commands to be executed at entry into a state
    depending on a particular source state; and may be also depending on
    the particular trigger.
    
    BASICS _________________________________________________________________

    To keep track of entry actions, OpList-s need to 
    be associated with a TransitionID-s, i.e. pairs of (state_index,
    from_state_index). This happens in the member '.__db', i.e.
    
       .action_db:    TransitionID --> TransitionAction

    where a TransitionID consists of: .from_state_index
                                      .state_index
                                      .trigger_id 
 
    and a TransitionAction consists of: .door_id
                                        .command_list
                                        
    where '.door_id'  identifies a specific door  of the  entry into  the state.
    It is distinctly associated with a list of commands '.command_list'. The 
    commands of '.command_list' are executed if the state is entered by
    a transition given with the key's TransitionID. A call to 

                            action_db.categorize()

    ensures that
                                  1     1
                        DoorID  <---------> OpList
   
    In words: 
    
       -- each TransitionAction *has a* valid door_id. That is, every list of
          commands is identified with a door_id.

       -- The door_id *distinctly* determines the command list (in the entry). 
          That is, door_id-s of TransitionAction-s differ if and only if their
          command lists are different.
                  
    Later on in the code generation, a 'door tree' is generated to produce
    optimized code which profits from common commands in command lists. But,
    for now, it is important to remember:

              .---------------------------------------------------.
              |  A DoorID distinctly identifies a OpList to  |
              |       be executed the at entry of a state.        |
              '---------------------------------------------------'
    """

    __slots__ = ("__db", "__largest_used_door_sub_index", "__trigger_id_db")

    def __init__(self):
        self.__db                          = TypedDict(TransitionID, TransitionAction)
        self.__largest_used_door_sub_index = 0    # '0' is used for 'Door 0', i.e. reload entry
        self.__trigger_id_db               = {}   # (ToState, FromState) --> max. used trigger_id

    def get(self, TheTransitionID):
        return self.__db.get(TheTransitionID)

    def get_action(self, StateIndex, FromStateIndex, TriggerId=0):
        return self.__db.get(TransitionID(StateIndex, FromStateIndex, TriggerId))

    def get_command_list(self, StateIndex, FromStateIndex, TriggerId=0):
        action = self.__db.get(TransitionID(StateIndex, FromStateIndex, TriggerId))
        if action is None: return None
        else:              return action.command_list

    def get_command_list_by_door_id(self, DoorId):
        for action in self.__db.itervalues():
            if action.door_id == DoorId:
                return action.command_list
        return None

    def get_door_id(self, StateIndex, FromStateIndex, TriggerId=0):
        """RETURN: DoorID of the door which implements the transition 
                          (FromStateIndex, StateIndex).
                   None,  if the transition is not implemented in this
                          state.
        """
        action = self.__db.get(TransitionID(StateIndex, FromStateIndex, TriggerId))
        if action is None: return None
        else:              return action.door_id

    def get_transition_id_list(self, DoorId):
        return [ 
           transition_id for transition_id, action in self.__db.iteritems()
                         if action.door_id == DoorId 
        ]

    def get_door_id_by_command_list(self, TheOpList):
        """Finds the DoorID of the door that implements TheOpList.
           RETURNS: None if no such door exists that implements TheOpList.
        """
        for action in self.__db.itervalues():
            if action.command_list == TheOpList:
                return action.door_id
        return None

    def absorb(self, Other):
        """Absorbs all, but the 'reload transitions'.
        """
        for tid, action in Other.__db.iteritems():
            self.enter(tid.target_state_index, tid.source_state_index, action)

        if self.__largest_used_door_sub_index < Other.__largest_used_door_sub_index:
            self.__largest_used_door_sub_index = Other.__largest_used_door_sub_index

    @typed(Cl=OpList)
    def enter_OpList(self, ToStateIndex, FromStateIndex, Cl):
        return self.enter(ToStateIndex, FromStateIndex, TransitionAction(Cl))

    @typed(TheAction=TransitionAction)
    def enter(self, ToStateIndex, FromStateIndex, TheAction):
        assert isinstance(TheAction, TransitionAction)
        #!! It is ABSOLUTELY essential, that the OpList-s related to actions are
        #!! independent! Each transition must have its OWN OpList!
        for transition_id, action in self.__db.iteritems():
            assert id(TheAction.command_list) != id(action.command_list) 

        transition_id = TransitionID(ToStateIndex, FromStateIndex, 
                                     TriggerId=self.__get_trigger_id(ToStateIndex, FromStateIndex))
        self.__db[transition_id] = TheAction
        return transition_id

    @typed(ta=TransitionAction)
    def enter_state_machine_entry(self, SM_id, ToStateIndex, ta):
        ta.door_id = DoorID.state_machine_entry(SM_id)
        return self.enter(ToStateIndex, E_StateIndices.BEFORE_ENTRY, ta)

    def enter_before(self, ToStateIndex, FromStateIndex, TheOpList):
        transition_id = TransitionID(ToStateIndex, FromStateIndex, TriggerId=0)
        ta = self.__db.get(transition_id)
        # A transition_action cannot be changed, once it has a DoorID assigned to it.
        assert ta.door_id is None
        ta.command_list = TheOpList.concatinate(ta.command_list)

    def __get_trigger_id(self, ToStateIndex, FromStateIndex):
        ft = (ToStateIndex, FromStateIndex)
        tmp = self.__trigger_id_db.get(ft)
        # "FromStateIndex == E_StateIndices.BEFORE_ENTRY" indicates the entry into the 
        # state machine. There cannot be more than one entry into the state 
        # machine. Thus, it cannot appear twice.
        assert FromStateIndex != E_StateIndices.BEFORE_ENTRY or tmp is None
        if tmp is None: self.__trigger_id_db[ft]  = 0; tmp = 0;
        else:           self.__trigger_id_db[ft] += 1
        return tmp

    def remove_transition_from_states(self, StateIndexSet):
        assert isinstance(StateIndexSet, set)
        for transition_id in self.__db.keys():
            if transition_id.source_state_index in StateIndexSet:
                del self.__db[transition_id]

    def size(self):
        return len(self.__db)

    def add_Accepter_on_all(self, PreContextID, AcceptanceID):
        """Add an acceptance at the top of each accepter at every door. If there
           is no accepter in a door it is created.
        """
        for ta in self.__db.itervalues():
            # Catch the accepter, if there is already one, if not create one.
            ta.command_list.access_accepter().add(PreContextID, AcceptanceID)

    def add_StoreInputPosition(self, StateIndex, FromStateIndex, PreContextID, PositionRegister, Offset):
        """Add 'store input position' to specific door. See 'SeStoreInputPosition'
           comment for the reason why we do not store pre-context-id.
        """
        command_list = self.__db[TransitionID(StateIndex, FromStateIndex, 0)].command_list
        cmd          = Op.StoreInputPosition(PreContextID, PositionRegister, Offset)
        # Make sure it is the first!
        command_list.insert(0, cmd)
        # Never store twice in the same position register! 
        # => Make sure, that there is no second of the same kind!
        i = len(command_list) - 1
        while i >= 1: # leave 'i=0' which has just been inserted!
            if command_list[i] == cmd:
                del command_list[i]
            i -= 1

    def has_transition(self, ToStateIndex, FromStateIndex):
        for key in self.__db.iterkeys():
            if key.target_state_index == ToStateIndex and key.source_state_index == FromStateIndex:
                return True
        return False

    def has_command(self, OpId):
        assert OpId in E_Op
        for action in self.__db.itervalues():
            if action.command_list.has_command_id(OpId): 
                return True
        return False

    def has_transitions_to_door_id(self, DoorId):
        for action in self.__db.itervalues():
            if action.door_id == DoorId:
                return True
        return False

    def door_id_set(self):
        """RETURNS: The door ids of this entry.

        In the frame of a CommandTree, this set is the set of 'leaf door ids'.
        """
        return set(action.door_id 
                   for action in self.__db.itervalues()
                   if action.door_id is not None)

    def get_state_machine_entry_door_id(self):
        """RETURNS: DoorID, if the entry contains THE entry into the analyzer.
                    None, if not.
        """
        for transition_id, action in self.__db.iteritems():
            if transition_id.source_state_index != E_StateIndices.BEFORE_ENTRY: 
                continue
            assert action.door_id.door_index == E_DoorIdIndex.STATE_MACHINE_ENTRY
            return action.door_id
        return None

    def delete(self, StateIndex, FromStateIndex, TriggerId=0):
        del self.__db[TransitionID(StateIndex, FromStateIndex, 0)]

    def replace_position_registers(self, PositionRegisterMap):
        """Originally, each pattern gets its own position register if it is
        required to store/restore the input position. The 'PositionRegisterMap'
        is a result of an analysis which tells whether some registers may
        actually be shared. This function does the replacement of positioning
        registers based on what is given in 'PositionRegisterMap'.
        """
        for action in self.__db.itervalues():
            action.command_list.replace_position_registers(PositionRegisterMap)

        return

    def delete_superfluous_commands(self):
        for action in self.__db.itervalues():
            action.command_list.delete_superfluous_commands()
        return

    def itervalues(self):
        for ta in self.__db.itervalues():
            assert ta.door_id is not None, ".categorize() needs to be called before this!"
            yield ta

    def iteritems(self):
        return self.__db.iteritems()

    def __setitem__(self, Key, Value):
        #!! It is ABSOLUTELY essential, that the OpList-s related to actions are
        #!! independent! Each transition must have its OWN OpList!
        for action in self.__db.itervalues():
            assert id(Value.command_list) != id(action.command_list)

        self.__db[Key] = Value
        return Value

    def categorize(self, StateIndex):
        """
        This function considers TransitionActions where '.door_id is None' and
        assigns them a DoorID.  A DoorID identifies (globally) the OpList
        and the state which they enter. In other words, if two transition actions
        of a state have the same command lists, they have the same DoorID. 

        RETURNS: List of newly assigned pairs of (TransitionID, DoorID)s.
        """
        #!! It is ABSOLUTELY essential, that the OpList-s related to actions are
        #!! independent! Each transition must have its OWN OpList!
        cmd_list_ids = [ id(action.command_list) for action in self.__db.itervalues() ]
        assert len(cmd_list_ids) == len(set(cmd_list_ids)) # size(list) == size(unique set)

        work_list = [
            (transition_id, action) for transition_id, action in self.__db.iteritems()
                                    if action.door_id is None
        ]

        if len(work_list) == 0:
            return

        command_list_db = dict(
            (action.command_list, action.door_id) for action in self.__db.itervalues()
                                                  if action.door_id is not None
        )

        def _get_door_id(CL):
            # If there was an action with the same command list, then assign
            # the same door id. Leave the action intact! May be, it is modified
            # later and will differ from the currently same action.
            new_door_id = command_list_db.get(CL)

            if new_door_id is not None: return new_door_id

            return dial_db.new_door_id(StateIndex)

        def sort_key(X):
            return (X[0].target_state_index, X[0].source_state_index, X[0].trigger_id)

        for transition_id, action in sorted(work_list, key=sort_key): # NOT: 'iteritems()'
            action.door_id                       = _get_door_id(action.command_list)
            command_list_db[action.command_list] = action.door_id

        assert self.check_consistency()

    @property 
    def largest_used_door_sub_index(self):
        return self.__largest_used_door_sub_index

    def check_consistency(self):
        """Any two entries with the same DoorID must have the same command list
           associated with it.
        """
        check_db = {}
        for action in self.__db.itervalues():
            if action.door_id is None: 
                continue
            cmp_command_list = check_db.get(action.door_id)
            if cmp_command_list is None: 
                check_db[action.door_id] = action.command_list
            elif cmp_command_list != action.command_list:
                return False

        # Some commands shall never occur more than once in a command list:
        # --> Op.unique_set
        for transition_action in self.__db.itervalues():
            unique_found_set = set()
            for cmd in transition_action.command_list:
                if   cmd.id not in Op.unique_set: continue
                elif cmd.id in unique_found_set:       return False
                unique_found_set.add(cmd.id)
        return True

    def get_string(self):
        txt = []
        for tid, ta in self.__db.iteritems():
            txt.append("%s:%s: {\n" % (tid, ta.door_id))
            for command in ta.command_list:
                txt.append("    " + repr(command))
            txt.append("}\n")
        return "".join(txt)

    @property
    def action_db(self):
        return self.__db

    def __hash__(self):
        xor_sum = 0
        for door in self.__db.itervalues():
            xor_sum ^= hash(door.command_list)
        return xor_sum

    def __eq__(self, Other):
        assert False, "not used"

    def is_equal(self, Other):
        assert False, "not used"

    def __repr__(self):
        def get_accepters(AccepterList):
            if len(AccepterList) == 0: return []
            assert len(AccepterList) == 1
            return [ str(AccepterList[0]) ]

        def get_storers(StorerList):
            txt = [ 
                str(cmd)
                for cmd in sorted(StorerList, key=lambda cmd: (cmd.content.pre_context_id, cmd.content.position_register))
            ]
            return txt

        def get_pre_context_oks(PCOKList):
            txt = [
                str(cmd)
                for cmd in sorted(PCOKList, key=lambda cmd: cmd.content.pre_context_id)
            ]
            return txt

        def get_set_template_state_keys(TemplateStateKeySetList):
            txt = [
                str(cmd)
                for cmd in sorted(TemplateStateKeySetList, key=lambda cmd: cmd.content.state_key)
            ]
            return txt

        def get_set_path_iterator_keys(PathIteratorSetKeyList):
            def sort_key(Op):
                return (Op.content.path_walker_id, Op.content.path_id, Op.content.offset)
            txt = [
                str(cmd)
                for cmd in sorted(PathIteratorSetKeyList, key=sort_key)
            ]
            return txt

        result = []
        for transition_id, door in sorted(self.__db.iteritems(),key=lambda x: x[0].source_state_index):
            accept_command_list = []
            store_command_list  = []
            pcok_command_list   = []
            ssk_command_list    = []
            spi_command_list    = []
            for cmd in door.command_list:
                if   cmd.id == E_Op.Accepter:            accept_command_list.append(cmd)
                elif cmd.id == E_Op.PreContextOK:        pcok_command_list.append(cmd)
                elif cmd.id == E_Op.TemplateStateKeySet: ssk_command_list.append(cmd)
                elif cmd.id == E_Op.PathIteratorSet:     spi_command_list.append(cmd)
                elif cmd.id == E_Op.StoreInputPosition:  store_command_list.append(cmd)

            result.append("    .from %s:" % repr(transition_id.source_state_index).replace("L", ""))
            a_txt  = get_accepters(accept_command_list)
            s_txt  = get_storers(store_command_list)
            p_txt  = get_pre_context_oks(pcok_command_list)
            sk_txt = get_set_template_state_keys(ssk_command_list)
            pi_txt = get_set_path_iterator_keys(spi_command_list)
            content = "".join("%s\n" % x for x in a_txt + s_txt + p_txt + sk_txt + pi_txt)
            content = content.strip()
            if content.count("\n") == 0:
                # Append to same line
                content = " " + content
            else:
                # Indent properly
                content = "\n        " + content.replace("\n", "\n        ") 
            result.append("%s\n" % content)

        if len(result) == 0: return ""
        #return "-X--\n%s\n-X--" % "".join(result)
        return "".join(result)