root/lang/python/dfareg/branches/codezine200809/dfareg/nfa.py @ 25811

Revision 25811, 5.0 kB (checked in by hiratara, 4 years ago)

バグってた。

Line 
1# -*- coding: utf-8 -*-
2"""
3NFA definition
4----------------------------------------
5Author: hiratara <hira.tara@gmail.com>
6"""
7class NFARuntime(object):
8    def __init__(self, NFA, debug=False):
9        self.NFA = NFA
10        self.cur_states = self.NFA.epsilon_expand(
11            frozenset([ self.NFA.start ])
12            )
13
14    def do_transition(self, char):
15        next_states = set()
16        for state in self.cur_states:
17            next_states |= self.NFA.transition(state, char)
18        next_states = self.NFA.epsilon_expand(next_states)
19
20        self.cur_states = next_states
21
22    def is_accept_state(self):
23        for state in self.cur_states:
24            if state in self.NFA.accepts: return True
25        return False
26
27    def does_accept(self, input):
28        for alphabet in input:
29            self.do_transition(alphabet)
30        return self.is_accept_state()
31
32
33class NFABacktrackRuntime(object):
34    def __init__(self, NFA, debug=False):
35        self.NFA = NFA
36        self.cur_state = self.NFA.start
37        self.left     = None
38        self.branches = set()
39        self.done     = set()
40        self.debug    = debug
41
42    def do_transition(self):
43        # 文字列を読み終わったので、Falseを返す
44        if self.left is None:
45            return False
46
47        original_left = self.left
48
49        # とりうる遷移
50        branches = set()
51
52        # εを読む
53        for state in self.NFA.transition(self.cur_state, u""):
54            branches.add( (state, self.left) )
55
56        if self.left:
57            # まだ読める字があるので、次の1文字を読む
58            char, self.left = self.left[0], self.left[1:]
59            for state in self.NFA.transition(self.cur_state, char):
60                branches.add( (state, self.left) )
61        else:
62            # 読める字がないので、読まずに終わる
63            branches.add( (self.cur_state, None) );
64
65        # すでに辿った道を除く
66        branches = set( filter(lambda x: x not in self.done, branches) )
67
68        if branches:
69            # どの選択肢をとるか選ぶ
70            self.cur_state, self.left = self._select(branches)
71            # 残りはバックトラックできるように溜める
72            self.branches |= branches
73            if self.debug:
74                if self.left is None or original_left == self.left:
75                    char = "''"
76                else:
77                    char = original_left[0]
78                print "-%s->%s" % (char, self.cur_state),
79        else:
80            # 遷移がもうない。状態をNoneにしておく
81            self.cur_state, self.left = None, None
82
83        return True
84
85    def _select(self, branches):
86        selected = branches.pop()
87        self.done.add(selected)
88        return selected
89
90    def backtrack(self):
91        if self.branches:
92            self.cur_state, self.left = self._select(self.branches)
93            if self.debug:
94                print
95                print "backtracked: status=%s, left=%s" \
96                                                   % (self.cur_state, self.left)
97            return True
98        else:
99            return False
100
101    def is_accept_state(self):
102        return self.cur_state in self.NFA.accepts
103
104    def does_accept(self, input):
105        self.left = input
106        while True:
107            # 適当な経路でNFAを辿る
108            while self.do_transition():
109                pass
110            if self.is_accept_state():
111                # 受理状態についたので、受理。
112                return True
113            else:
114                if self.debug: print "[failed!]",
115                if self.backtrack():
116                    # ダメだったのでバックトラックして別の経路へ
117                    continue
118                else:
119                    # バックトラックできない。受理しない。
120                    return False
121
122
123class NondeterministicFiniteAutomaton(object):
124    def __init__(self,
125                 transition , # a transition function
126                 start      , # a start state
127                 accepts    , # a set of accept states
128                 ):
129        self.transition = transition
130        self.start      = start
131        self.accepts    = accepts
132
133    def get_runtime(self, debug=False):
134        # return NFARuntime(self, debug)
135        return NFABacktrackRuntime(self, debug)
136
137    def epsilon_expand(self, set_):
138        # 空文字を辿るべき状態を集めたキュー
139        que = set( set_ )
140        # 辿り終わった状態
141        done = set()
142        while que:
143            # キューから取り出す
144            stat = que.pop()
145            # 空文字によって辿れる遷移を辿る
146            nexts = self.transition(stat, "")
147            # この状態は辿り終わったので、保存
148            done.add(stat)
149            # 辿って出て来た状態を、さらに空文字で辿るのに、キューに居れる
150            for next_stat in nexts:
151                # 辿り終わってない要素だけ
152                if not next_stat in done: que.add(next_stat)
153
154        return frozenset( done )
Note: See TracBrowser for help on using the browser.