Home | History | Annotate | Download | only in Plex
      1 #=======================================================================
      2 #
      3 #   Python Lexical Analyser
      4 #
      5 #   Converting NFA to DFA
      6 #
      7 #=======================================================================
      8 
      9 import Machines
     10 from Machines import LOWEST_PRIORITY
     11 from Transitions import TransitionMap
     12 
     13 def nfa_to_dfa(old_machine, debug = None):
     14   """
     15   Given a nondeterministic Machine, return a new equivalent
     16   Machine which is deterministic.
     17   """
     18   # We build a new machine whose states correspond to sets of states
     19   # in the old machine. Initially we add a new state corresponding to
     20   # the epsilon-closure of each initial old state. Then we give transitions
     21   # to each new state which are the union of all transitions out of any
     22   # of the corresponding old states. The new state reached on a given
     23   # character is the one corresponding to the set of states reachable
     24   # on that character from any of the old states. As new combinations of
     25   # old states are created, new states are added as needed until closure
     26   # is reached.
     27   new_machine = Machines.FastMachine()
     28   state_map = StateMap(new_machine)
     29   # Seed the process using the initial states of the old machine.
     30   # Make the corresponding new states into initial states of the new
     31   # machine with the same names.
     32   for (key, old_state) in old_machine.initial_states.iteritems():
     33     new_state = state_map.old_to_new(epsilon_closure(old_state))
     34     new_machine.make_initial_state(key, new_state)
     35   # Tricky bit here: we add things to the end of this list while we're
     36   # iterating over it. The iteration stops when closure is achieved.
     37   for new_state in new_machine.states:
     38     transitions = TransitionMap()
     39     for old_state in state_map.new_to_old(new_state):
     40       for event, old_target_states in old_state.transitions.iteritems():
     41         if event and old_target_states:
     42           transitions.add_set(event, set_epsilon_closure(old_target_states))
     43     for event, old_states in transitions.iteritems():
     44       new_machine.add_transitions(new_state, event, state_map.old_to_new(old_states))
     45   if debug:
     46     debug.write("\n===== State Mapping =====\n")
     47     state_map.dump(debug)
     48   return new_machine
     49 
     50 def set_epsilon_closure(state_set):
     51   """
     52   Given a set of states, return the union of the epsilon
     53   closures of its member states.
     54   """
     55   result = {}
     56   for state1 in state_set:
     57     for state2 in epsilon_closure(state1):
     58       result[state2] = 1
     59   return result
     60 
     61 def epsilon_closure(state):
     62   """
     63   Return the set of states reachable from the given state
     64   by epsilon moves.
     65   """
     66   # Cache the result
     67   result = state.epsilon_closure
     68   if result is None:
     69     result = {}
     70     state.epsilon_closure = result
     71     add_to_epsilon_closure(result, state)
     72   return result
     73 
     74 def add_to_epsilon_closure(state_set, state):
     75   """
     76   Recursively add to |state_set| states reachable from the given state
     77   by epsilon moves.
     78   """
     79   if not state_set.get(state, 0):
     80     state_set[state] = 1
     81     state_set_2 = state.transitions.get_epsilon()
     82     if state_set_2:
     83       for state2 in state_set_2:
     84         add_to_epsilon_closure(state_set, state2)
     85 
     86 class StateMap(object):
     87   """
     88   Helper class used by nfa_to_dfa() to map back and forth between
     89   sets of states from the old machine and states of the new machine.
     90   """
     91   new_machine     = None # Machine
     92   old_to_new_dict = None # {(old_state,...) : new_state}
     93   new_to_old_dict = None # {id(new_state) : old_state_set}
     94 
     95   def __init__(self, new_machine):
     96     self.new_machine = new_machine
     97     self.old_to_new_dict = {}
     98     self.new_to_old_dict= {}
     99 
    100   def old_to_new(self, old_state_set):
    101     """
    102     Return the state of the new machine corresponding to the
    103     set of old machine states represented by |state_set|. A new
    104     state will be created if necessary. If any of the old states
    105     are accepting states, the new state will be an accepting state
    106     with the highest priority action from the old states.
    107     """
    108     key = self.make_key(old_state_set)
    109     new_state = self.old_to_new_dict.get(key, None)
    110     if not new_state:
    111       action = self.highest_priority_action(old_state_set)
    112       new_state = self.new_machine.new_state(action)
    113       self.old_to_new_dict[key] = new_state
    114       self.new_to_old_dict[id(new_state)] = old_state_set
    115       #for old_state in old_state_set.keys():
    116         #new_state.merge_actions(old_state)
    117     return new_state
    118 
    119   def highest_priority_action(self, state_set):
    120     best_action = None
    121     best_priority = LOWEST_PRIORITY
    122     for state in state_set:
    123       priority = state.action_priority
    124       if priority > best_priority:
    125         best_action = state.action
    126         best_priority = priority
    127     return best_action
    128 
    129 #    def old_to_new_set(self, old_state_set):
    130 #        """
    131 #        Return the new state corresponding to a set of old states as
    132 #        a singleton set.
    133 #        """
    134 #        return {self.old_to_new(old_state_set):1}
    135 
    136   def new_to_old(self, new_state):
    137     """Given a new state, return a set of corresponding old states."""
    138     return self.new_to_old_dict[id(new_state)]
    139 
    140   def make_key(self, state_set):
    141     """
    142     Convert a set of states into a uniquified
    143     sorted tuple suitable for use as a dictionary key.
    144     """
    145     lst = list(state_set)
    146     lst.sort()
    147     return tuple(lst)
    148 
    149   def dump(self, file):
    150     from Transitions import state_set_str
    151     for new_state in self.new_machine.states:
    152       old_state_set = self.new_to_old_dict[id(new_state)]
    153       file.write("   State %s <-- %s\n" % (
    154         new_state['number'], state_set_str(old_state_set)))
    155 
    156 
    157