Browse Source

The raising of an event is interpreted as an "Action" by the runtime. Big-step Maximality and Event Lifeline tests working.

Joeri Exelmans 5 years ago
parent
commit
fc9539e6d3
26 changed files with 162 additions and 95 deletions
  1. 0 3
      src/sccd/runtime/grammar.g
  2. 1 1
      src/sccd/runtime/semantic_options.py
  3. 15 9
      src/sccd/runtime/statechart_instance.py
  4. 17 16
      src/sccd/runtime/statechart_syntax.py
  5. 86 23
      src/sccd/runtime/xml_loader.py
  6. 2 2
      test/test_files/semantics/big_step_maximality/00_take_one.xml
  7. 1 1
      test/test_files/semantics/event_lifeline/00_take_one_next_small_step.xml
  8. 1 1
      test/test_files/semantics/event_lifeline/02_take_one_queue.xml
  9. 1 1
      test/test_files/semantics/event_lifeline/10_take_many_next_small_step.xml
  10. 1 1
      test/test_files/semantics/event_lifeline/11_take_many_next_combo_step.xml
  11. 1 1
      test/test_files/semantics/event_lifeline/12_take_many_queue.xml
  12. 1 1
      test/test_files/semantics/event_lifeline/20_orthogonal_take_one_next_small_step.xml
  13. 1 1
      test/test_files/semantics/event_lifeline/21_orthogonal_take_one_queue.xml
  14. 1 1
      test/test_files/semantics/event_lifeline/30_orthogonal_take_many_next_small_step.xml
  15. 1 1
      test/test_files/semantics/event_lifeline/31_orthogonal_take_many_next_combo_step.xml
  16. 1 1
      test/test_files/semantics/event_lifeline/32_orthogonal_take_many_queue.xml
  17. 6 6
      test/test_files/semantics/original_semantics/enter_exit_hierarchy.xml
  18. 2 2
      test/test_files/semantics/original_semantics/history.xml
  19. 1 1
      test/test_files/semantics/original_semantics/inner_first.xml
  20. 1 1
      test/test_files/semantics/original_semantics/outer_first.xml
  21. 5 5
      test/test_files/semantics/original_semantics/parallel.xml
  22. 6 6
      test/test_files/semantics/original_semantics/parallel_history.xml
  23. 4 4
      test/test_files/semantics/original_semantics/parallel_history_2.xml
  24. 4 4
      test/test_files/semantics/original_semantics/parallel_history_3.xml
  25. 1 1
      test/test_files/semantics/priority/10_source_parent_history.xml
  26. 1 1
      test/test_files/semantics/priority/11_source_child_history.xml

+ 0 - 3
src/sccd/runtime/grammar.g

@@ -10,9 +10,6 @@ PARENT_NODE: ".."
 CURRENT_NODE: "." 
 IDENTIFIER: /[A-Za-z_][A-Za-z_0-9]*/ 
 
-// this rule isn't used but is required
-start : target_expr
-
 // target of a transition
 target_expr: _path | "(" _path ("," _path)+ ")" 
 

+ 1 - 1
src/sccd/runtime/semantic_options.py

@@ -29,7 +29,7 @@ class Concurrency(Enum):
   MANY = 1
 
 @dataclass
-class SemanticOptions:
+class SemanticConfiguration:
   big_step_maximality: BigStepMaximality = BigStepMaximality.TAKE_MANY
   combo_step_maximality: ComboStepMaximality = ComboStepMaximality.COMBO_TAKE_ONE
   internal_event_lifeline: InternalEventLifeline = InternalEventLifeline.NEXT_COMBO_STEP

+ 15 - 9
src/sccd/runtime/statechart_instance.py

@@ -5,7 +5,7 @@ from enum import Enum
 from sccd.runtime.infinity import INFINITY
 from sccd.runtime.event_queue import Timestamp
 from sccd.runtime.statechart_syntax import *
-from sccd.runtime.event import Event, OutputEvent, Instance, InstancesTarget
+from sccd.runtime.event import *
 from sccd.runtime.semantic_options import *
 from sccd.runtime.debug import print_debug
 from collections import Counter
@@ -44,8 +44,7 @@ class StatechartInstance(Instance):
         for state in states:
             print_debug(termcolor.colored('  ENTER %s'%state.name, 'green'))
             self.eventless_states += state.has_eventless_transitions
-            if state.enter:
-                state.enter(self)
+            self._perform_actions(state.enter)
         stable = not self.eventless_states
         print_debug(termcolor.colored('completed initialization', 'red'))
         return (stable, self._big_step.output_events)
@@ -166,8 +165,7 @@ class StatechartInstance(Instance):
             print_debug(termcolor.colored('  EXIT %s' % s.name, 'green'))
             self.eventless_states -= s.has_eventless_transitions
             # execute exit action(s)
-            if s.exit:
-                s.exit(self)
+            self._perform_actions(s.exit)
             self.configuration_bitmap &= ~2**s.state_id
         
         # combo state changed area
@@ -175,8 +173,7 @@ class StatechartInstance(Instance):
         self._combo_step.changed_bitmap |= t.lca.descendant_bitmap
         
         # execute transition action(s)
-        if t.action:
-            t.action(self, t.enabled_event.parameters if t.enabled_event else [])
+        self._perform_actions(t.actions)
             
         # enter states...
         targets = __getEffectiveTargetStates()
@@ -186,8 +183,7 @@ class StatechartInstance(Instance):
             self.eventless_states += s.has_eventless_transitions
             self.configuration_bitmap |= 2**s.state_id
             # execute enter action(s)
-            if s.enter:
-                s.enter(self)
+            self._perform_actions(s.enter)
         try:
             self.configuration = self.config_mem[self.configuration_bitmap]
         except:
@@ -247,6 +243,16 @@ class StatechartInstance(Instance):
                     t.enabled_event = event
                     return True
 
+    def _perform_actions(self, actions: List[Action]):
+        for a in actions:
+            if isinstance(a, RaiseInternalEvent):
+                self._raiseInternalEvent(Event(name=a.name, port="", parameters=[]))
+            elif isinstance(a, RaiseOutputEvent):
+                self._big_step.addOutputEvent(
+                    OutputEvent(Event(name=a.name, port=a.outport, parameters=[]),
+                    OutputPortTarget(a.outport),
+                    a.time_offset))
+
     def _raiseInternalEvent(self, event):
         if self.model.semantics.internal_event_lifeline == InternalEventLifeline.NEXT_SMALL_STEP:
             self._small_step.addNextEvent(event)

+ 17 - 16
src/sccd/runtime/statechart_syntax.py

@@ -3,6 +3,10 @@ from typing import *
 from sccd.runtime.event_queue import Timestamp
 from sccd.compiler.utils import FormattedWriter
 
+@dataclass
+class Action:
+    pass
+
 class State:
     def __init__(self, short_name):
         self.short_name = short_name
@@ -12,8 +16,8 @@ class State:
         self.children = []
         self.default_state = None
         self.transitions = []
-        self.enter = None
-        self.exit = None
+        self.enter: List[Action] = []
+        self.exit: List[Action] = []
         self.history = [] # list of history states that are children
 
         # optimization stuff
@@ -66,10 +70,10 @@ class State:
     def addTransition(self, transition):
         self.transitions.append(transition)
         
-    def setEnter(self, enter):
+    def setEnter(self, enter: List[Action]):
         self.enter = enter
         
-    def setExit(self, exit):
+    def setExit(self, exit: List[Action]):
         self.exit = exit
                     
     def __repr__(self):
@@ -116,25 +120,25 @@ class ParallelState(State):
         return targets
 
 @dataclass
-class Target:
-    expr: str
-    targets: List[State] = field(default_factory=list)
+class Trigger:
+    name: str
+    port: str
 
 class Transition:
-    def __init__(self, source, target: Target):
+    def __init__(self, source, targets: List[State]):
         self.guard = None
-        self.action = None
-        self.trigger = None
+        self.actions: List[Action] = []
+        self.trigger: Optional[Trigger] = None
         self.source = source
-        self.targets = target
+        self.targets = targets
         self.enabled_event = None # the event that enabled this transition
         self.optimize()
                     
     def setGuard(self, guard):
         self.guard = guard
         
-    def setAction(self, action):
-        self.action = action
+    def setActions(self, actions):
+        self.actions = actions
     
     def setTrigger(self, trigger):
         self.trigger = trigger
@@ -162,9 +166,6 @@ class Transition:
 class Expression:
     pass
 
-@dataclass
-class Action:
-    pass
 
 @dataclass
 class RaiseEvent(Action):

+ 86 - 23
src/sccd/runtime/xml_loader.py

@@ -6,7 +6,7 @@ from lark import Lark
 import sccd.compiler
 from sccd.runtime.statechart_syntax import *
 from sccd.runtime.event import Event
-from sccd.runtime.semantic_options import *
+from sccd.runtime.semantic_options import SemanticConfiguration
 
 schema_path = os.path.join(
   os.path.dirname(sccd.compiler.__file__),
@@ -15,14 +15,16 @@ schema_path = os.path.join(
 schema = ET.XMLSchema(ET.parse(schema_path))
 
 grammar = open(os.path.join(os.path.dirname(os.path.abspath(__file__)),"grammar.g"))
-l = Lark(grammar)
+l = Lark(grammar, parser="lalr", start=["target_expr"])
 
+
+# Some types immitating the types that are produced by the compiler
 @dataclass
 class Statechart:
   _class: Any
   root: State
   states: Dict[str, State]
-  semantics: SemanticOptions
+  semantics: SemanticConfiguration
 
 @dataclass
 class Class:
@@ -36,11 +38,19 @@ class Model:
   classes: Dict[str, Any]
   default_class: str
 
+@dataclass
+class InputEvent:
+  name: str
+  port: str
+  parameters: List[Any]
+  time_offset: Timestamp
+
 @dataclass
 class Test:
-  input_events: List[Any]
+  input_events: List[InputEvent]
   expected_events: List[Event]
 
+
 def load_model(src_file) -> Tuple[Model, Optional[Test]]:
   tree = ET.parse(src_file)
   schema.assertValid(tree)
@@ -52,29 +62,36 @@ def load_model(src_file) -> Tuple[Model, Optional[Test]]:
   for c in classes:
     class_name = c.get("name")
     default = c.get("default", "")
+
     scxml_node = c.find("scxml", root.nsmap)
     root_state, states = load_tree(scxml_node)
+
     # Semantics - We use reflection to find the xml attribute names and values
-    semantics = SemanticOptions()
-    for aspect in dataclasses.fields(SemanticOptions):
+    semantics = SemanticConfiguration()
+    for aspect in dataclasses.fields(SemanticConfiguration):
       key = scxml_node.get(aspect.name)
       if key is not None:
         value = aspect.type[key.upper()]
         setattr(semantics, aspect.name, value)
-    print(semantics)
+
     class_ = Class(class_name, None)
     statechart = Statechart(class_, root_state, states, semantics)
     class_.statechart = statechart
+
     model.classes[class_name] = lambda: class_
     if default:
       model.default_class = class_name
 
-  transitions = root.findall(".//transition", root.nsmap)
-  for t in transitions:
-    port = t.get("port", "")
-    if port != "" and port not in model.outports:
-      print("found port", port)
-      model.outports.append(port)
+  def find_ports(element_path, collection):
+    elements = root.findall(element_path, root.nsmap)
+    for e in elements:
+      port = e.get("port")
+      if port != None and port not in collection:
+        collection.append(port)
+  # Any 'port' attribute of a <transition> element is an input port
+  find_ports(".//transition", model.inports)
+  # Any 'port' attribute of a <raise> element is an output port
+  find_ports(".//raise", model.outports)
 
   test = None
   test_node = root.find(".//test", root.nsmap)
@@ -82,8 +99,12 @@ def load_model(src_file) -> Tuple[Model, Optional[Test]]:
     input_events = []
     expected_events = []
     input_node = test_node.find("input", root.nsmap)
-    if input_node:
-      pass
+    if input_node is not None:
+      for event_node in input_node:
+        name = event_node.get("name")
+        port = event_node.get("port")
+        time = int(event_node.get("time"))
+        input_events.append(InputEvent(name, port, [], time))
     slots = test_node.findall("expected/slot", root.nsmap)
     for s in slots:
       slot = []
@@ -98,14 +119,14 @@ def load_model(src_file) -> Tuple[Model, Optional[Test]]:
 
   return (model, test)
 
+class InvalidTag(Exception):
+  pass
 
 def load_tree(scxml_node) -> Tuple[State, Dict[str, State]]:
 
   states: Dict[str, State] = {}
   transitions: List[Tuple[Any, State]] = [] # List of (<transition>, State) tuples
 
-  class InvalidTag(Exception):
-    pass
 
   # Recursively create state hierarchy from XML node
   # Adding <transition> elements to the 'transitions' list as a side effect
@@ -134,13 +155,24 @@ def load_tree(scxml_node) -> Tuple[State, Dict[str, State]]:
         if child.short_name == initial:
           state.default_state = child
       except InvalidTag:
-        pass
+        pass # skip non-state tags
 
     if not initial and len(state.children) == 1:
         state.default_state = state.children[0]
 
     for xml_t in xml_node.findall("transition", xml_node.nsmap):
       transitions.append((xml_t, state))
+
+    # Parse enter/exit actions
+    def _get_enter_exit(tag, setter):
+      node = xml_node.find(tag, xml_node.nsmap)
+      if node is not None:
+        actions = load_actions(node)
+        setter(actions)
+
+    _get_enter_exit("onentry", state.setEnter)
+    _get_enter_exit("onexit", state.setExit)
+
     return state
 
   # First build a state tree
@@ -149,9 +181,9 @@ def load_tree(scxml_node) -> Tuple[State, Dict[str, State]]:
 
   # Add transitions
   for xml_t, source in transitions:
+    # Parse and find target state
     target_string = xml_t.get("target", "")
     parse_tree = l.parse(target_string, start="target_expr")
-
     def find_state(sequence) -> State:
       if sequence.data == "relative_path":
         el = source
@@ -165,13 +197,44 @@ def load_tree(scxml_node) -> Tuple[State, Dict[str, State]]:
         elif item.type == "IDENTIFIER":
           el = [x for x in el.children if x.short_name == item.value][0]
       return el
-
     targets = [find_state(seq) for seq in parse_tree.children]
+
     transition = Transition(source, targets)
+
+    # Trigger
+    event = xml_t.get("event")
+    port = xml_t.get("port")
+    trigger = None if event == None else Trigger(event, port)
+    transition.setTrigger(trigger)
+    # Actions
+    actions = load_actions(xml_t)
+    transition.setActions(actions)
     # todo: set guard
-    # todo: set trigger
-    transition.setTrigger(None)
-    # todo: set actions
+
     source.addTransition(transition)
 
   return (root, states)
+
+def load_action(action_node) -> Optional[Action]:
+  tag = ET.QName(action_node).localname
+  if tag == "raise":
+    event = action_node.get("event")
+    port = action_node.get("port")
+    if not port:
+      return RaiseInternalEvent(name=event, parameters=[])
+    else:
+      return RaiseOutputEvent(name=event, parameters=[], outport=port, time_offset=0)
+  else:
+    raise InvalidTag()
+
+# parent_node: XML node containing 0 or more action nodes as direct children
+def load_actions(parent_node) -> List[Action]:
+  actions = []
+  for node in parent_node:
+    try:
+      a = load_action(node)
+      if a:
+        actions.append(a)
+    except InvalidTag:
+      pass # skip non-action tags
+  return actions

+ 2 - 2
test/test_files/semantics/big_step_maximality/00_take_one.xml

@@ -15,13 +15,13 @@
 				<onentry>
 					<raise event="entered_a" port="out"/>
 				</onentry>
-				<transition target="../b"/>
+				<transition target="/b"/>
 			</state>
 			<state id="b">
 				<onentry>
 					<raise event="entered_b" port="out"/>
 				</onentry>
-				<transition target="../c"/>
+				<transition target="/c"/>
 			</state>
 			<state id="c">
 				<onentry>

+ 1 - 1
test/test_files/semantics/event_lifeline/00_take_one_next_small_step.xml

@@ -33,7 +33,7 @@
 	</class>
 	<test>
 		<input>
-			<event name="e" port="in" time="0.0"/>
+			<event name="e" port="in" time="0"/>
 		</input>
 		<expected>
 			<slot>

+ 1 - 1
test/test_files/semantics/event_lifeline/02_take_one_queue.xml

@@ -33,7 +33,7 @@
 	</class>
 	<test>
 		<input>
-			<event name="e" port="in" time="0.0"/>
+			<event name="e" port="in" time="0"/>
 		</input>
 		<expected>
 			<slot>

+ 1 - 1
test/test_files/semantics/event_lifeline/10_take_many_next_small_step.xml

@@ -33,7 +33,7 @@
 	</class>
 	<test>
 		<input>
-			<event name="e" port="in" time="0.0"/>
+			<event name="e" port="in" time="0"/>
 		</input>
 		<expected>
 			<slot>

+ 1 - 1
test/test_files/semantics/event_lifeline/11_take_many_next_combo_step.xml

@@ -33,7 +33,7 @@
 	</class>
 	<test>
 		<input>
-			<event name="e" port="in" time="0.0"/>
+			<event name="e" port="in" time="0"/>
 		</input>
 		<expected>
 			<slot>

+ 1 - 1
test/test_files/semantics/event_lifeline/12_take_many_queue.xml

@@ -33,7 +33,7 @@
 	</class>
 	<test>
 		<input>
-			<event name="e" port="in" time="0.0"/>
+			<event name="e" port="in" time="0"/>
 		</input>
 		<expected>
 			<slot>

+ 1 - 1
test/test_files/semantics/event_lifeline/20_orthogonal_take_one_next_small_step.xml

@@ -55,7 +55,7 @@
 	</class>
 	<test>
 		<input>
-			<event name="e" port="in" time="0.0"/>
+			<event name="e" port="in" time="0"/>
 		</input>
 		<expected>
 			<slot>

+ 1 - 1
test/test_files/semantics/event_lifeline/21_orthogonal_take_one_queue.xml

@@ -53,7 +53,7 @@
 	</class>
 	<test>
 		<input>
-			<event name="e" port="in" time="0.0"/>
+			<event name="e" port="in" time="0"/>
 		</input>
 		<expected>
 			<slot>

+ 1 - 1
test/test_files/semantics/event_lifeline/30_orthogonal_take_many_next_small_step.xml

@@ -57,7 +57,7 @@
 	</class>
 	<test>
 		<input>
-			<event name="e" port="in" time="0.0"/>
+			<event name="e" port="in" time="0"/>
 		</input>
 		<expected>
 			<slot>

+ 1 - 1
test/test_files/semantics/event_lifeline/31_orthogonal_take_many_next_combo_step.xml

@@ -52,7 +52,7 @@
 	</class>
 	<test>
 		<input>
-			<event name="e" port="in" time="0.0"/>
+			<event name="e" port="in" time="0"/>
 		</input>
 		<expected>
 			<slot>

+ 1 - 1
test/test_files/semantics/event_lifeline/32_orthogonal_take_many_queue.xml

@@ -53,7 +53,7 @@
 	</class>
 	<test>
 		<input>
-			<event name="e" port="in" time="0.0"/>
+			<event name="e" port="in" time="0"/>
 		</input>
 		<expected>
 			<slot>

+ 6 - 6
test/test_files/semantics/original_semantics/enter_exit_hierarchy.xml

@@ -77,12 +77,12 @@
   </class>
   <test>
     <input>
-      <event name="to_composite" port="in" time="0.0"/>
-      <event name="to_inner2" port="in" time="0.0"/>
-      <event name="to_outside" port="in" time="0.0"/>
-      <event name="to_inner3" port="in" time="0.0"/>
-      <event name="to_outside" port="in" time="0.0"/>
-      <event name="to_inner4" port="in" time="0.0"/>
+      <event name="to_composite" port="in" time="0"/>
+      <event name="to_inner2" port="in" time="0"/>
+      <event name="to_outside" port="in" time="0"/>
+      <event name="to_inner3" port="in" time="0"/>
+      <event name="to_outside" port="in" time="0"/>
+      <event name="to_inner4" port="in" time="0"/>
     </input>
     
     <expected>

+ 2 - 2
test/test_files/semantics/original_semantics/history.xml

@@ -38,8 +38,8 @@
     </class>
     <test>
         <input>
-            <event name="to_state_2" port="in" time="0.0"/>
-            <event name="to_state_3" port="in" time="0.0"/>
+            <event name="to_state_2" port="in" time="0"/>
+            <event name="to_state_3" port="in" time="0"/>
         </input>
         <expected>
             <slot>

+ 1 - 1
test/test_files/semantics/original_semantics/inner_first.xml

@@ -30,7 +30,7 @@
     </class>
     <test>
         <input>
-            <event name="event" port="test_input" time="0.0"/>
+            <event name="event" port="test_input" time="0"/>
         </input>
        <expected>
            <slot>

+ 1 - 1
test/test_files/semantics/original_semantics/outer_first.xml

@@ -30,7 +30,7 @@
     </class>
     <test>
         <input>
-            <event name="event" port="test_input" time="0.0"/>
+            <event name="event" port="test_input" time="0"/>
         </input>
        <expected>
            <slot>

+ 5 - 5
test/test_files/semantics/original_semantics/parallel.xml

@@ -44,11 +44,11 @@
     </class>
     <test>
         <input>
-            <event name="to_state_2" port="test_input" time="0.0"/>
-            <event name="to_state_4" port="test_input" time="0.0"/>
-            <event name="to_state_1" port="test_input" time="0.0"/>
-            <event name="to_state_2" port="test_input" time="0.0"/>
-            <event name="to_state_3" port="test_input" time="0.0"/>
+            <event name="to_state_2" port="test_input" time="0"/>
+            <event name="to_state_4" port="test_input" time="0"/>
+            <event name="to_state_1" port="test_input" time="0"/>
+            <event name="to_state_2" port="test_input" time="0"/>
+            <event name="to_state_3" port="test_input" time="0"/>
         </input>
         <expected>
             <slot>

+ 6 - 6
test/test_files/semantics/original_semantics/parallel_history.xml

@@ -66,12 +66,12 @@
     </class>
     <test>
         <input>
-            <event name="to_state_2" port="test_input" time="0.0"/>
-            <event name="to_state_4" port="test_input" time="0.0"/>
-            <event name="to_outer_1" port="test_input" time="0.0"/>
-            <event name="to_outer_2" port="test_input" time="0.0"/>
-            <event name="to_history_1" port="test_input" time="0.0"/>
-            <event name="to_history_2" port="test_input" time="0.0"/>
+            <event name="to_state_2" port="test_input" time="0"/>
+            <event name="to_state_4" port="test_input" time="0"/>
+            <event name="to_outer_1" port="test_input" time="0"/>
+            <event name="to_outer_2" port="test_input" time="0"/>
+            <event name="to_history_1" port="test_input" time="0"/>
+            <event name="to_history_2" port="test_input" time="0"/>
         </input>
        <expected>
            <slot>

+ 4 - 4
test/test_files/semantics/original_semantics/parallel_history_2.xml

@@ -67,10 +67,10 @@
     </class>
     <test>
         <input>
-            <event name="to_outer_1" port="test_input" time="0.0"/>
-            <event name="to_outer_2" port="test_input" time="0.0"/>
-            <event name="exit" port="test_input" time="0.0"/>
-            <event name="to_history_1" port="test_input" time="0.0"/>
+            <event name="to_outer_1" port="test_input" time="0"/>
+            <event name="to_outer_2" port="test_input" time="0"/>
+            <event name="exit" port="test_input" time="0"/>
+            <event name="to_history_1" port="test_input" time="0"/>
         </input>
        <expected>
            <slot>

+ 4 - 4
test/test_files/semantics/original_semantics/parallel_history_3.xml

@@ -67,10 +67,10 @@
     </class>
     <test>
         <input>
-            <event name="to_outer_1" port="test_input" time="0.0"/>
-            <event name="to_outer_2" port="test_input" time="0.0"/>
-            <event name="exit" port="test_input" time="0.0"/>
-            <event name="to_history_1" port="test_input" time="0.0"/>
+            <event name="to_outer_1" port="test_input" time="0"/>
+            <event name="to_outer_2" port="test_input" time="0"/>
+            <event name="exit" port="test_input" time="0"/>
+            <event name="to_history_1" port="test_input" time="0"/>
         </input>
        <expected>
            <slot>

+ 1 - 1
test/test_files/semantics/priority/10_source_parent_history.xml

@@ -25,7 +25,7 @@
 	</class>
 	<test>
 		<input>
-			<event name="e" port="in" time="0.0"/>
+			<event name="e" port="in" time="0"/>
 		</input>
 		<expected>
 			<slot>

+ 1 - 1
test/test_files/semantics/priority/11_source_child_history.xml

@@ -25,7 +25,7 @@
 	</class>
 	<test>
 		<input>
-			<event name="e" port="in" time="0.0"/>
+			<event name="e" port="in" time="0"/>
 		</input>
 		<expected>
 			<slot>