Subversion Repositories programming

Rev

Rev 130 | Rev 135 | 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
 
130 ira 199
    def output_dfa(self):
200
        #stuff here
201
        pass
202
 
203
if __name__ == '__main__':
204
    automaton = nfa()
205
    automaton.main_menu()
206
 
207