Subversion Repositories programming

Rev

Rev 131 | Rev 136 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
130 ira 1
#!/usr/bin/env python
2
# Copyright: Ira W. Snyder
3
# Start Date: 2005-10-08
131 ira 4
# End Date:
130 ira 5
# License: Public Domain
6
#
7
# Changelog Follows:
131 ira 8
# - 2005-10-16
9
# - Implemented the nfa class. It's mostly complete at this point.
10
# - E() function works for circular loops now.
11
# - Made the nfa.__next_states() function always return a valid reference
12
#   to a list in the dictionary. This means you should NEVER use
13
#   self.trans_func[(state, letter)] in code anywhere now :)
14
# - Final states are now checked for validity.
130 ira 15
#
16
 
17
################################################################################
18
# IMPORTANT NOTES
19
################################################################################
20
################################################################################
21
 
22
import sys, string
23
 
24
class nfa:
25
    """Implements a Non-Deterministic Finite Automaton"""
26
 
27
    def __init__(self):
28
        self.trans_func = {}
29
        self.num_states = 0
30
        self.final_states = []
31
        self.initial_state = 0
131 ira 32
 
130 ira 33
    def __state_in_range(self, state):
34
        """Check if a given state is in the acceptable range"""
35
 
36
        try:
37
            temp = int(state)
38
        except (TypeError, ValueError):
39
            return False
40
 
41
        if temp >= 0 and temp < self.num_states:
42
            return True
43
 
44
        return False
131 ira 45
 
130 ira 46
    def __input_num_states(self):
47
        """Get the number of states this automaton will have"""
48
 
49
        done = False
50
        while not done:
51
            num_states = raw_input('How many states in this automaton: ')
52
 
53
            try:
54
                self.num_states = int(num_states)
55
                done = True
56
            except (TypeError, ValueError):
57
                print 'Bad input, not an integer, try again'
58
                print
59
 
60
    def __input_final_states(self):
61
        """Get final states for this automaton"""
62
 
63
        print 'Enter final states on one line, seperated by spaces'
131 ira 64
 
130 ira 65
        done = False
66
        while not done:
67
            final_states = raw_input('Final states: ')
131 ira 68
 
130 ira 69
            bad_data = False
70
            for i in final_states.split():
71
                if not self.__state_in_range(i):
72
                    print 'State %s out of range. All input discarded.' % (i, )
73
                    print 'Try again please'
74
                    print
75
                    bad_data = True
76
                    break
77
 
78
            # if we left the for loop with bad data
79
            if bad_data:
80
                continue
81
 
82
            # all data is good
83
            for i in final_states.split():
131 ira 84
                if self.__state_in_range(i):
85
                    self.final_states.append(int(i))
130 ira 86
 
87
            done = True
88
 
89
    def __input_all_edges(self):
90
        """Read all of the edges for this automaton"""
91
 
92
        print 'Edges take the form "from HERE, by LETTER, to THERE"'
93
        print 'Enter something like: "1 a 1" to represent the following'
94
        print 'from q1, by a, go to q1'
95
        print
96
        print 'Conditions:'
97
        print '1. Blank line to end'
98
        print '2. ^ is the empty string'
99
 
100
        done = False
101
        while not done:
102
            edge = raw_input('Edge: ')
103
 
104
            if len(edge) == 0:
105
                done = True
106
                continue
131 ira 107
 
130 ira 108
            if len(edge.split()) != 3:
109
                print 'Bad edge entered'
110
                print
111
                continue
131 ira 112
 
130 ira 113
            (cur_st, letter, next_st) = edge.split()
114
 
131 ira 115
            # Make sure the states entered are in range
130 ira 116
            if self.__state_in_range(cur_st) and self.__state_in_range(next_st):
117
                new_state = False
131 ira 118
                cur_st = int(cur_st)
119
                next_st = int(next_st)
130 ira 120
 
131 ira 121
                # Add the state to the list if it's not there already
122
                if next_st not in self.__next_states(cur_st, letter):
123
                    self.__next_states(cur_st, letter).append(next_st)
130 ira 124
 
125
            else:
126
                print 'Invalid current or next state entered'
127
                print
128
                continue
131 ira 129
 
130 ira 130
    def __input_automaton(self):
131
        """Read this entire automaton's input"""
132
 
133
        self.__input_num_states()
134
        self.__input_final_states()
135
        self.__input_all_edges()
136
 
137
    def main_menu(self):
131 ira 138
        #NOTE: All of this code is not what it's supposed to be.
139
        #      It is for TESTING ONLY.
140
        #
141
        #TODO: Generate a menu :)
142
 
130 ira 143
        self.__input_automaton()
144
 
145
        print self.trans_func
146
        print self.num_states
147
        print self.final_states
148
        print self.initial_state
131 ira 149
        print
130 ira 150
 
131 ira 151
        for i in range(self.num_states):
152
            print 'E(%d): %s' % (i, self.__E(i))
153
 
130 ira 154
    def __next_states(self, state, letter):
131 ira 155
        """Return the next states for the key (state, letter)"""
156
 
130 ira 157
        try:
158
            next = self.trans_func[(state, letter)]
159
        except KeyError:
131 ira 160
            # Create a new empty list for this state if it doesn't exist yet
161
            next = self.trans_func[(state, letter)] = []
130 ira 162
 
131 ira 163
        if letter == '^' and state not in next:
164
            next.append(state)
165
 
130 ira 166
        return next
131 ira 167
 
168
    def __E(self, state, storage=None, visited=None):
169
        """Calculate E(state) and return it. This handles circular E()
170
        calculations."""
171
 
172
        # Work around weird mutable default argument stuff
173
        if storage is None:
174
            storage = []
175
 
176
        # Work around weird mutable default argument stuff
177
        if visited is None:
178
            visited = []
179
 
180
        # Find all of the direct next states that we can get to by
181
        # the empty string, and append anything that is not already there
130 ira 182
        for i in self.__next_states(state, '^'):
131 ira 183
            if i not in storage:
184
                storage.append(i)
130 ira 185
 
131 ira 186
        # Visit everything in storage that has not been visited already
187
        for i in storage:
188
            if i not in visited:
130 ira 189
                visited.append(i)
131 ira 190
                temp = self.__E(i, storage, visited)
130 ira 191
 
131 ira 192
                # Avoid duplicating things in storage
193
                for j in temp:
194
                    if j not in storage:
195
                        storage.append(j)
196
 
130 ira 197
        return storage
131 ira 198
 
135 ira 199
    def __unique_sort(self, li):
200
        """Make sure everything in a list is unique, and then sort it"""
201
 
202
        newlist = []
203
 
204
        for i in li:
205
            if i not in newlist:
206
                newlist.append(i)
207
 
208
        newlist.sort()
209
        return newlist
210
 
211
    def __generate_dfa_table(self):
212
 
213
        # get the list of all letters
214
        possible_letters = []
215
 
216
        for (state, letter) in self.trans_func.keys():
217
            if letter not in possible_letters:
218
                possible_letters.append(letter)
219
 
220
        # DFA_TABLE STRUCTURE
221
        #
222
        # [ [Final, DFA_State_Number, [States_in_NFA], letter_1, letter_2, ..., letter_n]
223
        #   [Final, DFA_State_Number, [States_in_NFA], letter_1, letter_2, ..., letter_n]
224
        #   [Final, DFA_State_Number, [States_in_NFA], letter_1, letter_2, ..., letter_n] ]
225
        #
226
 
227
        dfa_table = []
228
 
229
        # find the first state's definition
230
        def_in_nfa = self.__E(0)
231
        is_final = False
232
 
233
        temp_record = []
234
 
235
        # find out if we're final
236
        for i in def_in_nfa:
237
            if i in self.final_states:
238
                is_final = True
239
                break
240
 
241
        temp_record.append(is_final)
242
        temp_record.append(len(dfa_table))
243
        temp_record.append(def_in_nfa)
244
 
245
        # find out where we can go by each letter
246
        for l in possible_letters:
247
            where_to_go = []
248
 
249
            for s in def_in_nfa:
250
                where_to_go.extend(self.__next_states(s, l))
251
 
252
            where_to_go = self.__unique_sort(where_to_go)
253
 
254
            create_new_state = True
255
 
256
            for s in dfa_table:
257
                if s[2] == where_to_go:
258
                    temp_record.append(s[1])
259
                    create_new_state = False
260
                    break
261
 
262
            if create_new_state:
263
                # recurse???
264
                pass
265
 
266
################################################################################
267
# NEW STUFF IS AFTER THIS
268
################################################################################
269
 
270
        temp_record = [None for n in range(len(possible_letters) + 3)]
271
 
272
        # start at initial state
273
        # generate E(initial_state)
274
        # find out if we are a final state (based on E() )
275
        # name the state (starting at 0)
276
        #
277
 
130 ira 278
    def output_dfa(self):
279
        #stuff here
280
        pass
281
 
282
if __name__ == '__main__':
283
    automaton = nfa()
284
    automaton.main_menu()
285
 
286