-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathQmParser.py
More file actions
executable file
·189 lines (145 loc) · 6.13 KB
/
QmParser.py
File metadata and controls
executable file
·189 lines (145 loc) · 6.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#!/usr/bin/env python3
# -----------------------------------------------------------------------
# QmParser.py
#
#
# -----------------------------------------------------------------------
# mypy: ignore-errors
from copy import deepcopy
import xmiModelApi
import flattenstatemachine as flatt
import qmlib
from anytree import Node
from xmiModelApi import XmiModel
from lxml.etree import _ElementTree
ElementTreeType = _ElementTree
class UniqueNumberGenerator:
def __init__(self):
self.generator = self.unique_number_generator() # Create a generator instance
def unique_number_generator(self):
counter = 0
while True:
yield counter
counter += 1
def get_unique_number(self):
return next(self.generator) # Return the next unique number
# -------------------------------------------------------------------------
# parseTrans
#
# Recursively parse a qm transition and populate the xmiModel
# --------------------------------------------------------------------------
def parseTrans(qmTrans: ElementTreeType,
xmiModel: XmiModel,
xmiNode: Node,
number_gen: UniqueNumberGenerator):
source = xmiNode.id
target = None
if (qmTrans.get("target") != "None"):
target = int(qmTrans.get('target')) if qmTrans.get('target') else None
kind = qmTrans.get('kind')
guard = qmlib.pick_guard(qmTrans)
action = flatt.pick_action(qmTrans)
event = qmTrans.get('trig')
if target is None:
choices = qmTrans.findall("choice")
if len(choices) == 2:
psId = number_gen.get_unique_number()
psNode = xmiModel.addPsuedostate(psId)
xmiModel.addTransition(source, psId, event, guard, action, kind)
for choice in choices:
parseTrans(choice, xmiModel, psNode, number_gen)
else:
xmiModel.addTransition(source, target, event, guard, action, kind)
else:
xmiModel.addTransition(source, target, event, guard, action, kind, xmiNode.parent)
# -------------------------------------------------------------------------
# parseStateTree
#
# Recursively parse the qm model and populate the xmiModel
# --------------------------------------------------------------------------
def parseStateTree(qmRoot: ElementTreeType,
xmiModel: XmiModel,
xmiNode: Node,
number_gen: UniqueNumberGenerator):
for init in qmRoot.findall("initial"):
psNode = xmiModel.addPsuedostate(number_gen.get_unique_number(), xmiNode)
parseTrans(init, xmiModel, psNode, number_gen)
for tran in qmRoot.findall("tran"):
parseTrans(tran, xmiModel, xmiNode, number_gen)
for state in qmRoot.findall("state"):
stateName = state.get('name')
entry = flatt.pick_entry(state)
exit = flatt.pick_exit(state)
state_id = int(state.get('id'))
thisNode = xmiModel.addState(stateName, xmiNode, entry, exit, state_id)
parseStateTree(state, xmiModel, thisNode, number_gen)
# -------------------------------------------------------------------------
# populateXmiModel
#
# Recursively parse the qm model and populate the xmiModel
# --------------------------------------------------------------------------
def populateXmiModel(qmRoot: ElementTreeType,
smname: str) -> XmiModel:
qmFix = fixQMThing(qmRoot)
number_gen = UniqueNumberGenerator()
xmiModel = xmiModelApi.XmiModel(smname + "Package", smname)
# Add a unique ID to every state
states = qmFix.iter("state")
for state in states:
state.set("id", str(number_gen.get_unique_number()))
# Replace the relative target attributes with ID's
for node in qmFix.iter():
target = node.get("target")
if target is not None:
targetId = flatt.state_from_target(node).get("id")
node.set("target", str(targetId))
# Search for internal transitions. Internal transitions do not have a
# target and do not have an option child. Mark the transition as internal
for tran in qmFix.iter("tran"):
if tran.get('target') is None:
# Look for choice nodes
choices = list(tran.iter("choice"))
if len(choices) == 0:
tran.set("kind", "internal")
parseStateTree(qmFix, xmiModel, xmiModel.tree, number_gen)
return xmiModel
# -----------------------------------------------------------------------
# fixQMThing
#
# The QM modeling tool does not support guards on transitions.
# These are specified in the xml file as a transition to a single
# choice.
#
# This routine makes a fix where it looks for transitions with a single
# choice and then adds the children directly under the transition,
# essentilly moving everything up.
# -----------------------------------------------------------------------
def fixQMThing(qmRoot: ElementTreeType) -> ElementTreeType:
qmFix = deepcopy(qmRoot)
for tran in qmFix.iter("tran"):
choices = tran.findall("choice")
if len(choices) == 1:
choice_children = list(choices[0])
for child in choice_children:
tran.append(child)
# Remove the <choice> node after moving its children
# But first check if the choice had a target attribute
target = choices[0].get('target')
if target is not None:
tran.set('target', target)
tran.remove(choices[0])
# Look for all the targets and move them up as well.
for child in list(tran.iter()):
target = child.get('target')
if target is not None:
child.set('target', target[3:])
return qmFix
# -----------------------------------------------------------------------
# getXmiModel
#
# Process the input qmRoot and return an xmiModel
# -----------------------------------------------------------------------
def getXmiModel(qmRoot: ElementTreeType) -> XmiModel:
smRoot, smname = qmlib.get_state_machine(qmRoot)
xmiModel = populateXmiModel(smRoot, smname)
return xmiModel