DFA.py 5.9 KB

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