Rev 130 | Rev 135 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
#!/usr/bin/env python# Copyright: Ira W. Snyder# Start Date: 2005-10-08# End Date:# License: Public Domain## Changelog Follows:# - 2005-10-16# - Implemented the nfa class. It's mostly complete at this point.# - E() function works for circular loops now.# - Made the nfa.__next_states() function always return a valid reference# to a list in the dictionary. This means you should NEVER use# self.trans_func[(state, letter)] in code anywhere now :)# - Final states are now checked for validity.################################################################################## IMPORTANT NOTES################################################################################################################################################################import sys, stringclass nfa:"""Implements a Non-Deterministic Finite Automaton"""def __init__(self):self.trans_func = {}self.num_states = 0self.final_states = []self.initial_state = 0def __state_in_range(self, state):"""Check if a given state is in the acceptable range"""try:temp = int(state)except (TypeError, ValueError):return Falseif temp >= 0 and temp < self.num_states:return Truereturn Falsedef __input_num_states(self):"""Get the number of states this automaton will have"""done = Falsewhile not done:num_states = raw_input('How many states in this automaton: ')try:self.num_states = int(num_states)done = Trueexcept (TypeError, ValueError):print 'Bad input, not an integer, try again'def __input_final_states(self):"""Get final states for this automaton"""print 'Enter final states on one line, seperated by spaces'done = Falsewhile not done:final_states = raw_input('Final states: ')bad_data = Falsefor i in final_states.split():if not self.__state_in_range(i):print 'State %s out of range. All input discarded.' % (i, )print 'Try again please'bad_data = Truebreak# if we left the for loop with bad dataif bad_data:continue# all data is goodfor i in final_states.split():if self.__state_in_range(i):self.final_states.append(int(i))done = Truedef __input_all_edges(self):"""Read all of the edges for this automaton"""print 'Edges take the form "from HERE, by LETTER, to THERE"'print 'Enter something like: "1 a 1" to represent the following'print 'from q1, by a, go to q1'print 'Conditions:'print '1. Blank line to end'print '2. ^ is the empty string'done = Falsewhile not done:edge = raw_input('Edge: ')if len(edge) == 0:done = Truecontinueif len(edge.split()) != 3:print 'Bad edge entered'continue(cur_st, letter, next_st) = edge.split()# Make sure the states entered are in rangeif self.__state_in_range(cur_st) and self.__state_in_range(next_st):new_state = Falsecur_st = int(cur_st)next_st = int(next_st)# Add the state to the list if it's not there alreadyif next_st not in self.__next_states(cur_st, letter):self.__next_states(cur_st, letter).append(next_st)else:print 'Invalid current or next state entered'continuedef __input_automaton(self):"""Read this entire automaton's input"""self.__input_num_states()self.__input_final_states()self.__input_all_edges()def main_menu(self):#NOTE: All of this code is not what it's supposed to be.# It is for TESTING ONLY.##TODO: Generate a menu :)self.__input_automaton()print self.trans_funcprint self.num_statesprint self.final_statesprint self.initial_statefor i in range(self.num_states):print 'E(%d): %s' % (i, self.__E(i))def __next_states(self, state, letter):"""Return the next states for the key (state, letter)"""try:next = self.trans_func[(state, letter)]except KeyError:# Create a new empty list for this state if it doesn't exist yetnext = self.trans_func[(state, letter)] = []if letter == '^' and state not in next:next.append(state)return nextdef __E(self, state, storage=None, visited=None):"""Calculate E(state) and return it. This handles circular E()calculations."""# Work around weird mutable default argument stuffif storage is None:storage = []# Work around weird mutable default argument stuffif visited is None:visited = []# Find all of the direct next states that we can get to by# the empty string, and append anything that is not already therefor i in self.__next_states(state, '^'):if i not in storage:storage.append(i)# Visit everything in storage that has not been visited alreadyfor i in storage:if i not in visited:visited.append(i)temp = self.__E(i, storage, visited)# Avoid duplicating things in storagefor j in temp:if j not in storage:storage.append(j)return storagedef output_dfa(self):#stuff herepassif __name__ == '__main__':automaton = nfa()automaton.main_menu()