コード例 #1
0
ファイル: state.py プロジェクト: smmckay/quex3
    def __init__(self, PWState, TheAnalyzer):
        self.uniform_door_id          = UniformObject()
        self.uniform_terminal_door_id = UniformObject()
        self.door_id_sequence_list    = []
        for step_list in PWState.path_list:
            # Meaningful paths consist of more than one state and a terminal. 
            assert len(step_list) > 2

            door_id_sequence = self.__determine_door_id_sequence(step_list, TheAnalyzer, PWState)

            self.door_id_sequence_list.append(door_id_sequence)

        return
コード例 #2
0
ファイル: acceptance.py プロジェクト: mplucinski/quex
    def _interfere_acceptance(cls, snapshot_set_db, EntryRecipeDb, StateIndex):
        """If the acceptance scheme differs for only two recipes, then the 
        acceptance must be determined upon entry and stored in the LastAcceptance
        register.


        RETURN: [0] Accumulated 'accepter'
        """ 
        # Interference requires determined entry recipes.
        assert none_is_None(EntryRecipeDb.itervalues())

        # Irrelevant E_R.AcceptanceRegister? Impossible! Acceptance is relevant
        # for all states! '.accepter is None' is not treated.
        accepter = UniformObject.from_iterable(
                               recipe.accepter
                               for recipe in EntryRecipeDb.itervalues()).plain_content()

        if accepter != E_Values.VOID:
            # Homogeneity
            pass
        else:
            # Inhomogeneity
            accepter = [ cls.RestoreAcceptance ]
            snapshot_set_db[E_R.AcceptanceRegister] = set([StateIndex])

        assert accepter and accepter[-1].pre_context_id() == E_PreContextIDs.NONE
        return accepter
コード例 #3
0
ファイル: acceptance.py プロジェクト: xxyzzzq/quex
    def _interfere_acceptance(cls, snapshot_set_db, EntryRecipeDb, StateIndex):
        """If the acceptance scheme differs for only two recipes, then the 
        acceptance must be determined upon entry and stored in the LastAcceptance
        register.


        RETURN: [0] Accumulated 'accepter'
        """ 
        # Interference requires determined entry recipes.
        assert none_is_None(EntryRecipeDb.itervalues())

        # Irrelevant E_R.AcceptanceRegister? Impossible! Acceptance is relevant
        # for all states! '.accepter is None' is not treated.
        accepter = UniformObject.from_iterable(
                               recipe.accepter
                               for recipe in EntryRecipeDb.itervalues()).plain_content()

        if accepter != E_Values.VOID:
            # Homogeneity
            pass
        else:
            # Inhomogeneity
            accepter = [ cls.RestoreAcceptance ]
            snapshot_set_db[E_R.AcceptanceRegister] = set([StateIndex])

        assert accepter and accepter[-1].pre_context_id() == E_PreContextIDs.NONE
        return accepter
コード例 #4
0
ファイル: path.py プロジェクト: smmckay/quex3
    def __init__(self, StartState, TheTransitionMap, TransitionCharacter):
        if StartState is None: return # Only for Clone

        assert isinstance(StartState,          FSM_State)
        assert isinstance(TransitionCharacter, (int, long)), "Found '%s'" % TransitionCharacter
        assert isinstance(TheTransitionMap,    TransitionMap)

        # uniform_entry_OpList: 
        #    The entry into the StartState happens from outside.
        #    => A 'state_key' is assigned (the path_iterator)
        #       and it is not part of the iteration loop.
        #    => The StartState's entry IS NOT subject to uniformity considerations.
        self.uniform_entry_OpList = UniformObject()

        self.__step_list = [ CharacterPathStep(StartState.index, TransitionCharacter) ]

        self.transition_map_data = TransitionMapData(TheTransitionMap, TransitionCharacter)
コード例 #5
0
ファイル: target.py プロジェクト: mplucinski/quex
    def from_scheme(SchemeAsList):
        result = TargetByStateKey()
        result.__scheme          = tuple(SchemeAsList)
        result.__scheme_id       = None
        result.__uniform_door_id = UniformObject.from_iterable(SchemeAsList).content

        assert isinstance(result.__uniform_door_id, (types.NoneType, DoorID))
        return result
コード例 #6
0
    def from_scheme(SchemeAsList):
        result = TargetByStateKey()
        result.__scheme = tuple(SchemeAsList)
        result.__scheme_id = None
        result.__uniform_door_id = UniformObject.from_iterable(
            SchemeAsList).content

        assert isinstance(result.__uniform_door_id, (types.NoneType, DoorID))
        return result
コード例 #7
0
ファイル: state.py プロジェクト: nyulacska/gpr
    def __init__(self, Represented_AnalyzerState, DropOutCatcher):
        assert not isinstance(Represented_AnalyzerState, MegaState)
        state_index = Represented_AnalyzerState.index
        transition_map = Represented_AnalyzerState.transition_map

        adapted_transition_map = transition_map.relate_to_TargetByStateKeys(
            state_index, DropOutCatcher)
        ski_db = StateKeyIndexDB([state_index])
        MegaState.__init__(self, state_index, adapted_transition_map, ski_db)

        # Uniform Entry: In contrast to path compression, here we consider
        #                all entries into the MegaState.
        self.uniform_entry_OpList = UniformObject()
        for action in Represented_AnalyzerState.entry.itervalues():
            self.uniform_entry_OpList <<= action.command_list
            if self.uniform_entry_OpList.is_uniform() == False:
                break  # No more need to investigate

        self.entry.absorb(Represented_AnalyzerState.entry)
コード例 #8
0
ファイル: state.py プロジェクト: smmckay/quex3
class PseudoTemplateState(MegaState): 
    """________________________________________________________________________
    
    Represents an FSM_State in a way to that it acts homogeneously with
    other MegaState-s. That is, the transition_map is adapted so that it maps
    from a character interval to a TargetByStateKey.

              transition_map:  interval --> TargetByStateKey

    instead of mapping to a target state index.
    ___________________________________________________________________________
    """
    @typed(DropOutCatcher=Processor)
    def __init__(self, Represented_AnalyzerState, DropOutCatcher):
        assert not isinstance(Represented_AnalyzerState, MegaState)
        state_index            = Represented_AnalyzerState.index
        transition_map         = Represented_AnalyzerState.transition_map

        adapted_transition_map = transition_map.relate_to_TargetByStateKeys(state_index, 
                                                                            DropOutCatcher)
        ski_db                 = StateKeyIndexDB([state_index])
        MegaState.__init__(self, state_index, adapted_transition_map, ski_db, 
                           dial_db=Represented_AnalyzerState.entry.dial_db)

        # Uniform Entry: In contrast to path compression, here we consider 
        #                all entries into the MegaState. 
        self.uniform_entry_OpList = UniformObject()
        for action in Represented_AnalyzerState.entry.itervalues():
            self.uniform_entry_OpList <<= action.command_list
            if self.uniform_entry_OpList.is_uniform() == False:
                break # No more need to investigate

        self.entry.absorb(Represented_AnalyzerState.entry)

    @property
    def target_scheme_n(self):  
        return 0

    def _finalize_transition_map(self, TheAnalyzer):
        pass # Nothing to be done

    def _finalize_entry_OpLists(self): 
        pass

    def _finalize_content(self):            
        pass
コード例 #9
0
ファイル: path.py プロジェクト: mplucinski/quex
    def __init__(self, StartState, TheTransitionMap, TransitionCharacter):
        if StartState is None: return # Only for Clone

        assert isinstance(StartState,          AnalyzerState)
        assert isinstance(TransitionCharacter, (int, long)), "Found '%s'" % TransitionCharacter
        assert isinstance(TheTransitionMap,    TransitionMap)

        # uniform_entry_OpList: 
        #    The entry into the StartState happens from outside.
        #    => A 'state_key' is assigned (the path_iterator)
        #       and it is not part of the iteration loop.
        #    => The StartState's entry IS NOT subject to uniformity considerations.
        self.uniform_entry_OpList = UniformObject()

        self.__step_list = [ CharacterPathStep(StartState.index, TransitionCharacter) ]

        self.transition_map_data = TransitionMapData(TheTransitionMap, TransitionCharacter)
コード例 #10
0
ファイル: state.py プロジェクト: mplucinski/quex
class PseudoTemplateState(MegaState): 
    """________________________________________________________________________
    
    Represents an AnalyzerState in a way to that it acts homogeneously with
    other MegaState-s. That is, the transition_map is adapted so that it maps
    from a character interval to a TargetByStateKey.

              transition_map:  interval --> TargetByStateKey

    instead of mapping to a target state index.
    ___________________________________________________________________________
    """
    @typed(DropOutCatcher=Processor)
    def __init__(self, Represented_AnalyzerState, DropOutCatcher):
        assert not isinstance(Represented_AnalyzerState, MegaState)
        state_index            = Represented_AnalyzerState.index
        transition_map         = Represented_AnalyzerState.transition_map

        adapted_transition_map = transition_map.relate_to_TargetByStateKeys(state_index, 
                                                                            DropOutCatcher)
        ski_db                 = StateKeyIndexDB([state_index])
        MegaState.__init__(self, state_index, adapted_transition_map, ski_db)

        # Uniform Entry: In contrast to path compression, here we consider 
        #                all entries into the MegaState. 
        self.uniform_entry_OpList = UniformObject()
        for action in Represented_AnalyzerState.entry.itervalues():
            self.uniform_entry_OpList <<= action.command_list
            if self.uniform_entry_OpList.is_uniform() == False:
                break # No more need to investigate

        self.entry.absorb(Represented_AnalyzerState.entry)

    @property
    def target_scheme_n(self):  
        return 0

    def _finalize_transition_map(self, TheAnalyzer):
        pass # Nothing to be done

    def _finalize_entry_OpLists(self): 
        pass

    def _finalize_content(self):            
        pass
コード例 #11
0
ファイル: state.py プロジェクト: mplucinski/quex
    def __init__(self, Candidate):
        StateA = Candidate.state_a
        StateB = Candidate.state_b

        # Combined DropOut and Entry schemes are generated by the same function
        transition_map, target_scheme_n = combine_maps(StateA.transition_map, StateB.transition_map)

        ski_db = StateKeyIndexDB(StateA.state_index_sequence() + StateB.state_index_sequence())
        MegaState.__init__(self, index.get(), transition_map, ski_db)

        self.uniform_entry_OpList = UniformObject.from_iterable((
                                                       StateA.uniform_entry_OpList,
                                                       StateB.uniform_entry_OpList))

        self.__target_scheme_n = target_scheme_n
        self.__engine_type     = None # StateA.engine_type

        MegaState.bad_company_set(self, StateA.bad_company().union(StateB.bad_company()))
コード例 #12
0
ファイル: state.py プロジェクト: smmckay/quex3
    def __init__(self, Candidate):
        StateA = Candidate.state_a
        StateB = Candidate.state_b

        # Combined DropOut and Entry schemes are generated by the same function
        transition_map, target_scheme_n = combine_maps(StateA.transition_map, StateB.transition_map)

        ski_db = StateKeyIndexDB(StateA.state_index_sequence() + StateB.state_index_sequence())
        MegaState.__init__(self, index.get(), transition_map, ski_db, 
                           dial_db=StateA.entry.dial_db)

        self.uniform_entry_OpList = UniformObject.from_iterable((
                                                       StateA.uniform_entry_OpList,
                                                       StateB.uniform_entry_OpList))

        self.__target_scheme_n = target_scheme_n
        self.__engine_type     = None # StateA.engine_type

        MegaState.bad_company_set(self, StateA.bad_company().union(StateB.bad_company()))
コード例 #13
0
ファイル: state.py プロジェクト: mplucinski/quex
    def __init__(self, Represented_AnalyzerState, DropOutCatcher):
        assert not isinstance(Represented_AnalyzerState, MegaState)
        state_index            = Represented_AnalyzerState.index
        transition_map         = Represented_AnalyzerState.transition_map

        adapted_transition_map = transition_map.relate_to_TargetByStateKeys(state_index, 
                                                                            DropOutCatcher)
        ski_db                 = StateKeyIndexDB([state_index])
        MegaState.__init__(self, state_index, adapted_transition_map, ski_db)

        # Uniform Entry: In contrast to path compression, here we consider 
        #                all entries into the MegaState. 
        self.uniform_entry_OpList = UniformObject()
        for action in Represented_AnalyzerState.entry.itervalues():
            self.uniform_entry_OpList <<= action.command_list
            if self.uniform_entry_OpList.is_uniform() == False:
                break # No more need to investigate

        self.entry.absorb(Represented_AnalyzerState.entry)
コード例 #14
0
ファイル: acceptance.py プロジェクト: mplucinski/quex
    def _interfere_input_position_storage(cls, snapshot_set_db, EntryRecipeDb, 
                                          RequiredVariableSet, StateIndex):
        """Each position register is considered separately. If for one register 
        the offset differs, then it can only be determined from storing it in 
        this mouth state and restoring it later.
        """
        assert none_is_None(
                  flatten_it_list_of_lists(recipe.ip_offset_db.itervalues()
                                           for recipe in EntryRecipeDb.itervalues()))

        ip_offset_db = {}
        for variable_id in RequiredVariableSet:
            if type(variable_id) != tuple: continue
            register_id = variable_id[1]

            # Irrelevant position register? Possible! A recipe that considers 
            # a position register irrelevant is 'equal' to any other. Thus, it
            # can be filtered out. Irrelevant position register is not mentioned
            # in 'ip_offset_db' => 'offset = None'.
            offset_list = [
                  recipe.ip_offset_db.get(register_id)
                  for recipe in EntryRecipeDb.itervalues()
            ]
            offset = UniformObject.from_iterable(
                      x for x in offset_list if x is not None).plain_content()

            if offset != E_Values.VOID: 
                # Homogeneity
                pass
            else:
                # Inhomogeneity
                offset = E_Values.RESTORE
                snapshot_set_db[variable_id] = set([StateIndex])

            ip_offset_db[register_id] = offset

        assert none_is_None(ip_offset_db.itervalues())
        return ip_offset_db
コード例 #15
0
ファイル: acceptance.py プロジェクト: xxyzzzq/quex
    def _interfere_read_position_storage(cls, snapshot_set_db, EntryRecipeDb, 
                                          RequiredVariableSet, StateIndex):
        """Each position register is considered separately. If for one register 
        the offset differs, then it can only be determined from storing it in 
        this mouth state and restoring it later.
        """
        assert none_is_None(
                  flatten_it_list_of_lists(recipe.ip_offset_db.itervalues()
                                           for recipe in EntryRecipeDb.itervalues()))

        ip_offset_db = {}
        for variable_id in RequiredVariableSet:
            if type(variable_id) != tuple: continue
            register_id = variable_id[1]

            # Irrelevant position register? Possible! A recipe that considers 
            # a position register irrelevant is 'equal' to any other. Thus, it
            # can be filtered out. Irrelevant position register is not mentioned
            # in 'ip_offset_db' => 'offset = None'.
            offset_list = [
                  recipe.ip_offset_db.get(register_id)
                  for recipe in EntryRecipeDb.itervalues()
            ]
            offset = UniformObject.from_iterable(
                      x for x in offset_list if x is not None).plain_content()

            if offset != E_Values.VOID: 
                # Homogeneity
                pass
            else:
                # Inhomogeneity
                offset = E_Values.RESTORE
                snapshot_set_db[variable_id] = set([StateIndex])

            ip_offset_db[register_id] = offset

        assert none_is_None(ip_offset_db.itervalues())
        return ip_offset_db
コード例 #16
0
    def from_2_TargetByStateKeys(A, B, scheme_pair_db):
        """Generates a TargetByStateKey as a combination of two TargetByStateKey-s
        'A' and 'B'. The generation may rely on a 'scheme_pair_db' which:
            (1) Helps to avoid double generation of TargetByStateKey-s for the
                same combination.
            (2) Lets determine the number of different schemes in the 
                transition map, later.
        """
        result = TargetByStateKey()
        if scheme_pair_db is not None:
            key = (A.__scheme, B.__scheme)
            scheme = scheme_pair_db.get(key)
            if scheme is None:
                scheme = A.__scheme + B.__scheme
                scheme_pair_db[key] = scheme
        else:
            scheme = A.__scheme + B.__scheme

        result.__scheme = scheme
        result.__scheme_id = None  # This is done later
        result.__uniform_door_id = UniformObject.from_2(
            A.__uniform_door_id, B.__uniform_door_id).content
        assert isinstance(result.__uniform_door_id, (types.NoneType, DoorID))
        return result
コード例 #17
0
ファイル: target.py プロジェクト: mplucinski/quex
    def from_2_TargetByStateKeys(A, B, scheme_pair_db):
        """Generates a TargetByStateKey as a combination of two TargetByStateKey-s
        'A' and 'B'. The generation may rely on a 'scheme_pair_db' which:
            (1) Helps to avoid double generation of TargetByStateKey-s for the
                same combination.
            (2) Lets determine the number of different schemes in the 
                transition map, later.
        """
        result = TargetByStateKey()
        if scheme_pair_db is not None:
            key    = (A.__scheme, B.__scheme)
            scheme = scheme_pair_db.get(key)
            if scheme is None:
                scheme = A.__scheme + B.__scheme
                scheme_pair_db[key] = scheme
        else:
            scheme = A.__scheme + B.__scheme

        result.__scheme          = scheme
        result.__scheme_id       = None # This is done later
        result.__uniform_door_id = UniformObject.from_2(A.__uniform_door_id, 
                                                        B.__uniform_door_id).content
        assert isinstance(result.__uniform_door_id, (types.NoneType, DoorID))
        return result
コード例 #18
0
ファイル: path.py プロジェクト: mplucinski/quex
class CharacterPath(object):
    """________________________________________________________________________

    Represents identified path in the state machine such as:


       ( 1 )--- 'a' -->( 2 )--- 'b' -->( 3 )--- 'c' -->( 4 )--- 'd' -->( 5 )

    where the remaining transitions in each state match (except for the last).

    During analysis, the CharacterPath acts as a container which also stores
    information about the transition_map. The TransitionMapData object
    maintains information about possible transition_map matches with other
    states.

    ___________________________________________________________________________
    MEMBERS:
  
    .step_list: (list of CharacterPathStep-s)

       .------------------. .------------------.       .--------------------.
       |.state_index = 1  | |.state_index = 2  |       |.state_index = 4711 |
       |.trigger     = 'a'| |.trigger     = 'b'| . . . |.trigger     = None |
       '------------------' '------------------'       '--------------------'

       The last element of the '__step_list' contains the terminal. The 
       terminal state is not implemented by the path walker. It is the first
       state entered after the path walker has finished. Its role is further
       indicated by a trigger of 'None'.
   
    .uniform_entry_OpList:

      This object  keeps track about the actions to  be executed upon entry
      into a state on the path and upon drop-out of a state on on the path. If 
      any of them is the same for all paths on the state it contains an object
      which is not 'None'. In case it is not uniform 'None' is contained.

    .transition_map_data

     maintains data about the common transition map of all states. 
    ___________________________________________________________________________
    """
    __slots__ = ("__step_list",       
                 "transition_map_data", 
                 "uniform_entry_OpList") 

    def __init__(self, StartState, TheTransitionMap, TransitionCharacter):
        if StartState is None: return # Only for Clone

        assert isinstance(StartState,          AnalyzerState)
        assert isinstance(TransitionCharacter, (int, long)), "Found '%s'" % TransitionCharacter
        assert isinstance(TheTransitionMap,    TransitionMap)

        # uniform_entry_OpList: 
        #    The entry into the StartState happens from outside.
        #    => A 'state_key' is assigned (the path_iterator)
        #       and it is not part of the iteration loop.
        #    => The StartState's entry IS NOT subject to uniformity considerations.
        self.uniform_entry_OpList = UniformObject()

        self.__step_list = [ CharacterPathStep(StartState.index, TransitionCharacter) ]

        self.transition_map_data = TransitionMapData(TheTransitionMap, TransitionCharacter)

    def clone(self):
        result = CharacterPath(None, None, None)

        result.uniform_entry_OpList       = self.uniform_entry_OpList.clone()
        result.__step_list                     = [ x for x in self.__step_list ] # CharacterPathStep are immutable
        result.transition_map_data             = self.transition_map_data.clone()
        return result

    def extended_clone(self, PreviousTerminal, TransitionCharacter, TransitionMapWildCardPlug):
        """Append 'State' to path:

        -- add 'State' to the sequence of states on the path.

        -- absorb the 'State's' drop-out and entry actions into the path's 
           drop-out and entry actions.
        
        If 'TransitionCharacter' is None, then the state is considered a
        terminal state. Terminal states are not themselves implemented inside a
        PathWalkerState. Thus, their entry and drop out information does not
        have to be absorbed.
        """
        assert    TransitionCharacter is not None
        assert    isinstance(TransitionMapWildCardPlug, DoorID) \
               or TransitionMapWildCardPlug == -1

        result = self.clone()

        # OpList upon Entry to State
        # (TriggerIndex == 0, because there can only be one transition from
        #                     one state to the next on the path).
        prev_step         = self.__step_list[-1]
        entry_OpList = PreviousTerminal.entry.get_command_list(PreviousTerminal.index, 
                                                                              prev_step.state_index,
                                                                              TriggerId=0)
        assert entry_OpList is not None

        result.uniform_entry_OpList <<= entry_OpList

        result.__step_list.append(CharacterPathStep(PreviousTerminal.index, TransitionCharacter))

        result.transition_map_data.plug_this(TransitionMapWildCardPlug)

        return result

    def uniformity_with_predecessor(self, State):
        """Check whether the entry of the last state on the path executes the
           same OpList as the entry to 'State'. 
        """
        # This function is supposed to be called only when uniformity is required.
        # If so, then the path must be in a uniform state at any time. 
        assert self.uniform_entry_OpList.is_uniform()

        # Check on 'Entry' for what is done along the path.
        #
        # OpList upon Entry to State
        # (TriggerIndex == 0, because there can only be one transition from
        #                     one state to the next on the path).
        prev_step    = self.__step_list[-1]
        command_list = State.entry.get_command_list(State.index, prev_step.state_index, 
                                                              TriggerId=0)
        assert command_list is not None

        if not self.uniform_entry_OpList.fit(command_list): 
            return False

        return True

    def has_wildcard(self):
        return self.transition_map_data.wildcard_char is not None

    @property
    def step_list(self):
        return self.__step_list

    @property
    def transition_map(self):
        return self.transition_map_data.transition_map

    def finalize(self, TerminalStateIndex):
        self.__step_list.append(CharacterPathStep(TerminalStateIndex, None))
        self.transition_map_data.finalize()

    def contains_state(self, StateIndex):
        """Is 'StateIndex' on the path(?). This includes the terminal."""
        for dummy in (x for x in self.__step_list if x.state_index == StateIndex):
            return True
        return False

    def implemented_state_index_set(self):
        return set(x.state_index for x in self.__step_list[:-1])

    def state_index_set_size(self):
        return len(self.__step_list) - 1

    def state_index_set(self):
        assert len(self.__step_list) > 1
        return set(x.state_index for x in self.__step_list[:-1])

    def __repr__(self):
        return self.get_string()

    def get_string(self, NormalizeDB=None):
        # assert NormalizeDB is None, "Sorry, I guessed that this was no longer used."
        def norm(X):
            assert isinstance(X, (int, long)) or X is None
            return X if NormalizeDB is None else NormalizeDB[X]

        skeleton_txt = self.transition_map_data.get_string()

        sequence_txt = ""
        for x in self.__step_list[:-1]:
            sequence_txt += "(%i)--'%s'-->" % (x.state_index, chr(x.trigger))
        sequence_txt += "[%i]" % norm(self.__step_list[-1].state_index)

        return "".join(["start    = %i;\n" % norm(self.__step_list[0].state_index),
                        "path     = %s;\n" % sequence_txt,
                        "skeleton = {\n%s}\n"  % skeleton_txt, 
                        "wildcard = %s;\n" % repr(self.transition_map_data.wildcard_char is not None)])


    def assert_consistency(self, TheAnalyzer, CompressionType):
        assert len(self.__step_list) >= 2

        # If uniformity was required, it must have been maintained.
        if CompressionType == E_Compression.PATH_UNIFORM:
            assert self.uniform_entry_OpList.is_uniform()

        # If entry command list is claimed to be uniform
        # => then all states in path must have this particular drop-out (except for the first).
        if self.uniform_entry_OpList.is_uniform():
            prev_state_index = self.__step_list[0].state_index
            for state in (TheAnalyzer.state_db[s.state_index] for s in self.__step_list[1:-1]):
                command_list = state.entry.get_command_list(state.index, prev_state_index, TriggerId=0)
                assert command_list == self.uniform_entry_OpList.content
                prev_state_index = state.index
        return
コード例 #19
0
ファイル: path.py プロジェクト: smmckay/quex3
class CharacterPath(object):
    """________________________________________________________________________

    Represents identified path in the state machine such as:


       ( 1 )--- 'a' -->( 2 )--- 'b' -->( 3 )--- 'c' -->( 4 )--- 'd' -->( 5 )

    where the remaining transitions in each state match (except for the last).

    During analysis, the CharacterPath acts as a container which also stores
    information about the transition_map. The TransitionMapData object
    maintains information about possible transition_map matches with other
    states.

    ___________________________________________________________________________
    MEMBERS:
  
    .step_list: (list of CharacterPathStep-s)

       .------------------. .------------------.       .--------------------.
       |.state_index = 1  | |.state_index = 2  |       |.state_index = 4711 |
       |.trigger     = 'a'| |.trigger     = 'b'| . . . |.trigger     = None |
       '------------------' '------------------'       '--------------------'

       The last element of the '__step_list' contains the terminal. The 
       terminal state is not implemented by the path walker. It is the first
       state entered after the path walker has finished. Its role is further
       indicated by a trigger of 'None'.
   
    .uniform_entry_OpList:

      This object  keeps track about the actions to  be executed upon entry
      into a state on the path and upon drop-out of a state on on the path. If 
      any of them is the same for all paths on the state it contains an object
      which is not 'None'. In case it is not uniform 'None' is contained.

    .transition_map_data

     maintains data about the common transition map of all states. 
    ___________________________________________________________________________
    """
    __slots__ = ("__step_list",       
                 "transition_map_data", 
                 "uniform_entry_OpList") 

    def __init__(self, StartState, TheTransitionMap, TransitionCharacter):
        if StartState is None: return # Only for Clone

        assert isinstance(StartState,          FSM_State)
        assert isinstance(TransitionCharacter, (int, long)), "Found '%s'" % TransitionCharacter
        assert isinstance(TheTransitionMap,    TransitionMap)

        # uniform_entry_OpList: 
        #    The entry into the StartState happens from outside.
        #    => A 'state_key' is assigned (the path_iterator)
        #       and it is not part of the iteration loop.
        #    => The StartState's entry IS NOT subject to uniformity considerations.
        self.uniform_entry_OpList = UniformObject()

        self.__step_list = [ CharacterPathStep(StartState.index, TransitionCharacter) ]

        self.transition_map_data = TransitionMapData(TheTransitionMap, TransitionCharacter)

    def clone(self):
        result = CharacterPath(None, None, None)

        result.uniform_entry_OpList       = self.uniform_entry_OpList.clone()
        result.__step_list                     = [ x for x in self.__step_list ] # CharacterPathStep are immutable
        result.transition_map_data             = self.transition_map_data.clone()
        return result

    def extended_clone(self, PreviousTerminal, TransitionCharacter, TransitionMapWildCardPlug):
        """Append 'State' to path:

        -- add 'State' to the sequence of states on the path.

        -- absorb the 'State's' drop-out and entry actions into the path's 
           drop-out and entry actions.
        
        If 'TransitionCharacter' is None, then the state is considered a
        terminal state. Terminal states are not themselves implemented inside a
        PathWalkerState. Thus, their entry and drop out information does not
        have to be absorbed.
        """
        assert    TransitionCharacter is not None
        assert    isinstance(TransitionMapWildCardPlug, DoorID) \
               or TransitionMapWildCardPlug == -1

        result = self.clone()

        # OpList upon Entry to State
        # (TriggerIndex == 0, because there can only be one transition from
        #                     one state to the next on the path).
        prev_step         = self.__step_list[-1]
        entry_OpList = PreviousTerminal.entry.get_command_list(PreviousTerminal.index, 
                                                                              prev_step.state_index,
                                                                              TriggerId=0)
        assert entry_OpList is not None

        result.uniform_entry_OpList <<= entry_OpList

        result.__step_list.append(CharacterPathStep(PreviousTerminal.index, TransitionCharacter))

        result.transition_map_data.plug_this(TransitionMapWildCardPlug)

        return result

    def uniformity_with_predecessor(self, State):
        """Check whether the entry of the last state on the path executes the
           same OpList as the entry to 'State'. 
        """
        # This function is supposed to be called only when uniformity is required.
        # If so, then the path must be in a uniform state at any time. 
        assert self.uniform_entry_OpList.is_uniform()

        # Check on 'Entry' for what is done along the path.
        #
        # OpList upon Entry to State
        # (TriggerIndex == 0, because there can only be one transition from
        #                     one state to the next on the path).
        prev_step    = self.__step_list[-1]
        command_list = State.entry.get_command_list(State.index, prev_step.state_index, 
                                                    TriggerId=0)
        assert command_list is not None

        if not self.uniform_entry_OpList.fit(command_list): 
            return False

        return True

    def has_wildcard(self):
        return self.transition_map_data.wildcard_char is not None

    @property
    def step_list(self):
        return self.__step_list

    @property
    def transition_map(self):
        return self.transition_map_data.transition_map

    def finalize(self, TerminalStateIndex):
        self.__step_list.append(CharacterPathStep(TerminalStateIndex, None))
        self.transition_map_data.finalize()

    def contains_state(self, StateIndex):
        """Is 'StateIndex' on the path(?). This includes the terminal."""
        for dummy in (x for x in self.__step_list if x.state_index == StateIndex):
            return True
        return False

    def implemented_state_index_set(self):
        return set(x.state_index for x in self.__step_list[:-1])

    def state_index_set_size(self):
        return len(self.__step_list) - 1

    def state_index_set(self):
        assert len(self.__step_list) > 1
        return set(x.state_index for x in self.__step_list[:-1])

    def __repr__(self):
        return self.get_string()

    def get_string(self, NormalizeDB=None):
        # assert NormalizeDB is None, "Sorry, I guessed that this was no longer used."
        def norm(X):
            assert isinstance(X, (int, long)) or X is None
            return X if NormalizeDB is None else NormalizeDB[X]

        skeleton_txt = self.transition_map_data.get_string()

        sequence_txt = ""
        for x in self.__step_list[:-1]:
            sequence_txt += "(%i)--'%s'-->" % (x.state_index, chr(x.trigger))
        sequence_txt += "[%i]" % norm(self.__step_list[-1].state_index)

        return "".join(["start    = %i;\n" % norm(self.__step_list[0].state_index),
                        "path     = %s;\n" % sequence_txt,
                        "skeleton = {\n%s}\n"  % skeleton_txt, 
                        "wildcard = %s;\n" % repr(self.transition_map_data.wildcard_char is not None)])


    def assert_consistency(self, TheAnalyzer, CompressionType):
        assert len(self.__step_list) >= 2

        # If uniformity was required, it must have been maintained.
        if CompressionType == E_Compression.PATH_UNIFORM:
            assert self.uniform_entry_OpList.is_uniform()

        # If entry command list is claimed to be uniform
        # => then all states in path must have this particular drop-out (except for the first).
        if self.uniform_entry_OpList.is_uniform():
            prev_state_index = self.__step_list[0].state_index
            for state in (TheAnalyzer.state_db[s.state_index] for s in self.__step_list[1:-1]):
                command_list = state.entry.get_command_list(state.index, prev_state_index, TriggerId=0)
                assert command_list == self.uniform_entry_OpList.content
                prev_state_index = state.index
        return