Commit a21c962a authored by Maxime Perrotin's avatar Maxime Perrotin

Support for composite states

parent 1522826f
......@@ -136,7 +136,7 @@ def _process(process):
else:
term.next_id = context.mapping[term.inputString.lower()
+ '_'
+ term.via.lower()
+ term.entrypoint.lower()
+ '_START']
def update_composite_state(state, process):
......@@ -163,20 +163,39 @@ def _process(process):
if each.kind == 'next_state':
each.inputString = state.statename + '_' + each.inputString
update_terminator(state, each, process)
def propagate_inputs(nested_state, inputlist):
''' Nested states: Inputs at level N but be handled at level N-1
that is, all inputs of a composite states (the ones that allow
to exit the composite state from the outer scope) must be
processed by each of the substates.
'''
for _, val in nested_state.mapping.viewitems():
try:
val.extend(inputlist)
except AttributeError:
pass
for each in nested_state.composite_states:
# do the same recursively
propagate_inputs(each, nested_state.mapping[nested.statename])
del nested_state.mapping[nested.statename]
for each in process.composite_states:
update_composite_state(each, process)
propagate_inputs(each, process.mapping[each.statename])
del process.mapping[each.statename]
# Update terminators at process level
for each in process.terminators:
if each.kind == 'next_state':
update_terminator(process, each, process)
# Add the process states list to the process-level variables
states_decl = 'type states is ('
states_decl += ', '.join(process.mapping.iterkeys()) + ');'
states_decl += ', '.join(name for name in process.mapping.iterkeys()
if not name.endswith('START')) + ');'
process_level_decl.append(states_decl)
process_level_decl.append('state : states := START;')
process_level_decl.append('state : states;')
# Add function allowing to trace current state as a string
process_level_decl.append('function get_state return String;')
......
......@@ -34,6 +34,7 @@ import ogAST
import sdlSymbols
import genericSymbols
import logging
from itertools import chain
from singledispatch import singledispatch
LOG = logging.getLogger(__name__)
......@@ -130,8 +131,8 @@ def _state(ast, scene, states, terminators, parent=None):
if new_state not in scene.items():
scene.addItem(new_state)
for inp in ast.inputs:
render(inp, scene=scene, parent=new_state, states=states)
for exit in chain(ast.inputs, ast.connects):
render(exit, scene=scene, parent=new_state, states=states)
new_state.nested_scene = ast.composite or ogAST.CompositeState()
......@@ -328,3 +329,19 @@ def _input(ast, scene, parent, states):
parent=inp,
states=states)
return inp
@render.register(ogAST.Connect)
def _input(ast, scene, parent, states):
''' Add connect symbol from the AST to the scene '''
# Note: PROVIDED clause is not supported
conn = sdlSymbols.Connect(parent, ast=ast)
if conn not in scene.items():
scene.addItem(inp)
if not parent:
conn.setPos(ast.pos_x, ast.pos_y)
if ast.transition:
render(ast.transition,
scene=scene,
parent=conn,
states=states)
return conn
......@@ -60,6 +60,8 @@ class Record(genericSymbols.HorizontalSymbol, object):
property_box = QtGui.QGraphicsTextItem(self)
property_box.setPos(5, 30)
property_box.setPlainText(node['properties'])
# Text in statecharts is read-only:
self.text.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction)
def set_shape(self, width, height):
''' Define the polygon shape from width and height '''
......
......@@ -286,6 +286,9 @@ class EditableText(QGraphicsTextItem, object):
Slot connected to the autocompletion popup,
invoked when selection is made
'''
if not(self.textInteractionFlags() & Qt.TextEditable):
self.completer.hide()
return
text_cursor = self.textCursor()
# Go back to the previously saved cursor position
text_cursor.setPosition(self.cursor_position)
......@@ -537,6 +540,8 @@ class Symbol(QObject, QGraphicsPathItem, object):
_unique_followers = [] # unique : e.g. comment symbol
_insertable_followers = [] # no limit to insert below current symbol
_terminal_followers = [] # cannot be inserted between two symbols
# By default a symbol is resizeable
resizeable = True
# By default symbol size may expand when inner text exceeds border
auto_expand = True
# By default connections between symbols are lines, not arrows
......@@ -561,8 +566,8 @@ class Symbol(QObject, QGraphicsPathItem, object):
redbold = ()
# Specify if the symbol can be drawn with anti-aliasing
_antialiasing = True
# Specify if the symbol text can be edited
editable = True
# Specify if the symbol has a text area
has_text_area = True
def __init__(self, parent=None):
'''
......@@ -615,6 +620,13 @@ class Symbol(QObject, QGraphicsPathItem, object):
position = Property(QPointF, _pos, _set_pos)
def is_composite(self):
''' Return True if nested scene has something in it '''
try:
return any(item.isVisible() for item in self.nested_scene.items())
except AttributeError:
return False
@property
def allowed_followers(self):
'''
......@@ -714,7 +726,7 @@ class Symbol(QObject, QGraphicsPathItem, object):
_, syntax_errors, ___, ____, _____ = (
self.parser.parseSingleElement(
self.common_name, repr(self)))
except AssertionError:
except (AssertionError, AttributeError):
LOG.error('Checker failed - no parser for this construct?')
else:
try:
......@@ -885,6 +897,8 @@ class Symbol(QObject, QGraphicsPathItem, object):
def resize_item(self, rect):
''' resize item, e.g. when editing text - move children accordingly '''
if not self.resizeable:
return
pos = self.pos()
delta_x = (self.boundingRect().width() - rect.width()) / 2.0
delta_y = self.boundingRect().height() - rect.height()
......@@ -1170,7 +1184,7 @@ class Comment(Symbol, object):
Redefinition of the Resize function
(Comment symbol only resizes in one direction)
'''
if self.grabber.resize_mode.endswith('left'):
if not self.resizeable or self.grabber.resize_mode.endswith('left'):
return
self.set_shape(rect.width(), rect.height())
self.update_connections()
......@@ -1298,6 +1312,9 @@ class Cornergrabber(QGraphicsPolygonItem, object):
# Parent item may have changed its cursor (e.g. when inserting
# items). In that case, don't override the cursor for that area
cursor = self.parent.cursor()
elif not self.parent.resizeable:
cursor = self.parent.default_cursor
self.resize_mode = ''
# Left side
elif 0.0 <= pos.x() <= 10.0:
# Top-left corner: disabled
......@@ -1311,7 +1328,6 @@ class Cornergrabber(QGraphicsPolygonItem, object):
self.resize_mode = 'left'
# Middle of item
elif 5.0 < pos.x() < self.boundingRect().width() - 10.0 and (
# pos.y() <= 10.0 or
pos.y() > self.boundingRect().height() - 10.0):
cursor = Qt.SizeVerCursor
self.resize_mode = 'bottom'
......@@ -1344,7 +1360,7 @@ class HorizontalSymbol(Symbol, object):
super(HorizontalSymbol, self).__init__(parent)
self.minDistanceToSymbolAbove = 20
self.connection = None
if self.editable:
if self.has_text_area:
self.text = EditableText(parent=self, text=text,
hyperlink=hyperlink)
if parent:
......@@ -1412,7 +1428,8 @@ class HorizontalSymbol(Symbol, object):
''' Return all the items's sibling symbols '''
try:
return (item for item in self.parent.childItems()
if item is not self and type(item) is type(self))
if item is not self and (isinstance(self, type(item)) or
isinstance(item, type(self)))) # is type(self))
except:
return ()
......@@ -1508,7 +1525,7 @@ class VerticalSymbol(Symbol, object):
x=None, y=None, hyperlink=None):
super(VerticalSymbol, self).__init__(parent)
self.connection = None
if self.editable:
if self.has_text_area:
self.text = EditableText(parent=self,
text=text,
hyperlink=hyperlink)
......
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48px"
height="48px"
id="svg3021"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="connect.svg"
inkscape:export-filename="/home/maxime/taste/tool-src/misc/opengeode/icons/connect.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs3023">
<marker
inkscape:stockid="Arrow2Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Mend"
style="overflow:visible;">
<path
id="path3838"
style="fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mstart"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Mstart"
style="overflow:visible">
<path
id="path3817"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
transform="scale(0.4) translate(10,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Lend"
style="overflow:visible;">
<path
id="path3832"
style="fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) rotate(180) translate(1,0)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7"
inkscape:cx="24"
inkscape:cy="24"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="894"
inkscape:window-height="688"
inkscape:window-x="960"
inkscape:window-y="134"
inkscape:window-maximized="0" />
<metadata
id="metadata3026">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<rect
style="opacity:0.98999999;fill:#ffdab9;stroke:#000000;stroke-width:0.99311829;stroke-opacity:1"
id="rect3029"
width="46.720268"
height="27.150137"
x="0.85415173"
y="0.56778735"
rx="10.993615"
ry="13.575068" />
<rect
style="opacity:0.98999999;fill:#ffdab9;stroke:#000000;stroke-width:0.79259622;stroke-opacity:1"
id="rect3029-8-1"
width="39.206127"
height="20.607506"
x="4.7540388"
y="3.6228223"
rx="9.2254829"
ry="10.303753" />
<path
style="fill:none;stroke:#000000;stroke-width:1.23133481;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Mend)"
d="M 24.271272,27.713533 24.436688,46.625446 24.171719,27.65284 24.436688,45.496976"
id="path3019"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
</g>
</svg>
......@@ -391,7 +391,10 @@ class Terminator(object):
# Return expression
self.return_expr = None
# via clause, used for entering nested state with an entry point
# 'via' is the string for the renderer (e.g. "via foo")
self.via = None
# 'entrypoint' is the string of the entry point (e.g. "foo")
self.entrypoint = None
# some transitions can be chained, when entering/leaving nested states
self.next_id = -1
......@@ -508,6 +511,21 @@ class Input(object):
l=self.line, c=self.charPositionInLine)
class Connect(Input):
''' AST Entry for the CONNECT part (transition below a nested state) '''
def __init__(self):
''' Only one difference with INPUT: the connect_list attribute '''
super(Connect, self).__init__()
# List of strings
self.connect_list = []
self.width = 1
def __repr__(self):
''' Debug output for a CONNECT symbol '''
return 'CONNECT {exp} ({l},{c})'.format(exp=self.inputString,
l=self.line, c=self.charPositionInLine)
class Start(object):
''' AST Entry for the START symbol '''
def __init__(self):
......@@ -578,6 +596,8 @@ class State(object):
self.height = 35
# list of type Input
self.inputs = []
# list of type Connect (connection below a nested state)
self.connects = []
# optional comment symbol
self.comment = None
# optional hyperlink
......
......@@ -1777,7 +1777,6 @@ def process_definition(root, parent=None):
errors.extend(err)
warnings.extend(warn)
process.composite_states.append(comp)
warnings.append('Composite state detected but not supported yet')
elif child.type == lexer.REFERENCED:
process.referenced = True
else:
......@@ -1788,8 +1787,7 @@ def process_definition(root, parent=None):
def input_part(root, parent, context):
''' Parse an INPUT - set of TASTE provided interfaces '''
i = ogAST.Input()
warnings = []
errors = []
warnings, errors = [], []
coord = False
# Keep track of the number of terminator statements follow the input
# useful if we want to render graphs from the SDL model
......@@ -1946,7 +1944,8 @@ def state(root, parent, context):
'be followed by a connect statement'
.format(state_def.statelist[0]))
else:
err, warn = connect_part(child, state_def, context)
conn_part, err, warn = connect_part(child, state_def, context)
state_def.connects.append(conn_part)
warnings.extend(warn)
errors.extend(err)
elif child.type == lexer.COMMENT:
......@@ -1976,31 +1975,59 @@ def state(root, parent, context):
def connect_part(root, parent, context):
''' Connection of a nested state exit point with a transition '''
''' Connection of a nested state exit point with a transition
Very similar to INPUT '''
errors, warnings = [], []
connect_list = []
coord = False
conn = ogAST.Connect()
statename = parent.statelist[0].lower()
id_token = []
# Keep track of the number of terminator statements follow the input
# useful if we want to render graphs from the SDL model
terms = len(context.terminators)
# Retrieve composite state
nested, = (comp for comp in context.composite_states
if comp.statename == statename)
for child in root.getChildren():
if child.type == lexer.ID:
connect_list.append(child.toString().lower())
if child.type == lexer.CIF:
# Get symbol coordinates
conn.pos_x, conn.pos_y, conn.width, conn.height = cif(child)
coord = True
elif child.type == lexer.ID:
id_token.append(child)
conn.connect_list.append(child.toString().lower())
elif child.type == lexer.ASTERISK:
connect_list = nested.state_exitpoints
id_token.append(child)
conn.connect_list = nested.state_exitpoints
elif child.type == lexer.TRANSITION:
trans, err, warn = transition(child, parent, context=context)
trans, err, warn = transition(child, parent=conn, context=context)
errors.extend(err)
warnings.extend(warn)
context.transitions.append(trans)
trans_id = len(context.transitions) - 1
conn.transition_id = trans_id
conn.transition = trans
elif child.type == lexer.HYPERLINK:
conn.hyperlink = child.getChild(0).toString()[1:-1]
elif child.type == lexer.COMMENT:
conn.comment, _, ___ = end(child)
else:
warnings.append('Unsupported CONNECT PART child type: ' +
str(child.type))
if not connect_list:
connect_list.append('')
for exitp in connect_list:
sdl92Parser.tokenNames[child.type])
if not conn.connect_list:
conn.connect_list.append('')
if not id_token:
conn.inputString = ''
conn.line = root.getLine()
conn.charPositionInLine = root.getCharPositionInLine()
else:
conn.line = id_token[0].getLine()
conn.charPositionInLine = id_token[0].getCharPositionInLine()
conn.inputString = token_stream(id_token[0]).toString(
id_token[0].getTokenStartIndex(),
id_token[-1].getTokenStopIndex())
for exitp in conn.connect_list:
if exitp != '' and not exitp in nested.state_exitpoints:
errors.append('Exit point {ep} not defined in state {st}'
.format(ep=exitp, st=statename))
......@@ -2013,7 +2040,13 @@ def connect_part(root, parent, context):
for each in terminators:
# Set transition ID, referencing process.transitions
each.next_id = trans_id
return errors, warnings
# Set list of terminators
conn.terminators = list(context.terminators[terms:])
# Report errors with symbol coordinates
if coord:
errors = [[e, [conn.pos_x, conn.pos_y]] for e in errors]
warnings = [[w, [conn.pos_x, conn.pos_y]] for w in warnings]
return conn, errors, warnings
def cif(root):
......@@ -2303,38 +2336,41 @@ def decision(root, parent, context):
def nextstate(root, context):
''' Parse a NEXTSTATE [VIA State_Entry_Point] '''
next_state_id, via = '', None
next_state_id, via, entrypoint = '', None, None
for child in root.getChildren():
if child.type == lexer.ID:
next_state_id = child.text.lower()
next_state_id = child.text
elif child.type == lexer.DASH:
next_state_id = '-'
elif child.type == lexer.VIA:
if next_state_id.strip() != '-':
via = child.getChild(0).text.lower()
via = get_input_string(root).replace(
'NEXTSTATE', '', 1).strip()
entrypoint = child.getChild(0).text
try:
composite, = (comp for comp in context.composite_states
if comp.statename == next_state_id)
if comp.statename.lower()
== next_state_id.lower())
except ValueError:
raise TypeError('State {} is not a composite state'
.format(next_state_id))
else:
if via not in composite.state_entrypoints:
if entrypoint.lower() not in composite.state_entrypoints:
raise TypeError('State {s} has no "{p}" entrypoint'
.format(s=next_state_id, p=via))
.format(s=next_state_id, p=entrypoint))
for each in composite.content.named_start:
if each.inputString == via + '_START':
if each.inputString == entrypoint.lower() + '_START':
break
else:
raise TypeError('Entrypoint {p} in state {s} is '
'declared but not defined'.format
(s=next_state_id, p=via))
(s=next_state_id, p=entrypoint))
else:
raise TypeError('"History" NEXTSTATE'
' cannot have a "via" clause')
else:
raise TypeError('NEXTSTATE undefined construct')
return next_state_id, via
return next_state_id, via, entrypoint
def terminator_statement(root, parent, context):
......@@ -2357,7 +2393,7 @@ def terminator_statement(root, parent, context):
elif term.type == lexer.NEXTSTATE:
t.kind = 'next_state'
try:
t.inputString, t.via = nextstate(term, context)
t.inputString, t.via, t.entrypoint = nextstate(term, context)
except TypeError as err:
errors.append(str(err))
t.line = term.getChild(0).getLine()
......@@ -2839,7 +2875,7 @@ def parseSingleElement(elem='', string=''):
'terminator_statement', 'label', 'task', 'procedure_call', 'end',
'text_area', 'state', 'start', 'procedure', 'floating_label',
'connect_part'))
LOG.debug('Parsing string: ' + string)
LOG.debug('Parsing string: ' + string + 'with elem ' + elem)
parser = parser_init(string=string)
parser_ptr = getattr(parser, elem)
assert(parser_ptr is not None)
......
......@@ -24,6 +24,8 @@ import re
import code
import pprint
from functools import partial
from collections import deque
from itertools import chain
# Added to please py2exe - NOQA makes flake8 ignore the following lines:
# pylint: disable=W0611
......@@ -50,10 +52,11 @@ from PySide.QtCore import Qt, QSize, QFile, QIODevice, QRectF, QTimer
from PySide.QtUiTools import QUiLoader
from PySide import QtSvg
from genericSymbols import Symbol, Comment, EditableText, Cornergrabber
from genericSymbols import(Symbol, Comment, EditableText, Cornergrabber,
Connection)
from sdlSymbols import(Input, Output, Decision, DecisionAnswer, Task,
ProcedureCall, TextSymbol, State, Start, Join, Label, Procedure,
ProcedureStart, ProcedureStop, StateStart)
ProcedureStart, ProcedureStop, StateStart, Connect)
# Icons and png files generated from the resource file:
import icons # NOQA
......@@ -122,14 +125,15 @@ G_SYMBOLS = set()
# Lookup table used to configure the context-dependent toolbars
ACTIONS = {
'process': [Start, State, Input, Task, Decision, DecisionAnswer,
'process': [Start, State, Input, Connect, Task, Decision, DecisionAnswer,
Output, ProcedureCall, TextSymbol, Comment, Label,
Join, Procedure],
'procedure': [ProcedureStart, Task, Decision,
DecisionAnswer, Output, ProcedureCall, TextSymbol,
Comment, Label, Join, ProcedureStop],
'statechart': [],
'state': [StateStart, State, Input, Task, Decision, DecisionAnswer, Output,
'state': [StateStart, State, Input, Connect, Task, Decision,
DecisionAnswer, Output,
ProcedureCall, TextSymbol, Comment, Label, Join, ProcedureStop]
}
......@@ -231,9 +235,9 @@ class Sdl_toolbar(QtGui.QToolBar, object):
# Check for singletons (e.g. START symbol)
try:
for item in scene.items():
for item in scene.visible_symb:
try:
if item.is_singleton and item.isVisible():
if item.is_singleton: # and item.isVisible():
self.actions[
item.__class__.__name__].setEnabled(False)
except (AttributeError, KeyError) as error:
......@@ -289,10 +293,63 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
# Selection rectangle when user clicks on the scene and moves mouse
self.select_rect = None
@property
def visible_symb(self):
''' Return the visible items of a scene '''
return (it for it in self.items() if it.isVisible() and not
isinstance(it, (Cornergrabber, Connection, EditableText)))
@property
def states(self):
''' Return visible state components of the scene '''
return (it for it in self.visible_symb if isinstance(it, State))
@property
def texts(self):
''' Return visible text areas components of the scene '''
return (it for it in self.visible_symb if isinstance(it, TextSymbol))
@property
def procs(self):
''' Return visible procedure declaration components of the scene '''
return (it for it in self.visible_symb if isinstance(it, Procedure))
@property
def start(self):
''' Return visible start components of the scene '''
return (it for it in self.visible_symb if isinstance(it, Start))
@property
def floating_labels(self):
''' Return visible floating label components of the scene '''
return (it for it in self.visible_symb if isinstance(it, Label) and
not it.hasParent)
@property
def returns(self):
''' Return visible return components of the scene '''
return (it for it in self.visible_symb if isinstance(it,
ProcedureStop))
@property
def composite_states(self):
''' Return states that contain a composite part '''
return (it for it in self.states if it.is_composite())
def quit_scene(self):
''' Called in case of scene switch (e.g. UP button) '''
pass
def render_process(self, process):
''' Render a process and its inner procedures in the scene '''
self.process_name = process.processName or 'opengeode'
......@@ -309,6 +366,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
G_SYMBOLS.add(sub_top_level)
top_level.nested_scene = subscene
def refresh(self):
''' Refresh the symbols and connections in the scene '''
for symbol in self.items():
......@@ -544,34 +602,23 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
self.undo_stack.endMacro()
self.refresh()
def get_pr_string(self):
''' Parse the graphical items and returns a PR string '''
pr_data = ['PROCESS ' + self.process_name + ';']
# Separate the text boxes and START symbol from the states
# (They need to be placed at the top of the .pr file
items = [item for item in self.items() if item.isVisible()]
states = (item for item in items if isinstance(item, State))
texts = (item for item in items if isinstance(item, TextSymbol))
procs = (item for item in items if isinstance(item, Procedure))
start = [item for item in items if isinstance(item, Start)]
labels = (item for item in items if isinstance(item, Label) and
not item.hasParent)
for item in texts:
pr_data.append(repr(item))
for item in procs:
pr_data = deque()
for item in chain(self.texts, self.procs, self.start):
pr_data.append(repr(item))
try:
start, = start
pr_data.append(repr(start))
except ValueError:
LOG.debug('START Symbol missing')
for item in labels:
for item in self.floating_labels:
pr_data.append(item.parse_gr())
for item in states:
for item in self.states:
if item.is_composite():
pr_data.appendleft(item.parse_composite_state())
pr_data.append(item.parse_gr())