123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- #=======================================================================
- #
- # Python Lexical Analyser
- #
- # Converting NFA to DFA
- #
- #=======================================================================
- from __future__ import absolute_import
- from . import Machines
- from .Machines import LOWEST_PRIORITY
- from .Transitions import TransitionMap
- def nfa_to_dfa(old_machine, debug=None):
- """
- Given a nondeterministic Machine, return a new equivalent
- Machine which is deterministic.
- """
- # We build a new machine whose states correspond to sets of states
- # in the old machine. Initially we add a new state corresponding to
- # the epsilon-closure of each initial old state. Then we give transitions
- # to each new state which are the union of all transitions out of any
- # of the corresponding old states. The new state reached on a given
- # character is the one corresponding to the set of states reachable
- # on that character from any of the old states. As new combinations of
- # old states are created, new states are added as needed until closure
- # is reached.
- new_machine = Machines.FastMachine()
- state_map = StateMap(new_machine)
- # Seed the process using the initial states of the old machine.
- # Make the corresponding new states into initial states of the new
- # machine with the same names.
- for (key, old_state) in old_machine.initial_states.items():
- new_state = state_map.old_to_new(epsilon_closure(old_state))
- new_machine.make_initial_state(key, new_state)
- # Tricky bit here: we add things to the end of this list while we're
- # iterating over it. The iteration stops when closure is achieved.
- for new_state in new_machine.states:
- transitions = TransitionMap()
- for old_state in state_map.new_to_old(new_state):
- for event, old_target_states in old_state.transitions.items():
- if event and old_target_states:
- transitions.add_set(event, set_epsilon_closure(old_target_states))
- for event, old_states in transitions.items():
- new_machine.add_transitions(new_state, event, state_map.old_to_new(old_states))
- if debug:
- debug.write("\n===== State Mapping =====\n")
- state_map.dump(debug)
- return new_machine
- def set_epsilon_closure(state_set):
- """
- Given a set of states, return the union of the epsilon
- closures of its member states.
- """
- result = {}
- for state1 in state_set:
- for state2 in epsilon_closure(state1):
- result[state2] = 1
- return result
- def epsilon_closure(state):
- """
- Return the set of states reachable from the given state
- by epsilon moves.
- """
- # Cache the result
- result = state.epsilon_closure
- if result is None:
- result = {}
- state.epsilon_closure = result
- add_to_epsilon_closure(result, state)
- return result
- def add_to_epsilon_closure(state_set, state):
- """
- Recursively add to |state_set| states reachable from the given state
- by epsilon moves.
- """
- if not state_set.get(state, 0):
- state_set[state] = 1
- state_set_2 = state.transitions.get_epsilon()
- if state_set_2:
- for state2 in state_set_2:
- add_to_epsilon_closure(state_set, state2)
- class StateMap(object):
- """
- Helper class used by nfa_to_dfa() to map back and forth between
- sets of states from the old machine and states of the new machine.
- """
- new_machine = None # Machine
- old_to_new_dict = None # {(old_state,...) : new_state}
- new_to_old_dict = None # {id(new_state) : old_state_set}
- def __init__(self, new_machine):
- self.new_machine = new_machine
- self.old_to_new_dict = {}
- self.new_to_old_dict = {}
- def old_to_new(self, old_state_set):
- """
- Return the state of the new machine corresponding to the
- set of old machine states represented by |state_set|. A new
- state will be created if necessary. If any of the old states
- are accepting states, the new state will be an accepting state
- with the highest priority action from the old states.
- """
- key = self.make_key(old_state_set)
- new_state = self.old_to_new_dict.get(key, None)
- if not new_state:
- action = self.highest_priority_action(old_state_set)
- new_state = self.new_machine.new_state(action)
- self.old_to_new_dict[key] = new_state
- self.new_to_old_dict[id(new_state)] = old_state_set
- #for old_state in old_state_set.keys():
- #new_state.merge_actions(old_state)
- return new_state
- def highest_priority_action(self, state_set):
- best_action = None
- best_priority = LOWEST_PRIORITY
- for state in state_set:
- priority = state.action_priority
- if priority > best_priority:
- best_action = state.action
- best_priority = priority
- return best_action
- # def old_to_new_set(self, old_state_set):
- # """
- # Return the new state corresponding to a set of old states as
- # a singleton set.
- # """
- # return {self.old_to_new(old_state_set):1}
- def new_to_old(self, new_state):
- """Given a new state, return a set of corresponding old states."""
- return self.new_to_old_dict[id(new_state)]
- def make_key(self, state_set):
- """
- Convert a set of states into a uniquified
- sorted tuple suitable for use as a dictionary key.
- """
- lst = list(state_set)
- lst.sort()
- return tuple(lst)
- def dump(self, file):
- from .Transitions import state_set_str
- for new_state in self.new_machine.states:
- old_state_set = self.new_to_old_dict[id(new_state)]
- file.write(" State %s <-- %s\n" % (
- new_state['number'], state_set_str(old_state_set)))
|