Skip to content

Commit 6d8067d

Browse files
committed
feat: parse SCXML <invoke> elements
Add InvokeDefinition dataclass to schema with all SCXML invoke attributes (type, src, id, autoforward, namelist, params, content, finalize). Parse <invoke> children in parse_state() including inline <content> SCXML, <param>, and <finalize> elements.
1 parent 8117a5e commit 6d8067d

2 files changed

Lines changed: 69 additions & 0 deletions

File tree

statemachine/io/scxml/parser.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from .schema import HistoryState
1616
from .schema import IfAction
1717
from .schema import IfBranch
18+
from .schema import InvokeDefinition
1819
from .schema import LogAction
1920
from .schema import Param
2021
from .schema import RaiseAction
@@ -192,6 +193,11 @@ def parse_state( # noqa: C901
192193
child_history_state = parse_history(child_state_elem)
193194
state.history[child_history_state.id] = child_history_state
194195

196+
# Parse invoke elements
197+
for invoke_elem in state_elem.findall("invoke"):
198+
invoke_def = parse_invoke(invoke_elem)
199+
state.invocations.append(invoke_def)
200+
195201
# Parse donedata (only valid on final states)
196202
if is_final:
197203
donedata_elem = state_elem.find("donedata")
@@ -218,6 +224,53 @@ def parse_donedata(element: ET.Element) -> DoneData:
218224
return DoneData(params=params, content_expr=content_expr)
219225

220226

227+
def parse_invoke(element: ET.Element) -> InvokeDefinition:
228+
"""Parse an <invoke> element into an InvokeDefinition."""
229+
type_attr = element.attrib.get("type")
230+
typeexpr = element.attrib.get("typeexpr")
231+
src = element.attrib.get("src")
232+
srcexpr = element.attrib.get("srcexpr")
233+
id_attr = element.attrib.get("id")
234+
idlocation = element.attrib.get("idlocation")
235+
autoforward = element.attrib.get("autoforward", "false").lower() == "true"
236+
namelist = element.attrib.get("namelist")
237+
238+
params = []
239+
content = None
240+
finalize = None
241+
for child in element:
242+
if child.tag == "param":
243+
name = child.attrib["name"]
244+
expr = child.attrib.get("expr")
245+
location = child.attrib.get("location")
246+
params.append(Param(name=name, expr=expr, location=location))
247+
elif child.tag == "content":
248+
# Inline SCXML content: serialize the child <scxml> element back to string
249+
scxml_child = child.find("{http://www.w3.org/2005/07/scxml}scxml")
250+
if scxml_child is None:
251+
scxml_child = child.find("scxml")
252+
if scxml_child is not None:
253+
content = ET.tostring(scxml_child, encoding="unicode")
254+
elif child.text:
255+
content = re.sub(r"\s+", " ", child.text).strip()
256+
elif child.tag == "finalize":
257+
finalize = parse_executable_content(child)
258+
259+
return InvokeDefinition(
260+
type=type_attr,
261+
typeexpr=typeexpr,
262+
src=src,
263+
srcexpr=srcexpr,
264+
id=id_attr,
265+
idlocation=idlocation,
266+
autoforward=autoforward,
267+
namelist=namelist,
268+
params=params,
269+
content=content,
270+
finalize=finalize,
271+
)
272+
273+
221274
def parse_transition(trans_elem: ET.Element, initial: bool = False) -> Transition:
222275
target = trans_elem.get("target")
223276

statemachine/io/scxml/schema.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,21 @@ class DoneData:
114114
content_expr: "str | None" = None
115115

116116

117+
@dataclass
118+
class InvokeDefinition:
119+
type: "str | None" = None
120+
typeexpr: "str | None" = None
121+
src: "str | None" = None
122+
srcexpr: "str | None" = None
123+
id: "str | None" = None
124+
idlocation: "str | None" = None
125+
autoforward: bool = False
126+
namelist: "str | None" = None
127+
params: List[Param] = field(default_factory=list)
128+
content: "str | None" = None
129+
finalize: "ExecutableContent | None" = None
130+
131+
117132
@dataclass
118133
class State:
119134
id: str
@@ -126,6 +141,7 @@ class State:
126141
states: Dict[str, "State"] = field(default_factory=dict)
127142
history: Dict[str, "HistoryState"] = field(default_factory=dict)
128143
donedata: "DoneData | None" = None
144+
invocations: "List[InvokeDefinition]" = field(default_factory=list)
129145

130146

131147
@dataclass

0 commit comments

Comments
 (0)