6 def do_filter(generator):
7 return acc_lines(remove_multiline_comments(to_char(remove_single_line_comments(generator))))
10 def acc_lines(generator):
17 yield current.lstrip("\n")
21 def to_char(generator):
22 for line in generator:
30 def remove_single_line_comments(generator):
32 if len(i) and i[0] == '#':
34 yield re.sub(r'//.*', '', i)
37 def remove_multiline_comments(generator):
40 for char in generator:
63 class StateMachineRenderer(object):
65 self.states = {} # state -> parent
66 self.machines = {} # state-> initial
67 self.edges = {} # event -> [(state, state)]
69 self.context = [] # [(context, depth_encountered)]
70 self.context_depth = 0
71 self.state_contents = {}
73 self.clusterlabel = {}
76 return "-------------------\n\nstates: %s\n\n machines: %s\n\n edges: %s\n\n context %s\n\n state_contents %s\n\n--------------------" % (
84 def read_input(self, input_lines):
86 for line in input_lines:
89 # pass two lines at a time to get the context so that regexes can
90 # match on split signatures
91 self.get_context(line, previous_line)
94 def get_context(self, line, previous_line):
95 match = re.search(r"(\w+::)*::(?P<tag>\w+)::\w+\(const (?P<event>\w+)", line)
96 if match is None and previous_line is not None:
97 # it is possible that we need to match on the previous line as well, so join
98 # them to make them one line and try and get this matching
99 joined_line = ' '.join([previous_line, line])
100 match = re.search(r"(\w+::)*::(?P<tag>\w+)::\w+\(\s*const (?P<event>\w+)", joined_line)
101 if match is not None:
102 self.context.append((match.group('tag'), self.context_depth, match.group('event')))
104 self.context_depth += 1
106 self.context_depth -= 1
107 while len(self.context) and self.context[-1][1] == self.context_depth:
110 def get_state(self, line):
111 if "boost::statechart::state_machine" in line:
113 r"boost::statechart::state_machine<\s*(\w*),\s*(\w*)\s*>",
116 raise Exception("Error: malformed state_machine line: " + line)
117 self.machines[tokens.group(1)] = tokens.group(2)
118 self.context.append((tokens.group(1), self.context_depth, ""))
120 if "boost::statechart::state" in line:
122 r"boost::statechart::state<\s*(\w*),\s*(\w*)\s*,?\s*(\w*)\s*>",
125 raise Exception("Error: malformed state line: " + line)
126 self.states[tokens.group(1)] = tokens.group(2)
127 if tokens.group(2) not in self.state_contents.keys():
128 self.state_contents[tokens.group(2)] = []
129 self.state_contents[tokens.group(2)].append(tokens.group(1))
130 if tokens.group(3) is not "":
131 self.machines[tokens.group(1)] = tokens.group(3)
132 self.context.append((tokens.group(1), self.context_depth, ""))
135 def get_event(self, line):
136 if "boost::statechart::transition" in line:
137 for i in re.finditer(r'boost::statechart::transition<\s*([\w:]*)\s*,\s*(\w*)\s*>',
139 if i.group(1) not in self.edges.keys():
140 self.edges[i.group(1)] = []
141 if len(self.context) is 0:
142 raise Exception("no context at line: " + line)
143 self.edges[i.group(1)].append((self.context[-1][0], i.group(2)))
144 i = re.search("return\s+transit<\s*(\w*)\s*>()", line)
146 if len(self.context) is 0:
147 raise Exception("no context at line: " + line)
148 if self.context[-1][2] is "":
149 raise Exception("no event in context at line: " + line)
150 if self.context[-1][2] not in self.edges.keys():
151 self.edges[self.context[-1][2]] = []
152 self.edges[self.context[-1][2]].append((self.context[-1][0], i.group(1)))
156 for state in self.machines.keys():
157 if state not in self.states.keys():
158 top_level.append(state)
159 print >> sys.stderr, "Top Level States: ", str(top_level)
160 print """digraph G {"""
162 print """\tcompound=true;"""
163 for i in self.emit_state(top_level[0]):
165 for i in self.edges.keys():
166 for j in self.emit_event(i):
170 def emit_state(self, state):
171 if state in self.state_contents.keys():
172 self.clusterlabel[state] = "cluster%s" % (str(self.subgraphnum),)
173 yield "subgraph cluster%s {" % (str(self.subgraphnum),)
174 self.subgraphnum += 1
175 yield """\tlabel = "%s";""" % (state,)
176 yield """\tcolor = "blue";"""
177 for j in self.state_contents[state]:
178 for i in self.emit_state(j):
183 for (k, v) in self.machines.items():
185 yield state+"[shape=Mdiamond];"
191 def emit_event(self, event):
198 for (fro, to) in self.edges[event]:
199 appendix = ['label="%s"' % (event,)]
200 if fro in self.machines.keys():
201 appendix.append("ltail=%s" % (self.clusterlabel[fro],))
202 while fro in self.machines.keys():
203 fro = self.machines[fro]
204 if to in self.machines.keys():
205 appendix.append("lhead=%s" % (self.clusterlabel[to],))
206 while to in self.machines.keys():
207 to = self.machines[to]
208 yield("%s -> %s %s;" % (fro, to, append(appendix)))
211 INPUT_GENERATOR = do_filter(sys.stdin.xreadlines())
212 RENDERER = StateMachineRenderer()
213 RENDERER.read_input(INPUT_GENERATOR)