3 # This module keeps track of arbitrary "states" at any point of the code.
4 # A state is considered known if every path to the given point agrees on
5 # its state, otherwise it is None (i.e. unknown).
7 # It might be useful to be able to "freeze" the set of states by pushing
8 # all state changes to the tips of the trees for fast reading. Perhaps this
9 # could be done on get_state, clearing the cache on set_state (assuming
10 # incoming is immutable).
12 # This module still needs a lot of work, and probably should totally be
13 # redesigned. It doesn't take return, raise, continue, or break into
16 from Cython.Compiler.Scanning import StringSourceDescriptor
18 _END_POS = (StringSourceDescriptor(unichr(sys.maxunicode)*10, ''),
19 sys.maxint, sys.maxint)
20 except AttributeError: # Py3
21 _END_POS = (StringSourceDescriptor(unichr(sys.maxunicode)*10, ''),
22 sys.maxsize, sys.maxsize)
24 class ControlFlow(object):
26 def __init__(self, start_pos, incoming, parent):
27 self.start_pos = start_pos
28 self.incoming = incoming
29 if parent is None and incoming is not None:
30 parent = incoming.parent
33 self.end_pos = _END_POS
35 def start_branch(self, pos):
37 branch_point = BranchingControlFlow(pos, self)
38 if self.parent is not None:
39 self.parent.branches[-1] = branch_point
40 return branch_point.branches[0]
42 def next_branch(self, pos):
44 return self.parent.new_branch(pos)
46 def finish_branch(self, pos):
48 self.parent.end_pos = pos
49 return LinearControlFlow(pos, self.parent)
51 def get_state(self, item, pos=_END_POS):
52 return self.get_pos_state(item, pos)[1]
54 def get_pos_state(self, item, pos=_END_POS):
56 if pos > self.end_pos:
61 pos_state = self._get_pos_state(item, pos)
62 if pos > self.end_pos:
63 self.tip[item] = pos_state
66 def _get_pos_state(self, item, pos):
68 while current is not None and pos <= current.start_pos:
69 current = current.incoming
72 state = current._get_pos_state_local(item, pos)
73 while (state is None or state == (None, None)) and current.incoming is not None:
74 current = current.incoming
75 state = current._get_pos_state_local(item, pos)
80 def set_state(self, pos, item, state):
84 while pos < current.start_pos and current.incoming is not None:
85 current = current.incoming
86 if item in current.tip:
88 current._set_state_local(pos, item, state)
91 class LinearControlFlow(ControlFlow):
93 def __init__(self, start_pos=(), incoming=None, parent=None):
94 ControlFlow.__init__(self, start_pos, incoming, parent)
97 def _set_state_local(self, pos, item, state):
98 if item in self.events:
99 event_list = self.events[item]
102 self.events[item] = event_list
103 bisect.insort(event_list, (pos, state))
105 def _get_pos_state_local(self, item, pos):
106 if item in self.events:
107 event_list = self.events[item]
108 for event in event_list[::-1]:
113 def to_string(self, indent='', limit=None):
115 if len(self.events) == 0:
116 s = indent + "[no state changes]"
120 for item, event_list in self.events.items():
121 for pos, state in event_list:
122 all.append((indent, pos, item, state))
124 all = ["%s%s: %s <- %s" % data for data in all]
126 if self.incoming is not limit and self.incoming is not None:
127 s = "%s\n%s" % (self.incoming.to_string(indent, limit=limit), s)
131 class BranchingControlFlow(ControlFlow):
133 def __init__(self, start_pos, incoming, parent=None):
134 ControlFlow.__init__(self, start_pos, incoming, parent)
135 self.branches = [LinearControlFlow(start_pos, incoming, parent=self)]
136 self.branch_starts = [start_pos]
138 def _set_state_local(self, pos, item, state):
139 for branch_pos, branch in zip(self.branch_starts[::-1], self.branches[::-1]):
140 if pos >= branch_pos:
141 branch._set_state_local(pos, item, state)
144 def _get_pos_state_local(self, item, pos, stop_at=None):
145 if pos < self.end_pos:
146 for branch_pos, branch in zip(self.branch_starts[::-1], self.branches[::-1]):
147 if pos >= branch_pos:
148 return branch._get_pos_state_local(item, pos)
150 state = self.branches[0]._get_pos_state_local(item, pos)
153 last_pos, last_state = state
154 if last_state is None:
156 for branch in self.branches[1:]:
157 state = branch._get_pos_state_local(item, pos)
160 other_pos, other_state = state
161 if other_state != last_state:
163 elif last_pos is not other_pos:
164 last_pos = max(last_pos, other_pos)
165 return last_pos, last_state
168 def new_branch(self, pos):
169 self.branches.append(LinearControlFlow(pos, self.incoming, parent=self))
170 self.branch_starts.append(pos)
171 return self.branches[-1]
173 def to_string(self, indent='', limit=None):
174 join = "\n%sor\n" % indent
175 s = join.join([branch.to_string(indent+" ", limit=self.incoming) for branch in self.branches])
176 if self.incoming is not limit and self.incoming is not None:
177 s = "%s\n%s" % (self.incoming.to_string(indent, limit=limit), s)