瀏覽代碼

Fix durations for input events. Fix renderer.

Joeri Exelmans 5 年之前
父節點
當前提交
767a2afbc8

+ 41 - 52
src/sccd/controller/controller.py

@@ -12,7 +12,7 @@ class InputEvent:
   name: str
   port: str
   parameters: List[Any]
-  time_offset: Timestamp
+  time_offset: Duration
 
 # The Controller class is a primitive that can be used to build backends of any kind:
 # Threads, integration with existing event loop, game loop, test framework, ...
@@ -33,20 +33,29 @@ class Controller:
         self.initialized = False
 
         self.model.globals.assert_ready()
-        print_debug("model delta is %s" % str(self.model.globals.delta))
+        # print_debug("model delta is %s" % str(self.model.globals.delta))
 
-    # time_offset: the offset relative to the current simulated time
-    # (the timestamp given in the last call to run_until)
     def add_input(self, input: InputEvent):
             if input.name == "":
                 raise Exception("Input event can't have an empty name.")
         
-            if input.port not in self.model.globals.inports:
-                raise Exception("No such port: '" + input.port + "'")
-
+            try:
+                self.model.globals.inports.get_id(input.port)
+            except KeyError:
+                raise Exception("No such port: '%s'" % input.port)
+
+            try:
+                event_id = self.model.globals.events.get_id(input.name)
+            except KeyError:
+                raise Exception("No such event: '%s'" % input.name)
+
+            if self.model.globals.delta == duration(0):
+                offset = 0
+            else:
+                offset = input.time_offset // self.model.globals.delta
 
             e = Event(
-                id=self.model.globals.get_event_id(input.name),
+                id=event_id,
                 name=input.name,
                 port=input.port,
                 parameters=input.parameters)
@@ -54,7 +63,7 @@ class Controller:
             # For now, add events received on input ports to all instances.
             # In the future, we can optimize this by keeping a mapping from port name to a list of instances
             # potentially responding to the event
-            self.queue.add(self.simulated_time+input.time_offset,
+            self.queue.add(self.simulated_time+offset,
                 Controller.EventQueueEntry(e, self.object_manager.instances))
 
     # Get timestamp of next entry in event queue
@@ -63,7 +72,7 @@ class Controller:
 
     # Returns duration since start
     def get_simulated_duration(self) -> Duration:
-        return (self.model.globals.delta * self.simulated_time).normalize()
+        return (self.model.globals.delta * self.simulated_time)
 
     # Run until the event queue has no more due events wrt given timestamp and until all instances are stable.
     # If no timestamp is given (now = None), run until event queue is empty.
@@ -76,7 +85,8 @@ class Controller:
             pipe_events = []
             for e in events:
                 if isinstance(e.target, InstancesTarget):
-                    self.queue.add(self.simulated_time+e.time_offset, Controller.EventQueueEntry(e.event, e.target.instances))
+                    offset = e.time_offset // self.model.globals.delta
+                    self.queue.add(self.simulated_time + offset, Controller.EventQueueEntry(e.event, e.target.instances))
                 elif isinstance(e.target, OutputPortTarget):
                     assert (e.time_offset == 0) # cannot combine 'after' with 'output port'
                     pipe_events.append(e.event)
@@ -85,31 +95,9 @@ class Controller:
             if pipe_events:
                 pipe.put(pipe_events, block=True, timeout=None)
 
-        # Helper. Let all unstable instances execute big steps until they are stable
-        def do_stabilize():
-            while unstable:
-                for i in reversed(range(len(unstable))):
-                    instance = unstable[i]
-                    stable, output = instance.big_step(self.simulated_time, [])
-                    process_big_step_output(output)
-                    if stable:
-                        del unstable[i]
-                try:
-                    interrupt.get_nowait()
-                    return False # interrupted
-                except queue.Empty:
-                    pass
-            else:
-                # already stable
-                return True
-            print_debug("all instances stabilized.")
-            return True
-
-
         if not self.initialized:
             self.initialized = True
 
-            print_debug("time is now %s" % str(self.get_simulated_duration()))
             # first run...
             # initialize the object manager, in turn initializing our default class
             # and adding the generated events to the queue
@@ -118,6 +106,7 @@ class Controller:
                 process_big_step_output(events)
                 if not stable:
                     unstable.append(i)
+            print_debug("initialized. time is now %s" % str(self.get_simulated_duration()))
 
 
         # Actual "event loop"
@@ -126,26 +115,26 @@ class Controller:
         # Should we only stabilize when there are no more events?
         # Should we never stabilize?
         # Should this be a semantic option?
-        while unstable or self.queue.is_due(now):
+        # while unstable or self.queue.is_due(now):
             # 1. Handle events
-            for timestamp, entry in self.queue.due(now):
-                # check if there's a time leap
-                if timestamp is not self.simulated_time:
-                    # before every "time leap", continue to run instances until they are stable.
-                    if not do_stabilize():
-                        return
-                    # make time leap
-                    self.simulated_time = timestamp
-                    print_debug("\ntime is now %s" % str(self.get_simulated_duration()))
-                # run all instances for whom there are events
-                for instance in entry.targets:
-                    stable, output = instance.big_step(timestamp, [entry.event])
-                    # print_debug("completed big step (time = %s)" % str(self.model.globals.delta * self.simulated_time))
-                    process_big_step_output(output)
-                    if not stable:
-                        unstable.append(instance)
+        for timestamp, entry in self.queue.due(now):
+            # check if there's a time leap
+            if timestamp is not self.simulated_time:
+                # before every "time leap", continue to run instances until they are stable.
+                # if not do_stabilize():
+                    # return
+                # make time leap
+                self.simulated_time = timestamp
+                print_debug("\ntime is now %s" % str(self.get_simulated_duration()))
+            # run all instances for whom there are events
+            for instance in entry.targets:
+                stable, output = instance.big_step(timestamp, [entry.event])
+                # print_debug("completed big step (time = %s)" % str(self.model.globals.delta * self.simulated_time))
+                process_big_step_output(output)
+                # if not stable:
+                    # unstable.append(instance)
             # 2. No more due events -> stabilize
-            if not do_stabilize():
-                return
+            # if not do_stabilize():
+                # return
 
         self.simulated_time = now

+ 2 - 1
src/sccd/execution/event.py

@@ -2,6 +2,7 @@ import termcolor
 from dataclasses import *
 from abc import *
 from typing import *
+from sccd.util.duration import *
 
 # A raised event.
 @dataclass(frozen=True)
@@ -32,7 +33,7 @@ class EventTarget(ABC):
 
 # A raised output event with a target and a time offset.
 class OutputEvent:
-    def __init__(self, event: Event, target: EventTarget, time_offset = 0):
+    def __init__(self, event: Event, target: EventTarget, time_offset: Duration = duration(0)):
         self.event = event
         self.target = target
         self.time_offset = time_offset

+ 1 - 1
src/sccd/execution/statechart_state.py

@@ -130,7 +130,7 @@ class StatechartState:
 
   def _start_timers(self, triggers: List[AfterTrigger]):
       for after in triggers:
-          delay: int = after.delay.eval([], self.data_model)
+          delay: Duration = after.delay.eval([], self.data_model)
           self.output.append(OutputEvent(
               Event(id=after.id, name=after.name, parameters=[after.nextTimerId()]),
               target=InstancesTarget([self.instance]),

+ 19 - 14
src/sccd/model/globals.py

@@ -1,44 +1,49 @@
 # from dataclasses import *
+import termcolor
 from typing import *
 from sccd.syntax.expression import *
 from sccd.util.namespace import *
 from sccd.util.duration import *
+from sccd.util.debug import *
 
 # @dataclass
 class Globals:
   # max_delta: upper bound on model delta
-  def __init__(self, fixed_delta: Optional[Duration] = Duration(100, Microsecond)):
+  def __init__(self, fixed_delta: Optional[Duration] = duration(100, Microsecond)):
     self.fixed_delta = fixed_delta
 
     self.events = Namespace()
     self.inports = Namespace()
     self.outports = Namespace()
-    self.durations: List[DurationLiteral] = []
+    self.durations: List[Duration] = []
 
     # The smallest unit for all durations in the model.
     # Calculated after all expressions have been parsed, based on all DurationLiterals.
     self.delta: Optional[Duration] = None
 
-  # Convert all DurationLiterals to model delta
-  def _conv(self):
-    for d in self.durations:
-      # The following error is impossible: (i think)
-      # if d.original % self.delta != Duration(0):
-      #   raise Exception("Duration %s cannot be represented by delta %s" % (str(d.original), str(self.delta)))
-      d.converted = d.original // self.delta
-
   def process_durations(self):
-    self.delta = gcd(*(d.original for d in self.durations))
+    self.delta = gcd(*self.durations)
+
+    # self.delta will be duration(0) now if there are no durations in the model, or all durations are 0.
 
     # Ensure delta not too big
     if self.fixed_delta:
-      if self.delta < self.fixed_delta:
+      if duration(0) < self.delta < self.fixed_delta:
         raise Exception("Model contains duration deltas (smallest = %s) not representable with fixed delta of %s." % (str(self.delta), str(self.fixed_delta)))
       else:
         self.delta = self.fixed_delta
 
-    self._conv()
+    if self.delta == duration(0):
+      print_debug(termcolor.colored("Warning: model delta is 0: Model does not have any notion of time.", 'yellow'))
+    else:
+      pass
+      # # Convert all DurationLiterals to model delta
+      # for d in self.durations:
+      #   # The following error is impossible: (i think)
+      #   # if d.original % self.delta != duration(0):
+      #   #   raise Exception("Duration %s cannot be represented by delta %s" % (str(d.original), str(self.delta)))
+      #   d.converted = d.original // self.delta
 
   def assert_ready(self):
     if self.delta is None:
-      raise Exception("Globals not ready: durations not yet processed.")
+      raise Exception("Globals not ready: durations not yet processed.")

+ 16 - 3
src/sccd/parser/expression_parser.py

@@ -58,8 +58,16 @@ class _ExpressionTransformer(Transformer):
   def assignment(self, node):
     return Assignment(node[0], node[1].value, node[2])
 
+  def duration_literal(self, node):
+    return DurationLiteral(node[0])
+
   def duration(self, node):
+    val = int(node[0])
+    suffix = node[1]
+
     unit = {
+      "d": None, # 'd' stands for "duration", the non-unit for all zero-durations.
+                 # need this to parse zero-duration as a duration instead of int.
       "fs": FemtoSecond,
       "ps": PicoSecond,
       "ns": Nanosecond,
@@ -68,15 +76,16 @@ class _ExpressionTransformer(Transformer):
       "s": Second,
       "m": Minute,
       "h": Hour
-    }[node[0].children[1]]
-    d = DurationLiteral(Duration(int(node[0].children[0]),unit))
+    }[suffix]
+
+    d = duration(val, unit)
     self.globals.durations.append(d)
     return d
 
 # Global variables so we don't have to rebuild our parser every time
 # Obviously not thread-safe
 _transformer = _ExpressionTransformer()
-_action_lang_parser = Lark(_action_lang_grammar, parser="lalr", start=["expr", "block"], transformer=_transformer)
+_action_lang_parser = Lark(_action_lang_grammar, parser="lalr", start=["expr", "block", "duration"], transformer=_transformer)
 _state_ref_parser = Lark(_state_ref_grammar, parser="lalr", start=["state_ref"])
 
 # Exported functions:
@@ -86,6 +95,10 @@ def parse_expression(globals: Globals, datamodel, expr: str) -> Expression:
   _transformer.datamodel = datamodel
   return _action_lang_parser.parse(expr, start="expr")
 
+def parse_duration(globals: Globals, expr:str) -> Duration:
+  _transformer.globals = globals
+  return _action_lang_parser.parse(expr, start="duration")
+
 def parse_block(globals: Globals, datamodel, block: str) -> Statement:
   _transformer.globals = globals
   _transformer.datamodel = datamodel

+ 5 - 3
src/sccd/parser/grammar/action_language.g

@@ -51,7 +51,7 @@ array: "[" (expr ("," expr)*)? "]"
 ?literal: ESCAPED_STRING -> string
         | INT -> int
         | bool_literal -> bool
-        | duration_literal -> duration
+        | duration -> duration_literal
 
 ?compare_operator: EQ | NEQ | GT | GEQ | LT | LEQ
 ?add_operator: PLUS | MINUS
@@ -81,9 +81,9 @@ FALSE: "False"
 
 INT: /[0-9]+/
 
-?duration_literal: (INT duration_unit)+
+duration: (INT duration_unit)+
 
-?duration_unit: TIME_H | TIME_M | TIME_S | TIME_MS | TIME_US | TIME_NS | TIME_PS | TIME_FS
+?duration_unit: TIME_H | TIME_M | TIME_S | TIME_MS | TIME_US | TIME_NS | TIME_PS | TIME_FS | TIME_D
 
 TIME_H: "h"
 TIME_M: "m"
@@ -94,6 +94,8 @@ TIME_NS: "ns"
 TIME_PS: "ps"
 TIME_FS: "fs"
 
+TIME_D: "d" // for zero-duration
+
 
 // Statement parsing
 

+ 96 - 55
src/sccd/parser/statechart_parser.py

@@ -12,22 +12,49 @@ class XmlLoadError(Exception):
     parent = el.getparent()
     if parent is None:
       parent = el
-    # el = parent
+
     lines = etree.tostring(parent).decode('utf-8').strip().split('\n')
-    nbr_lines = len(etree.tostring(el).decode('utf-8').strip().split('\n'))
-    lines_numbers = []
-    l = parent.sourceline
-    for line in lines:
-      ll = ("%4d: " % l) + line
-      if l >= el.sourceline and l < el.sourceline + nbr_lines:
+    nbr_highlighted_lines = len(etree.tostring(el).decode('utf-8').strip().split('\n'))
+    text = []
+
+    parent_firstline = parent.sourceline
+    parent_lastline = parent.sourceline + len(lines) - 1
+
+    el_firstline = el.sourceline
+    el_lastline = el.sourceline + nbr_highlighted_lines - 1
+
+    numbered_lines = list(zip(range(parent.sourceline, parent.sourceline + len(lines)), lines))
+
+    from_line = max(parent_firstline, el_firstline - 5)
+    to_line = min(parent_lastline, el_lastline + 5)
+
+    if from_line == parent_firstline+1:
+      from_line = parent_firstline
+    if to_line == parent_lastline-1:
+      to_line = parent_lastline
+
+    def f(tup):
+      return from_line <= tup[0] <= to_line
+
+    if from_line != parent_firstline:
+      text.append("%4d: %s" % (parent_firstline, lines[0]))
+      text.append("     ...")
+
+    for linenumber, line in filter(f, numbered_lines):
+      ll = "%4d: %s" % (linenumber, line)
+      if el_firstline <= linenumber <= el_lastline:
         ll = termcolor.colored(ll, 'yellow')
-      lines_numbers.append(ll)
-      l += 1
-    super().__init__("\n\n%s\n\n%s:\nline %d: <%s>: %s" % ('\n'.join(lines_numbers), src_file,el.sourceline, el.tag, str(err)))
+      text.append(ll)
+
+    if to_line != parent_lastline:
+      text.append("     ...")
+      text.append("%4d: %s" % (parent_lastline, lines[-1]))
+
+    super().__init__("\n\n%s\n\n%s:\nline %d: <%s>: %s" % ('\n'.join(text), src_file,el.sourceline, el.tag, str(err)))
     
-    self.src_file = src_file
-    self.el = el
-    self.err = err
+    # self.src_file = src_file
+    # self.el = el
+    # self.err = err
 
 
 class XmlParser:
@@ -74,7 +101,7 @@ class XmlParser:
             start_method(el)
 
         elif event == "end":
-          end_method = getattr(self, "end_"+el.tag)
+          end_method = getattr(self, "end_"+el.tag, None)
           if end_method:
             end_method(el)
 
@@ -214,6 +241,9 @@ class StateParser(ActionParser):
     self.state.require().exit = actions
 
   def start_transition(self, el):
+    if self.state.require().parent is None:
+      raise Exception("Root <state> cannot be source of a transition.")
+
     self.actions.push([])
 
   def end_transition(self, el):
@@ -226,50 +256,12 @@ class StateParser(ActionParser):
     transitions.append((el, source, actions))
 
 # Parses <statechart> element and all its children.
-class StatechartParser(StateParser):
+class TreeParser(StateParser):
 
   def __init__(self):
     super().__init__()
     self.statechart = XmlParser.Context("statechart")
-    self.statecharts = XmlParser.Context("statecharts")
-
-  # <semantics>
 
-  def _internal_end_semantics(self, el):
-    statechart = self.statechart.require()
-    # Use reflection to find the possible XML attributes and their values
-    for aspect in dataclasses.fields(Semantics):
-      key = el.get(aspect.name)
-      if key is not None:
-        if key == "*":
-          setattr(statechart.semantics, aspect.name, None)
-        else:
-          value = aspect.type[key.upper()]
-          setattr(statechart.semantics, aspect.name, value)
-
-  def end_semantics(self, el):
-    self._internal_end_semantics(el)
-
-  def end_override_semantics(self, el):
-    self._internal_end_semantics(el)
-
-  # <datamodel>
-
-  def end_var(self, el):
-    globals = self.globals.require()
-    datamodel = self.datamodel.require()
-
-    id = el.get("id")
-    expr = el.get("expr")
-    parsed = parse_expression(globals, datamodel, expr=expr)
-    datamodel.create(id, parsed.eval([], datamodel))
-
-  def start_datamodel(self, el):
-    statechart = self.statechart.require()
-    self.datamodel.push(statechart.datamodel)
-
-  def end_datamodel(self, el):
-    self.datamodel.pop()
 
   # <tree>
 
@@ -353,15 +345,63 @@ class StatechartParser(StateParser):
 
     statechart.tree = StateTree(root)
 
+class StatechartParser(TreeParser):
+
+  def __init__(self, load_external = True):
+    super().__init__()
+    self.load_external = load_external
+
+    self.statecharts = XmlParser.Context("statecharts")
+
+  # <semantics>
+
+  def _internal_end_semantics(self, el):
+    statechart = self.statechart.require()
+    # Use reflection to find the possible XML attributes and their values
+    for aspect in dataclasses.fields(Semantics):
+      key = el.get(aspect.name)
+      if key is not None:
+        if key == "*":
+          setattr(statechart.semantics, aspect.name, None)
+        else:
+          value = aspect.type[key.upper()]
+          setattr(statechart.semantics, aspect.name, value)
+
+  def end_semantics(self, el):
+    self._internal_end_semantics(el)
+
+  def end_override_semantics(self, el):
+    if self.load_external:
+      self._internal_end_semantics(el)
+
+  # <datamodel>
+
+  def end_var(self, el):
+    globals = self.globals.require()
+    datamodel = self.datamodel.require()
+
+    id = el.get("id")
+    expr = el.get("expr")
+    parsed = parse_expression(globals, datamodel, expr=expr)
+    datamodel.create(id, parsed.eval([], datamodel))
+
+  def start_datamodel(self, el):
+    statechart = self.statechart.require()
+    self.datamodel.push(statechart.datamodel)
+
+  def end_datamodel(self, el):
+    self.datamodel.pop()
+
   # <statechart>
 
   def start_statechart(self, el):
     src_file = self.src_file.require()
     ext_file = el.get("src")
+    statechart = None
     if ext_file is None:
       statechart = Statechart(
         tree=None, semantics=Semantics(), datamodel=DataModel())
-    else:
+    elif self.load_external:
       ext_file_path = os.path.join(os.path.dirname(src_file), ext_file)
       self.statecharts.push([])
       self.parse(ext_file_path)
@@ -374,4 +414,5 @@ class StatechartParser(StateParser):
   def end_statechart(self, el):
     statecharts = self.statecharts.require()
     sc = self.statechart.pop()
-    statecharts.append(sc)
+    if sc is not None:
+      statecharts.append(sc)

+ 3 - 7
src/sccd/syntax/expression.py

@@ -112,17 +112,13 @@ class BoolLiteral(Expression):
 
 @dataclass
 class DurationLiteral(Expression):
-    original: Duration
-
-    # All duration expressions in a model evaluate to a duration with the same unit.
-    # What remains is a just an integer in  that unit.
-    converted: Optional[int] = None
+    d: Duration
 
     def eval(self, events, datamodel):
-        return self.converted
+        return self.d
 
     def render(self):
-        return self.original.__str__()
+        return str(self.d)
 
     def get_static_type(self) -> type:
         return int

+ 12 - 10
src/sccd/syntax/tree.py

@@ -17,7 +17,7 @@ class State:
     enter: List[Action] = field(default_factory=list)
     exit: List[Action] = field(default_factory=list)
 
-    gen: Optional['StateGenerated'] = None
+    gen: Optional['StateOptimization'] = None
 
     def __post_init__(self):
         if self.parent is not None:
@@ -34,7 +34,7 @@ class State:
 
 # Generated fields (for optimization) of a state
 @dataclass(frozen=True)
-class StateGenerated:
+class StateOptimization:
     state_id: int
     full_name: str
     ancestors: List[State] # order: close to far away, i.e. first element is parent
@@ -120,14 +120,14 @@ class Transition:
     actions: List[Action] = field(default_factory=list)
     trigger: Optional[Trigger] = None
 
-    gen: Optional['TransitionGenerated'] = None        
+    gen: Optional['TransitionOptimization'] = None        
                     
     def __repr__(self):
         return termcolor.colored("%s 🡪 %s" % (self.source.gen.full_name, self.targets[0].gen.full_name), 'green')
 
 # Generated fields (for optimization) of a transition
 @dataclass(frozen=True)
-class TransitionGenerated:
+class TransitionOptimization:
     lca: State
     arena_bitmap: Bitmap
 
@@ -143,7 +143,7 @@ class StateTree:
 
         next_id = 0
 
-        def init_tree(state: State, parent_full_name: str, ancestors: List[State]):
+        def init_state(state: State, parent_full_name: str, ancestors: List[State]):
             nonlocal next_id
 
             state_id = next_id
@@ -172,7 +172,7 @@ class StateTree:
                     after_triggers.append(t.trigger)
 
             for c in state.children:
-                init_tree(c, full_name, [state] + ancestors)
+                init_state(c, full_name, [state] + ancestors)
                 if isinstance(c, HistoryState):
                     history.append(c)
 
@@ -180,7 +180,7 @@ class StateTree:
             for c in state.children:
                 descendants.extend(c.gen.descendants)
 
-            state.gen = StateGenerated(
+            state.gen = StateOptimization(
                 state_id=state_id,
                 full_name=full_name,
                 ancestors=ancestors,
@@ -190,9 +190,11 @@ class StateTree:
                 has_eventless_transitions=has_eventless_transitions,
                 after_triggers=after_triggers)
 
-        init_tree(root, "", [])
-        self.root = root
+            # print("state:", full_name)
+            # print("ancestors:", len(ancestors))
 
+        init_state(root, "", [])
+        self.root = root
 
         def init_transition(t: Transition):
             # the least-common ancestor can be computed statically
@@ -207,7 +209,7 @@ class StateTree:
                             lca = a
                             break
 
-            t.gen = TransitionGenerated(
+            t.gen = TransitionOptimization(
                 lca=lca,
                 arena_bitmap=lca.gen.descendant_bitmap.set(lca.gen.state_id))
 

+ 0 - 0
src/sccd/test/__init__.py


+ 91 - 60
src/sccd/util/duration.py

@@ -1,11 +1,12 @@
 from enum import *
+from abc import *
 from dataclasses import *
 from typing import *
 import math
 import functools
 
 @dataclass
-class Unit:
+class _Unit:
   notation: str
   relative_size: int
   larger: Optional[Tuple[Any, int]] = None
@@ -14,15 +15,15 @@ class Unit:
   def __eq__(self, other):
     return self is other
 
-FemtoSecond = Unit("fs", 1)
-PicoSecond = Unit("ps", 1000)
-Nanosecond = Unit("ns", 1000000)
-Microsecond = Unit("µs", 1000000000)
-Millisecond = Unit("ms", 1000000000000)
-Second = Unit("s", 1000000000000000)
-Minute = Unit("m", 60000000000000000)
-Hour = Unit("h", 3600000000000000000)
-Day = Unit("D", 86400000000000000000)
+FemtoSecond = _Unit("fs", 1)
+PicoSecond = _Unit("ps", 1000)
+Nanosecond = _Unit("ns", 1000000)
+Microsecond = _Unit("µs", 1000000000)
+Millisecond = _Unit("ms", 1000000000000)
+Second = _Unit("s", 1000000000000000)
+Minute = _Unit("m", 60000000000000000)
+Hour = _Unit("h", 3600000000000000000)
+Day = _Unit("D", 86400000000000000000)
 
 FemtoSecond.larger = (PicoSecond, 1000)
 PicoSecond.larger = (Nanosecond, 1000)
@@ -42,23 +43,75 @@ Hour.larger = (Day, 24)
 # Nanosecond.smaller = (PicoSecond, 1000)
 # PicoSecond.smaller = (FemtoSecond, 1000)
 
+class Duration(ABC):
+  def __repr__(self):
+    return "Duration("+self.__str__()+")"
 
-# @dataclass
-class Duration:
-  def __init__(self, val: int, unit: Unit = None):
-    self.val = val
-    self.unit = unit
+  @abstractmethod
+  def __str__(self):
+    pass
+
+  @abstractmethod
+  def __eq__(self):
+    pass
+
+  @abstractmethod
+  def __mul__(self):
+    pass
+
+  def __floordiv__(self, other: 'Duration') -> int:
+    if other is _zero:
+      raise ZeroDivisionError("duration floordiv by zero duration")
+    self_conv, other_conv, _ = _same_unit(self, other)
+    return self_conv // other_conv
+
+  def __mod__(self, other):
+      self_conv, other_conv, unit = _same_unit(self, other)
+      new_val = self_conv % other_conv
+      if new_val == 0:
+        return _zero
+      else:
+        return _NonZeroDuration(new_val, unit)
+
+  def __lt__(self, other):
+    self_conv, other_conv = _same_unit(self, other)
+    return self_conv.val < other_conv.val
+
+class _ZeroDuration(Duration):
+  def _convert(self, unit: _Unit) -> int:
+    return 0
 
-    if self.val != 0 and self.unit is None:
+  def __str__(self):
+    return '0'
+
+  def __eq__(self, other):
+    return self is other
+
+  def __mul__(self, other: int) -> Duration:
+    return self
+
+  # Commutativity
+  __rmul__ = __mul__
+
+_zero = _ZeroDuration() # Singleton. Only place the constructor should be called.
+
+def duration(val: int, unit: Optional[_Unit] = None) -> Duration:
+  if unit is None:
+    if val != 0:
       raise Exception("Duration: Non-zero value should have unit")
-    # Zero-durations are treated a bit special
-    if self.val == 0 and self.unit is not None:
+    else:
+      return _zero
+  else:
+    if val == 0:
       raise Exception("Duration: Zero value should not have unit")
+    else:
+      return _NonZeroDuration(val, unit)
 
-    # Convert Duration to the largest possible unit without losing accuracy.
-
-    if self.unit is None:
-      return
+# @dataclass
+class _NonZeroDuration(Duration):
+  def __init__(self, val: int, unit: _Unit = None):
+    self.val = val
+    self.unit = unit
 
     while self.unit.larger:
       next_unit, factor = self.unit.larger
@@ -69,58 +122,36 @@ class Duration:
 
   # Can only convert to smaller units.
   # Returns new Duration.
-  def _convert(self, unit: Unit) -> int:
-    if self.unit is None:
-      return 0
-
+  def _convert(self, unit: _Unit) -> int:
     # Precondition
     assert self.unit.relative_size >= unit.relative_size
     factor = self.unit.relative_size // unit.relative_size
     return self.val * factor
 
   def __str__(self):
-    if self.unit is None:
-      return '0'
     return str(self.val)+' '+self.unit.notation
 
-  def __repr__(self):
-    return "Duration("+self.__str__()+")"
 
   def __eq__(self, other):
+    if isinstance(other, _ZeroDuration):
+      return False
     return self.val == other.val and self.unit is other.unit
 
-  def __mul__(self, other: int):
-    new_val = self.val * other
-    if new_val == 0:
-      return Duration(0)
-    else:
-      return Duration(new_val, self.unit)
+  def __mul__(self, other: int) -> Duration:
+    if other == 0:
+      return _zero
+    return _NonZeroDuration(self.val * other, self.unit)
 
   # Commutativity
   __rmul__ = __mul__
 
-  def __floordiv__(self, other: 'Duration'):
-    if other.val == 0:
-      raise ZeroDivisionError("duration floordiv by zero duration")
-    self_conv, other_conv, _ = _same_unit(self, other)
-    return self_conv // other_conv
-
-  def __mod__(self, other):
-      self_conv, other_conv, unit = _same_unit(self, other)
-      new_val = self_conv % other_conv
-      if new_val == 0:
-        return Duration(0)
-      else:
-        return Duration(new_val, unit)
-
-  def __lt__(self, other):
-    self_conv, other_conv = _same_unit(self, other)
-    return self_conv.val < other_conv.val
-
-def _same_unit(x: Duration, y: Duration) -> Tuple[int, int, Unit]:
-  if x.unit is None:
-    return (0, y.val, y.unit)
-  if y.unit is None:
+def _same_unit(x: Duration, y: Duration) -> Tuple[int, int, _Unit]:
+  if x is _zero:
+    if y is _zero:
+      return (0, 0, None)
+    else:
+      return (0, y.val, y.unit)
+  if y is _zero:
     return (x.val, 0, x.unit)
 
   if x.unit.relative_size >= y.unit.relative_size:
@@ -136,7 +167,7 @@ def _same_unit(x: Duration, y: Duration) -> Tuple[int, int, Unit]:
 def gcd_pair(x: Duration, y: Duration) -> Duration:
   x_conv, y_conv, unit = _same_unit(x, y)
   gcd = math.gcd(x_conv, y_conv)
-  return Duration(gcd, unit)
+  return duration(gcd, unit)
 
 def gcd(*iterable: Iterable[Duration]) -> Duration:
-  return functools.reduce(gcd_pair, iterable, Duration(0))
+  return functools.reduce(gcd_pair, iterable, _zero)

+ 31 - 31
src/sccd/util/test_duration.py

@@ -5,9 +5,9 @@ class TestDuration(unittest.TestCase):
 
   def test_equal(self):
     # The same amount of time, but objects not considered equal.
-    x = Duration(1000, Millisecond)
-    y = Duration(1, Second)
-    z = Duration(3, Day)
+    x = duration(1000, Millisecond)
+    y = duration(1, Second)
+    z = duration(3, Day)
 
     self.assertEqual(x, y)
     self.assertEqual(y, x)
@@ -15,70 +15,70 @@ class TestDuration(unittest.TestCase):
     self.assertNotEqual(x, z)
 
   def test_gcd(self):
-    x = Duration(20, Second)
-    y = Duration(100, Microsecond)
+    x = duration(20, Second)
+    y = duration(100, Microsecond)
 
     u = gcd(x, y)
     v = gcd(y, x)
 
-    self.assertEqual(u, Duration(100, Microsecond))
-    self.assertEqual(v, Duration(100, Microsecond))
+    self.assertEqual(u, duration(100, Microsecond))
+    self.assertEqual(v, duration(100, Microsecond))
 
   def test_gcd_zero(self):
-    x = Duration(0)
-    y = Duration(100, Microsecond)
+    x = duration(0)
+    y = duration(100, Microsecond)
 
     u = gcd(x, y)
     v = gcd(y, x)
 
-    self.assertEqual(u, Duration(100, Microsecond))
-    self.assertEqual(v, Duration(100, Microsecond))
+    self.assertEqual(u, duration(100, Microsecond))
+    self.assertEqual(v, duration(100, Microsecond))
 
   def test_gcd_many(self):
-    l = [Duration(3, Microsecond), Duration(0), Duration(10, Millisecond)]
+    l = [duration(3, Microsecond), duration(0), duration(10, Millisecond)]
 
     u = gcd(*l)
 
-    self.assertEqual(u, Duration(1, Microsecond))
+    self.assertEqual(u, duration(1, Microsecond))
 
   def test_gcd_few(self):
-    l = [Duration(3, Microsecond)]
+    l = [duration(3, Microsecond)]
 
     u = gcd(*l)
 
-    self.assertEqual(u, Duration(3, Microsecond))
+    self.assertEqual(u, duration(3, Microsecond))
 
   def test_gcd_none(self):
     l = []
 
     u = gcd(*l)
 
-    self.assertEqual(u, Duration(0))
+    self.assertEqual(u, duration(0))
 
   def test_mult(self):
-    x = Duration(10, Millisecond)
+    x = duration(10, Millisecond)
     
-    self.assertEqual(x * 10, Duration(100, Millisecond))
-    self.assertEqual(10 * x, Duration(100, Millisecond))
+    self.assertEqual(x * 10, duration(100, Millisecond))
+    self.assertEqual(10 * x, duration(100, Millisecond))
 
   def test_floordiv(self):
-    x = Duration(100, Millisecond)
-    y = Duration(10, Millisecond)
-    z = Duration(3, Millisecond)
+    x = duration(100, Millisecond)
+    y = duration(10, Millisecond)
+    z = duration(3, Millisecond)
 
     # Duration divided by duration is factor
     self.assertEqual(x // y, 10)
     self.assertEqual(y // x, 0)
     self.assertEqual(x // z, 33)
 
-    self.assertRaises(ZeroDivisionError, lambda: x // Duration(0))
+    self.assertRaises(ZeroDivisionError, lambda: x // duration(0))
 
   def test_mod(self):
-    x = Duration(100, Millisecond)
-    y = Duration(10, Microsecond)
-    z = Duration(1, Second)
-    i = Duration(3, Millisecond)
-
-    self.assertEqual(x % y, Duration(0))
-    self.assertEqual(x % z, Duration(100, Millisecond))
-    self.assertEqual(x % i, Duration(1, Millisecond))
+    x = duration(100, Millisecond)
+    y = duration(10, Microsecond)
+    z = duration(1, Second)
+    i = duration(3, Millisecond)
+
+    self.assertEqual(x % y, duration(0))
+    self.assertEqual(x % z, duration(100, Millisecond))
+    self.assertEqual(x % i, duration(1, Millisecond))

+ 10 - 0
test/lib/test_parser.py

@@ -1,5 +1,6 @@
 import os
 from sccd.parser.statechart_parser import *
+from sccd.parser.expression_parser import *
 from lib.test import *
 from copy import deepcopy
 
@@ -29,6 +30,15 @@ class TestParser(StatechartParser):
     big_step = self.big_step.pop()
     output.append(big_step)
 
+  def end_input_event(self, el):
+    input = self.test_input.require()
+    globals = self.globals.require()
+    name = el.get("name")
+    port = el.get("port")
+    time = el.get("time")
+    duration = parse_duration(globals, time)
+    input.append(InputEvent(name=name, port=port, parameters=[], time_offset=duration))
+
   def start_input(self, el):
     self.test_input.require()
 

+ 24 - 16
test/render.py

@@ -4,7 +4,7 @@ import subprocess
 import multiprocessing
 from lib.os_tools import *
 from sccd.util.indenting_writer import *
-from sccd.model.xml_loader import *
+from sccd.parser.statechart_parser import *
 import lxml.etree as ET
 
 if __name__ == '__main__':
@@ -29,17 +29,22 @@ if __name__ == '__main__':
             print("Failed to run 'state-machine-cat'. Make sure this application is installed on your system.")
             exit()
     else:
-      print("No input files specified.")      
+      print("No input files specified.")
       print()
       parser.print_usage()
       exit()
 
     def process(src):
-      statechart_node = ET.parse(src).getroot()
-      tree_node = statechart_node.find(".//tree")
-      if tree_node is None:
-        return # no tree here :(
-      tree = load_tree(Globals(), tree_node)
+      parser = StatechartParser(load_external = False)
+      parser.globals.push(Globals(fixed_delta = None))
+      parser.statecharts.push([])
+      parser.parse(src)
+      statecharts = parser.statecharts.pop()
+      if len(statecharts) != 1:
+        return # no statechart here :(
+
+      statechart = statecharts[0]
+      root = statechart.tree.root
 
       target_path = lambda ext: os.path.join(args.output_dir, dropext(src)+ext)
       smcat_target = target_path('.smcat')
@@ -58,8 +63,11 @@ if __name__ == '__main__':
 
       # Used for drawing initial state
       class PseudoState:
+        @dataclass
+        class Gen:
+          full_name: str
         def __init__(self, name):
-          self.name = name
+          self.gen = PseudoState.Gen(name)
       # Used for drawing initial state
       class PseudoTransition:
         def __init__(self, source, targets):
@@ -73,9 +81,9 @@ if __name__ == '__main__':
 
       def write_state(s, hide=False):
         if not hide:
-          w.write(name_to_name(s.name))
+          w.write(name_to_name(s.gen.full_name))
           w.extendWrite(' [label="')
-          w.extendWrite(name_to_label(s.name))
+          w.extendWrite(name_to_label(s.gen.full_name))
           w.extendWrite('"')
           if isinstance(s, ParallelState):
             w.extendWrite(' type=parallel')
@@ -98,8 +106,8 @@ if __name__ == '__main__':
             w.extendWrite(' {')
             w.indent()
           if s.default_state:
-            w.write(name_to_name(s.name)+'_initial [type=initial],')
-            transitions.append(PseudoTransition(source=PseudoState(s.name+'/initial'), targets=[s.default_state]))
+            w.write(name_to_name(s.gen.full_name)+'_initial [type=initial],')
+            transitions.append(PseudoTransition(source=PseudoState(s.gen.full_name+'/initial'), targets=[s.default_state]))
           for i, c in enumerate(s.children):
             write_state(c)
             w.extendWrite(',' if i < len(s.children)-1 else ';')
@@ -108,7 +116,7 @@ if __name__ == '__main__':
             w.write('}')
         transitions.extend(s.transitions)
 
-      write_state(tree.root, hide=True)
+      write_state(root, hide=True)
 
       ctr = 0
       for t in transitions:
@@ -122,17 +130,17 @@ if __name__ == '__main__':
           label += ','.join([r.render() for r in raises])
 
         if len(t.targets) == 1:
-          w.write(name_to_name(t.source.name) + ' -> ' + name_to_name(t.targets[0].name))
+          w.write(name_to_name(t.source.gen.full_name) + ' -> ' + name_to_name(t.targets[0].gen.full_name))
           if label:
             w.extendWrite(': '+label)
           w.extendWrite(';')
         else:
-          w.write(name_to_name(t.source.name) + ' -> ' + ']split'+str(ctr))
+          w.write(name_to_name(t.source.gen.full_name) + ' -> ' + ']split'+str(ctr))
           if label:
               w.extendWrite(': '+label)
           w.extendWrite(';')
           for tt in t.targets:
-            w.write(']split'+str(ctr) + ' -> ' + name_to_name(tt.name))
+            w.write(']split'+str(ctr) + ' -> ' + name_to_name(tt.gen.full_name))
             w.extendWrite(';')
           ctr += 1
 

+ 80 - 0
test/test_files/features/after/test_after.svg

@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.40.1 (20161225.0304)
+ -->
+<!-- Title: state transitions Pages: 1 -->
+<svg width="242pt" height="231pt"
+ viewBox="0.00 0.00 242.00 231.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 227)">
+<title>state transitions</title>
+<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-227 238,-227 238,4 -4,4"/>
+<!-- __initial -->
+<g id="node1" class="node">
+<title>__initial</title>
+<ellipse fill="#000000" stroke="#000000" stroke-width="2" cx="117" cy="-217.5" rx="5.5" ry="5.5"/>
+</g>
+<!-- _s1 -->
+<g id="node2" class="node">
+<title>_s1</title>
+<polygon fill="transparent" stroke="transparent" stroke-width="2" points="145,-184 89,-184 89,-148 145,-148 145,-184"/>
+<text text-anchor="start" x="110.6646" y="-162.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s1</text>
+<path fill="none" stroke="#000000" stroke-width="2" d="M101.3333,-149C101.3333,-149 132.6667,-149 132.6667,-149 138.3333,-149 144,-154.6667 144,-160.3333 144,-160.3333 144,-171.6667 144,-171.6667 144,-177.3333 138.3333,-183 132.6667,-183 132.6667,-183 101.3333,-183 101.3333,-183 95.6667,-183 90,-177.3333 90,-171.6667 90,-171.6667 90,-160.3333 90,-160.3333 90,-154.6667 95.6667,-149 101.3333,-149"/>
+</g>
+<!-- __initial&#45;&gt;_s1 -->
+<g id="edge1" class="edge">
+<title>__initial&#45;&gt;_s1</title>
+<path fill="none" stroke="#000000" d="M117,-211.9886C117,-207.6293 117,-201.1793 117,-194.4801"/>
+<polygon fill="#000000" stroke="#000000" points="120.5001,-194.0122 117,-184.0122 113.5001,-194.0122 120.5001,-194.0122"/>
+<text text-anchor="middle" x="118.3895" y="-195" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
+</g>
+<!-- _s2 -->
+<g id="node3" class="node">
+<title>_s2</title>
+<polygon fill="transparent" stroke="transparent" stroke-width="2" points="106,-120 0,-120 0,-74 106,-74 106,-120"/>
+<text text-anchor="start" x="46.6646" y="-103.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s2</text>
+<text text-anchor="start" x="5.5022" y="-83.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">onentry/ ^out.in_2</text>
+<polygon fill="#000000" stroke="#000000" points="0,-97 0,-97 106,-97 106,-97 0,-97"/>
+<path fill="none" stroke="#000000" stroke-width="2" d="M13,-75C13,-75 93,-75 93,-75 99,-75 105,-81 105,-87 105,-87 105,-107 105,-107 105,-113 99,-119 93,-119 93,-119 13,-119 13,-119 7,-119 1,-113 1,-107 1,-107 1,-87 1,-87 1,-81 7,-75 13,-75"/>
+</g>
+<!-- _s1&#45;&gt;_s2 -->
+<g id="edge2" class="edge">
+<title>_s1&#45;&gt;_s2</title>
+<path fill="none" stroke="#000000" d="M94.3018,-147.7885C91.4944,-145.2608 88.7306,-142.6278 86.215,-140 82.6375,-136.2629 79.0684,-132.1535 75.6683,-128.0028"/>
+<polygon fill="#000000" stroke="#000000" points="78.3304,-125.7267 69.3802,-120.0571 72.8413,-130.0707 78.3304,-125.7267"/>
+<text text-anchor="start" x="86" y="-131" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">after(100 ms) &#160;&#160;</text>
+</g>
+<!-- _s3 -->
+<g id="node4" class="node">
+<title>_s3</title>
+<polygon fill="transparent" stroke="transparent" stroke-width="2" points="234,-120 128,-120 128,-74 234,-74 234,-120"/>
+<text text-anchor="start" x="174.6646" y="-103.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s3</text>
+<text text-anchor="start" x="133.5022" y="-83.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">onentry/ ^out.in_3</text>
+<polygon fill="#000000" stroke="#000000" points="128,-97 128,-97 234,-97 234,-97 128,-97"/>
+<path fill="none" stroke="#000000" stroke-width="2" d="M141,-75C141,-75 221,-75 221,-75 227,-75 233,-81 233,-87 233,-87 233,-107 233,-107 233,-113 227,-119 221,-119 221,-119 141,-119 141,-119 135,-119 129,-113 129,-107 129,-107 129,-87 129,-87 129,-81 135,-75 141,-75"/>
+</g>
+<!-- _s1&#45;&gt;_s3 -->
+<g id="edge3" class="edge">
+<title>_s1&#45;&gt;_s3</title>
+<path fill="none" stroke="#000000" d="M145.1022,-148.8417C148.6296,-146.1072 152.0347,-143.1369 155,-140 158.1875,-136.6281 161.1728,-132.8012 163.9023,-128.8539"/>
+<polygon fill="#000000" stroke="#000000" points="167.0262,-130.4645 169.4484,-120.1502 161.1228,-126.7027 167.0262,-130.4645"/>
+<text text-anchor="start" x="164" y="-131" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">after(200 ms) &#160;&#160;</text>
+</g>
+<!-- _s4 -->
+<g id="node5" class="node">
+<title>_s4</title>
+<polygon fill="transparent" stroke="transparent" stroke-width="2" points="106,-46 0,-46 0,0 106,0 106,-46"/>
+<text text-anchor="start" x="46.6646" y="-29.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s4</text>
+<text text-anchor="start" x="5.5022" y="-9.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">onentry/ ^out.in_4</text>
+<polygon fill="#000000" stroke="#000000" points="0,-23 0,-23 106,-23 106,-23 0,-23"/>
+<path fill="none" stroke="#000000" stroke-width="2" d="M13,-1C13,-1 93,-1 93,-1 99,-1 105,-7 105,-13 105,-13 105,-33 105,-33 105,-39 99,-45 93,-45 93,-45 13,-45 13,-45 7,-45 1,-39 1,-33 1,-33 1,-13 1,-13 1,-7 7,-1 13,-1"/>
+</g>
+<!-- _s2&#45;&gt;_s4 -->
+<g id="edge4" class="edge">
+<title>_s2&#45;&gt;_s4</title>
+<path fill="none" stroke="#000000" d="M53,-73.9916C53,-68.476 53,-62.474 53,-56.5881"/>
+<polygon fill="#000000" stroke="#000000" points="56.5001,-56.249 53,-46.2491 49.5001,-56.2491 56.5001,-56.249"/>
+<text text-anchor="start" x="53" y="-57" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">after(150 ms) &#160;&#160;</text>
+</g>
+</g>
+</svg>

+ 134 - 0
test/test_files/features/after/test_after_reentry.svg

@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.40.1 (20161225.0304)
+ -->
+<!-- Title: state transitions Pages: 1 -->
+<svg width="527pt" height="404pt"
+ viewBox="0.00 0.00 527.00 404.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 400)">
+<title>state transitions</title>
+<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-400 523,-400 523,4 -4,4"/>
+<g id="clust1" class="cluster">
+<title>cluster__p</title>
+<path fill="none" stroke="#000000" stroke-width="2" d="M20,-8C20,-8 499,-8 499,-8 505,-8 511,-14 511,-20 511,-20 511,-345 511,-345 511,-351 505,-357 499,-357 499,-357 20,-357 20,-357 14,-357 8,-351 8,-345 8,-345 8,-20 8,-20 8,-14 14,-8 20,-8"/>
+<text text-anchor="start" x="256.1646" y="-338.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">p</text>
+</g>
+<g id="clust2" class="cluster">
+<title>cluster__p_o0</title>
+<polygon fill="none" stroke="#000000" stroke-dasharray="5,2" points="138,-16 138,-319 503,-319 503,-16 138,-16"/>
+<text text-anchor="start" x="314.3292" y="-300.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">o0</text>
+</g>
+<g id="clust3" class="cluster">
+<title>cluster__p_o1</title>
+<polygon fill="none" stroke="#000000" stroke-dasharray="5,2" points="24,-21 24,-319 130,-319 130,-21 24,-21"/>
+<text text-anchor="start" x="70.8292" y="-300.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">o1</text>
+</g>
+<!-- __initial -->
+<g id="node1" class="node">
+<title>__initial</title>
+<ellipse fill="#000000" stroke="#000000" stroke-width="2" cx="16" cy="-390.5" rx="5.5" ry="5.5"/>
+</g>
+<!-- _p -->
+<!-- __initial&#45;&gt;_p -->
+<g id="edge1" class="edge">
+<title>__initial&#45;&gt;_p</title>
+<path fill="none" stroke="#000000" d="M16,-384.9533C16,-380.7779 16,-374.5043 16,-367.0332"/>
+<polygon fill="#000000" stroke="#000000" points="19.5001,-366.9971 16,-356.9971 12.5001,-366.9972 19.5001,-366.9971"/>
+<text text-anchor="middle" x="17.3895" y="-368" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
+</g>
+<!-- _p_o0 -->
+<!-- _p_o0_initial -->
+<g id="node4" class="node">
+<title>_p_o0_initial</title>
+<ellipse fill="#000000" stroke="#000000" stroke-width="2" cx="380" cy="-275.5" rx="5.5" ry="5.5"/>
+</g>
+<!-- _p_o0_a -->
+<g id="node5" class="node">
+<title>_p_o0_a</title>
+<polygon fill="transparent" stroke="transparent" stroke-width="2" points="408,-188 352,-188 352,-152 408,-152 408,-188"/>
+<text text-anchor="start" x="376.6646" y="-166.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">a</text>
+<path fill="none" stroke="#000000" stroke-width="2" d="M364.3333,-153C364.3333,-153 395.6667,-153 395.6667,-153 401.3333,-153 407,-158.6667 407,-164.3333 407,-164.3333 407,-175.6667 407,-175.6667 407,-181.3333 401.3333,-187 395.6667,-187 395.6667,-187 364.3333,-187 364.3333,-187 358.6667,-187 353,-181.3333 353,-175.6667 353,-175.6667 353,-164.3333 353,-164.3333 353,-158.6667 358.6667,-153 364.3333,-153"/>
+</g>
+<!-- _p_o0_initial&#45;&gt;_p_o0_a -->
+<g id="edge2" class="edge">
+<title>_p_o0_initial&#45;&gt;_p_o0_a</title>
+<path fill="none" stroke="#000000" d="M380,-269.8288C380,-265.1736 380,-258.4097 380,-252.5 380,-252.5 380,-252.5 380,-205.5 380,-203.1079 380,-200.6252 380,-198.1342"/>
+<polygon fill="#000000" stroke="#000000" points="383.5001,-198.0597 380,-188.0598 376.5001,-198.0598 383.5001,-198.0597"/>
+<text text-anchor="middle" x="381.3895" y="-226" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
+</g>
+<!-- _p_o0_b -->
+<g id="node6" class="node">
+<title>_p_o0_b</title>
+<polygon fill="transparent" stroke="transparent" stroke-width="2" points="302,-70 196,-70 196,-24 302,-24 302,-70"/>
+<text text-anchor="start" x="245.6646" y="-53.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">b</text>
+<text text-anchor="start" x="201.5022" y="-33.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">onentry/ ^out.in_b</text>
+<polygon fill="#000000" stroke="#000000" points="196,-47 196,-47 302,-47 302,-47 196,-47"/>
+<path fill="none" stroke="#000000" stroke-width="2" d="M209,-25C209,-25 289,-25 289,-25 295,-25 301,-31 301,-37 301,-37 301,-57 301,-57 301,-63 295,-69 289,-69 289,-69 209,-69 209,-69 203,-69 197,-63 197,-57 197,-57 197,-37 197,-37 197,-31 203,-25 209,-25"/>
+</g>
+<!-- _p_o0_a&#45;&gt;_p_o0_b -->
+<g id="edge3" class="edge">
+<title>_p_o0_a&#45;&gt;_p_o0_b</title>
+<path fill="none" stroke="#000000" d="M351.848,-168.1724C294.1799,-164.0503 168,-152.9258 168,-134.5 168,-134.5 168,-134.5 168,-87.5 168,-74.97 175.85,-66.3164 186.6625,-60.3401"/>
+<polygon fill="#000000" stroke="#000000" points="188.2559,-63.4597 195.8756,-56.0982 185.3283,-57.1012 188.2559,-63.4597"/>
+<text text-anchor="start" x="168" y="-108" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">after(100 ms) [INSTATE([&quot;/p/o1/x&quot;])] &#160;&#160;</text>
+</g>
+<!-- _p_o0_c -->
+<g id="node7" class="node">
+<title>_p_o0_c</title>
+<polygon fill="transparent" stroke="transparent" stroke-width="2" points="480,-70 374,-70 374,-24 480,-24 480,-70"/>
+<text text-anchor="start" x="424" y="-53.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">c</text>
+<text text-anchor="start" x="379.8376" y="-33.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">onentry/ ^out.in_c</text>
+<polygon fill="#000000" stroke="#000000" points="374,-47 374,-47 480,-47 480,-47 374,-47"/>
+<path fill="none" stroke="#000000" stroke-width="2" d="M387,-25C387,-25 467,-25 467,-25 473,-25 479,-31 479,-37 479,-37 479,-57 479,-57 479,-63 473,-69 467,-69 467,-69 387,-69 387,-69 381,-69 375,-63 375,-57 375,-57 375,-37 375,-37 375,-31 381,-25 387,-25"/>
+</g>
+<!-- _p_o0_a&#45;&gt;_p_o0_c -->
+<g id="edge4" class="edge">
+<title>_p_o0_a&#45;&gt;_p_o0_c</title>
+<path fill="none" stroke="#000000" d="M408.1541,-159.659C418.1638,-154.0322 427,-145.8506 427,-134.5 427,-134.5 427,-134.5 427,-87.5 427,-85.127 427,-82.6757 427,-80.2081"/>
+<polygon fill="#000000" stroke="#000000" points="430.5001,-80.1306 427,-70.1306 423.5001,-80.1306 430.5001,-80.1306"/>
+<text text-anchor="start" x="427" y="-108" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">after(150 ms) &#160;&#160;</text>
+</g>
+<!-- _p_o0_b&#45;&gt;_p_o0_a -->
+<g id="edge5" class="edge">
+<title>_p_o0_b&#45;&gt;_p_o0_a</title>
+<path fill="none" stroke="#000000" d="M302.2827,-58.5519C338.5508,-67.1869 380,-78.9301 380,-87.5 380,-134.5 380,-134.5 380,-134.5 380,-136.8921 380,-139.3748 380,-141.8658"/>
+<polygon fill="#000000" stroke="#000000" points="376.5001,-141.9402 380,-151.9402 383.5001,-141.9403 376.5001,-141.9402"/>
+<text text-anchor="middle" x="381.3895" y="-108" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
+</g>
+<!-- _p_o1 -->
+<!-- _p_o1_initial -->
+<g id="node9" class="node">
+<title>_p_o1_initial</title>
+<ellipse fill="#000000" stroke="#000000" stroke-width="2" cx="60" cy="-275.5" rx="5.5" ry="5.5"/>
+</g>
+<!-- _p_o1_x -->
+<g id="node10" class="node">
+<title>_p_o1_x</title>
+<polygon fill="transparent" stroke="transparent" stroke-width="2" points="88,-188 32,-188 32,-152 88,-152 88,-188"/>
+<text text-anchor="start" x="57" y="-166.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">x</text>
+<path fill="none" stroke="#000000" stroke-width="2" d="M44.3333,-153C44.3333,-153 75.6667,-153 75.6667,-153 81.3333,-153 87,-158.6667 87,-164.3333 87,-164.3333 87,-175.6667 87,-175.6667 87,-181.3333 81.3333,-187 75.6667,-187 75.6667,-187 44.3333,-187 44.3333,-187 38.6667,-187 33,-181.3333 33,-175.6667 33,-175.6667 33,-164.3333 33,-164.3333 33,-158.6667 38.6667,-153 44.3333,-153"/>
+</g>
+<!-- _p_o1_initial&#45;&gt;_p_o1_x -->
+<g id="edge6" class="edge">
+<title>_p_o1_initial&#45;&gt;_p_o1_x</title>
+<path fill="none" stroke="#000000" d="M60,-269.8288C60,-265.1736 60,-258.4097 60,-252.5 60,-252.5 60,-252.5 60,-205.5 60,-203.1079 60,-200.6252 60,-198.1342"/>
+<polygon fill="#000000" stroke="#000000" points="63.5001,-198.0597 60,-188.0598 56.5001,-198.0598 63.5001,-198.0597"/>
+<text text-anchor="middle" x="61.3895" y="-226" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
+</g>
+<!-- _p_o1_y -->
+<g id="node11" class="node">
+<title>_p_o1_y</title>
+<polygon fill="transparent" stroke="transparent" stroke-width="2" points="88,-65 32,-65 32,-29 88,-29 88,-65"/>
+<text text-anchor="start" x="57" y="-43.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">y</text>
+<path fill="none" stroke="#000000" stroke-width="2" d="M44.3333,-30C44.3333,-30 75.6667,-30 75.6667,-30 81.3333,-30 87,-35.6667 87,-41.3333 87,-41.3333 87,-52.6667 87,-52.6667 87,-58.3333 81.3333,-64 75.6667,-64 75.6667,-64 44.3333,-64 44.3333,-64 38.6667,-64 33,-58.3333 33,-52.6667 33,-52.6667 33,-41.3333 33,-41.3333 33,-35.6667 38.6667,-30 44.3333,-30"/>
+</g>
+<!-- _p_o1_x&#45;&gt;_p_o1_y -->
+<g id="edge7" class="edge">
+<title>_p_o1_x&#45;&gt;_p_o1_y</title>
+<path fill="none" stroke="#000000" d="M55.7033,-151.6741C54.7416,-146.1833 54,-140.1255 54,-134.5 54,-134.5 54,-134.5 54,-87.5 54,-83.4573 54.2962,-79.2119 54.7569,-75.0534"/>
+<polygon fill="#000000" stroke="#000000" points="58.2353,-75.4511 56.1661,-65.0603 51.3039,-74.4736 58.2353,-75.4511"/>
+<text text-anchor="start" x="54" y="-108" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">after(250 ms) &#160;&#160;</text>
+</g>
+</g>
+</svg>

+ 46 - 0
test/test_files/features/datamodel/test_cond.svg

@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.40.1 (20161225.0304)
+ -->
+<!-- Title: state transitions Pages: 1 -->
+<svg width="121pt" height="147pt"
+ viewBox="0.00 0.00 121.29 147.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 143)">
+<title>state transitions</title>
+<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-143 117.291,-143 117.291,4 -4,4"/>
+<!-- __initial -->
+<g id="node1" class="node">
+<title>__initial</title>
+<ellipse fill="#000000" stroke="#000000" stroke-width="2" cx="28" cy="-133.5" rx="5.5" ry="5.5"/>
+</g>
+<!-- _start -->
+<g id="node2" class="node">
+<title>_start</title>
+<polygon fill="transparent" stroke="transparent" stroke-width="2" points="56,-100 0,-100 0,-64 56,-64 56,-100"/>
+<text text-anchor="start" x="16.3324" y="-78.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">start</text>
+<path fill="none" stroke="#000000" stroke-width="2" d="M12.3333,-65C12.3333,-65 43.6667,-65 43.6667,-65 49.3333,-65 55,-70.6667 55,-76.3333 55,-76.3333 55,-87.6667 55,-87.6667 55,-93.3333 49.3333,-99 43.6667,-99 43.6667,-99 12.3333,-99 12.3333,-99 6.6667,-99 1,-93.3333 1,-87.6667 1,-87.6667 1,-76.3333 1,-76.3333 1,-70.6667 6.6667,-65 12.3333,-65"/>
+</g>
+<!-- __initial&#45;&gt;_start -->
+<g id="edge1" class="edge">
+<title>__initial&#45;&gt;_start</title>
+<path fill="none" stroke="#000000" d="M28,-127.9886C28,-123.6293 28,-117.1793 28,-110.4801"/>
+<polygon fill="#000000" stroke="#000000" points="31.5001,-110.0122 28,-100.0122 24.5001,-110.0122 31.5001,-110.0122"/>
+<text text-anchor="middle" x="29.3895" y="-111" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
+</g>
+<!-- _done -->
+<g id="node3" class="node">
+<title>_done</title>
+<polygon fill="transparent" stroke="transparent" stroke-width="2" points="56,-36 0,-36 0,0 56,0 56,-36"/>
+<text text-anchor="start" x="14.6584" y="-14.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">done</text>
+<path fill="none" stroke="#000000" stroke-width="2" d="M12.3333,-1C12.3333,-1 43.6667,-1 43.6667,-1 49.3333,-1 55,-6.6667 55,-12.3333 55,-12.3333 55,-23.6667 55,-23.6667 55,-29.3333 49.3333,-35 43.6667,-35 43.6667,-35 12.3333,-35 12.3333,-35 6.6667,-35 1,-29.3333 1,-23.6667 1,-23.6667 1,-12.3333 1,-12.3333 1,-6.6667 6.6667,-1 12.3333,-1"/>
+</g>
+<!-- _start&#45;&gt;_done -->
+<g id="edge2" class="edge">
+<title>_start&#45;&gt;_done</title>
+<path fill="none" stroke="#000000" d="M28,-63.8314C28,-58.4728 28,-52.4735 28,-46.6262"/>
+<polygon fill="#000000" stroke="#000000" points="31.5001,-46.4363 28,-36.4363 24.5001,-46.4363 31.5001,-46.4363"/>
+<text text-anchor="start" x="28" y="-47" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">[y == 0]^out.done &#160;&#160;</text>
+</g>
+</g>
+</svg>

+ 59 - 46
test/test_files/features/expressions/test_expressions.svg

@@ -4,11 +4,11 @@
 <!-- Generated by graphviz version 2.40.1 (20161225.0304)
  -->
 <!-- Title: state transitions Pages: 1 -->
-<svg width="216pt" height="1824pt"
- viewBox="0.00 0.00 215.50 1824.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<svg width="242pt" height="1824pt"
+ viewBox="0.00 0.00 242.00 1824.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1820)">
 <title>state transitions</title>
-<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-1820 211.5,-1820 211.5,4 -4,4"/>
+<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-1820 238,-1820 238,4 -4,4"/>
 <g id="clust1" class="cluster">
 <title>cluster__comparisons</title>
 <path fill="none" stroke="#000000" stroke-width="2" d="M112.5,-1310C112.5,-1310 180.5,-1310 180.5,-1310 186.5,-1310 192.5,-1316 192.5,-1322 192.5,-1322 192.5,-1765 192.5,-1765 192.5,-1771 186.5,-1777 180.5,-1777 180.5,-1777 112.5,-1777 112.5,-1777 106.5,-1777 100.5,-1771 100.5,-1765 100.5,-1765 100.5,-1322 100.5,-1322 100.5,-1316 106.5,-1310 112.5,-1310"/>
@@ -24,21 +24,28 @@
 <path fill="none" stroke="#000000" stroke-width="2" d="M40.5,-74C40.5,-74 187.5,-74 187.5,-74 193.5,-74 199.5,-80 199.5,-86 199.5,-86 199.5,-611 199.5,-611 199.5,-617 193.5,-623 187.5,-623 187.5,-623 40.5,-623 40.5,-623 34.5,-623 28.5,-617 28.5,-611 28.5,-611 28.5,-86 28.5,-86 28.5,-80 34.5,-74 40.5,-74"/>
 <text text-anchor="start" x="76.9872" y="-604.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">boolean_logic</text>
 </g>
+<!-- self_tr__comparisons__comparisons_8 -->
+<!-- _comparisons -->
+<!-- self_tr__comparisons__comparisons_8&#45;&gt;_comparisons -->
+<g id="edge9" class="edge">
+<title>self_tr__comparisons__comparisons_8:w&#45;&gt;_comparisons</title>
+<path fill="none" stroke="#000000" d="M157.5,-1810.5C150.9413,-1810.5 152.0017,-1799.902 155.9308,-1786.5696"/>
+<polygon fill="#000000" stroke="#000000" points="159.2788,-1787.5917 159.0508,-1776.9993 152.6236,-1785.422 159.2788,-1787.5917"/>
+</g>
 <!-- __initial -->
-<g id="node1" class="node">
+<g id="node2" class="node">
 <title>__initial</title>
-<ellipse fill="#000000" stroke="#000000" stroke-width="2" cx="177.5" cy="-1810.5" rx="5.5" ry="5.5"/>
+<ellipse fill="#000000" stroke="#000000" stroke-width="2" cx="228.5" cy="-1810.5" rx="5.5" ry="5.5"/>
 </g>
-<!-- _comparisons -->
 <!-- __initial&#45;&gt;_comparisons -->
 <g id="edge1" class="edge">
 <title>__initial&#45;&gt;_comparisons</title>
-<path fill="none" stroke="#000000" d="M177.5,-1804.9623C177.5,-1800.7143 177.5,-1794.3733 177.5,-1787.1925"/>
-<polygon fill="#000000" stroke="#000000" points="181.0001,-1786.9976 177.5,-1776.9976 174.0001,-1786.9976 181.0001,-1786.9976"/>
-<text text-anchor="middle" x="178.8895" y="-1788" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
+<path fill="none" stroke="#000000" d="M225.4624,-1805.9139C220.1039,-1797.8236 208.5513,-1780.3814 198.1272,-1764.643"/>
+<polygon fill="#000000" stroke="#000000" points="200.9386,-1762.5493 192.4986,-1756.1449 195.1026,-1766.4148 200.9386,-1762.5493"/>
+<text text-anchor="middle" x="219.8895" y="-1788" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
 </g>
 <!-- _final -->
-<g id="node2" class="node">
+<g id="node3" class="node">
 <title>_final</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="129,-46 0,-46 0,0 129,0 129,-46"/>
 <text text-anchor="start" x="53.999" y="-29.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">final</text>
@@ -46,13 +53,19 @@
 <polygon fill="#000000" stroke="#000000" points=".5,-23 .5,-23 129.5,-23 129.5,-23 .5,-23"/>
 <path fill="none" stroke="#000000" stroke-width="2" d="M13,-1C13,-1 116,-1 116,-1 122,-1 128,-7 128,-13 128,-13 128,-33 128,-33 128,-39 122,-45 116,-45 116,-45 13,-45 13,-45 7,-45 1,-39 1,-33 1,-33 1,-13 1,-13 1,-7 7,-1 13,-1"/>
 </g>
+<!-- _comparisons&#45;&gt;self_tr__comparisons__comparisons_8 -->
+<g id="edge8" class="edge">
+<title>_comparisons:e&#45;&gt;self_tr__comparisons__comparisons_8:e</title>
+<path fill="none" stroke="#000000" d="M179.3607,-1776.9997C174.1284,-1794.4465 165.5723,-1810.5 157.5,-1810.5"/>
+<text text-anchor="start" x="175.5" y="-1788" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">in.e &#160;&#160;</text>
+</g>
 <!-- _comparisons_initial -->
-<g id="node4" class="node">
+<g id="node5" class="node">
 <title>_comparisons_initial</title>
 <ellipse fill="#000000" stroke="#000000" stroke-width="2" cx="139.5" cy="-1733.5" rx="5.5" ry="5.5"/>
 </g>
 <!-- _comparisons_s1 -->
-<g id="node5" class="node">
+<g id="node6" class="node">
 <title>_comparisons_s1</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="167.5,-1682 111.5,-1682 111.5,-1646 167.5,-1646 167.5,-1682"/>
 <text text-anchor="start" x="133.1646" y="-1660.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s1</text>
@@ -66,7 +79,7 @@
 <text text-anchor="middle" x="140.8895" y="-1702" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
 </g>
 <!-- _comparisons_s2 -->
-<g id="node6" class="node">
+<g id="node7" class="node">
 <title>_comparisons_s2</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="167.5,-1600 111.5,-1600 111.5,-1564 167.5,-1564 167.5,-1600"/>
 <text text-anchor="start" x="133.1646" y="-1578.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s2</text>
@@ -80,7 +93,7 @@
 <text text-anchor="start" x="139.5" y="-1620" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">[1 == 1] &#160;&#160;</text>
 </g>
 <!-- _comparisons_s3 -->
-<g id="node7" class="node">
+<g id="node8" class="node">
 <title>_comparisons_s3</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="167.5,-1518 111.5,-1518 111.5,-1482 167.5,-1482 167.5,-1518"/>
 <text text-anchor="start" x="133.1646" y="-1496.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s3</text>
@@ -94,7 +107,7 @@
 <text text-anchor="start" x="139.5" y="-1538" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">[1 != 2] &#160;&#160;</text>
 </g>
 <!-- _comparisons_s4 -->
-<g id="node8" class="node">
+<g id="node9" class="node">
 <title>_comparisons_s4</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="167.5,-1436 111.5,-1436 111.5,-1400 167.5,-1400 167.5,-1436"/>
 <text text-anchor="start" x="133.1646" y="-1414.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s4</text>
@@ -108,7 +121,7 @@
 <text text-anchor="start" x="139.5" y="-1456" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">[1 &lt; 2] &#160;&#160;</text>
 </g>
 <!-- _comparisons_s5 -->
-<g id="node9" class="node">
+<g id="node10" class="node">
 <title>_comparisons_s5</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="167.5,-1354 111.5,-1354 111.5,-1318 167.5,-1318 167.5,-1354"/>
 <text text-anchor="start" x="133.1646" y="-1332.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s5</text>
@@ -130,103 +143,103 @@
 <text text-anchor="middle" x="140.8895" y="-1293" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
 </g>
 <!-- _arithmetic_initial -->
-<g id="node11" class="node">
+<g id="node12" class="node">
 <title>_arithmetic_initial</title>
 <ellipse fill="#000000" stroke="#000000" stroke-width="2" cx="98.5" cy="-1238.5" rx="5.5" ry="5.5"/>
 </g>
 <!-- _arithmetic_s1 -->
-<g id="node12" class="node">
+<g id="node13" class="node">
 <title>_arithmetic_s1</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="126.5,-1187 70.5,-1187 70.5,-1151 126.5,-1151 126.5,-1187"/>
 <text text-anchor="start" x="92.1646" y="-1165.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s1</text>
 <path fill="none" stroke="#000000" stroke-width="2" d="M82.8333,-1152C82.8333,-1152 114.1667,-1152 114.1667,-1152 119.8333,-1152 125.5,-1157.6667 125.5,-1163.3333 125.5,-1163.3333 125.5,-1174.6667 125.5,-1174.6667 125.5,-1180.3333 119.8333,-1186 114.1667,-1186 114.1667,-1186 82.8333,-1186 82.8333,-1186 77.1667,-1186 71.5,-1180.3333 71.5,-1174.6667 71.5,-1174.6667 71.5,-1163.3333 71.5,-1163.3333 71.5,-1157.6667 77.1667,-1152 82.8333,-1152"/>
 </g>
 <!-- _arithmetic_initial&#45;&gt;_arithmetic_s1 -->
-<g id="edge8" class="edge">
+<g id="edge10" class="edge">
 <title>_arithmetic_initial&#45;&gt;_arithmetic_s1</title>
 <path fill="none" stroke="#000000" d="M98.5,-1232.5745C98.5,-1224.7003 98.5,-1210.2498 98.5,-1197.1135"/>
 <polygon fill="#000000" stroke="#000000" points="102.0001,-1197.0109 98.5,-1187.011 95.0001,-1197.011 102.0001,-1197.0109"/>
 <text text-anchor="middle" x="99.8895" y="-1207" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
 </g>
 <!-- _arithmetic_s2 -->
-<g id="node13" class="node">
+<g id="node14" class="node">
 <title>_arithmetic_s2</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="126.5,-1105 70.5,-1105 70.5,-1069 126.5,-1069 126.5,-1105"/>
 <text text-anchor="start" x="92.1646" y="-1083.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s2</text>
 <path fill="none" stroke="#000000" stroke-width="2" d="M82.8333,-1070C82.8333,-1070 114.1667,-1070 114.1667,-1070 119.8333,-1070 125.5,-1075.6667 125.5,-1081.3333 125.5,-1081.3333 125.5,-1092.6667 125.5,-1092.6667 125.5,-1098.3333 119.8333,-1104 114.1667,-1104 114.1667,-1104 82.8333,-1104 82.8333,-1104 77.1667,-1104 71.5,-1098.3333 71.5,-1092.6667 71.5,-1092.6667 71.5,-1081.3333 71.5,-1081.3333 71.5,-1075.6667 77.1667,-1070 82.8333,-1070"/>
 </g>
 <!-- _arithmetic_s1&#45;&gt;_arithmetic_s2 -->
-<g id="edge9" class="edge">
+<g id="edge11" class="edge">
 <title>_arithmetic_s1&#45;&gt;_arithmetic_s2</title>
 <path fill="none" stroke="#000000" d="M98.5,-1150.8015C98.5,-1140.3976 98.5,-1127.1215 98.5,-1115.3768"/>
 <polygon fill="#000000" stroke="#000000" points="102.0001,-1115.1476 98.5,-1105.1476 95.0001,-1115.1476 102.0001,-1115.1476"/>
 <text text-anchor="start" x="98.5" y="-1125" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">[1 + 1 == 2] &#160;&#160;</text>
 </g>
 <!-- _arithmetic_s3 -->
-<g id="node14" class="node">
+<g id="node15" class="node">
 <title>_arithmetic_s3</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="126.5,-1023 70.5,-1023 70.5,-987 126.5,-987 126.5,-1023"/>
 <text text-anchor="start" x="92.1646" y="-1001.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s3</text>
 <path fill="none" stroke="#000000" stroke-width="2" d="M82.8333,-988C82.8333,-988 114.1667,-988 114.1667,-988 119.8333,-988 125.5,-993.6667 125.5,-999.3333 125.5,-999.3333 125.5,-1010.6667 125.5,-1010.6667 125.5,-1016.3333 119.8333,-1022 114.1667,-1022 114.1667,-1022 82.8333,-1022 82.8333,-1022 77.1667,-1022 71.5,-1016.3333 71.5,-1010.6667 71.5,-1010.6667 71.5,-999.3333 71.5,-999.3333 71.5,-993.6667 77.1667,-988 82.8333,-988"/>
 </g>
 <!-- _arithmetic_s2&#45;&gt;_arithmetic_s3 -->
-<g id="edge10" class="edge">
+<g id="edge12" class="edge">
 <title>_arithmetic_s2&#45;&gt;_arithmetic_s3</title>
 <path fill="none" stroke="#000000" d="M98.5,-1068.8015C98.5,-1058.3976 98.5,-1045.1215 98.5,-1033.3768"/>
 <polygon fill="#000000" stroke="#000000" points="102.0001,-1033.1476 98.5,-1023.1476 95.0001,-1033.1476 102.0001,-1033.1476"/>
 <text text-anchor="start" x="98.5" y="-1043" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">[42 == 52 &#45; 11 + 1] &#160;&#160;</text>
 </g>
 <!-- _arithmetic_s4 -->
-<g id="node15" class="node">
+<g id="node16" class="node">
 <title>_arithmetic_s4</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="126.5,-941 70.5,-941 70.5,-905 126.5,-905 126.5,-941"/>
 <text text-anchor="start" x="92.1646" y="-919.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s4</text>
 <path fill="none" stroke="#000000" stroke-width="2" d="M82.8333,-906C82.8333,-906 114.1667,-906 114.1667,-906 119.8333,-906 125.5,-911.6667 125.5,-917.3333 125.5,-917.3333 125.5,-928.6667 125.5,-928.6667 125.5,-934.3333 119.8333,-940 114.1667,-940 114.1667,-940 82.8333,-940 82.8333,-940 77.1667,-940 71.5,-934.3333 71.5,-928.6667 71.5,-928.6667 71.5,-917.3333 71.5,-917.3333 71.5,-911.6667 77.1667,-906 82.8333,-906"/>
 </g>
 <!-- _arithmetic_s3&#45;&gt;_arithmetic_s4 -->
-<g id="edge11" class="edge">
+<g id="edge13" class="edge">
 <title>_arithmetic_s3&#45;&gt;_arithmetic_s4</title>
 <path fill="none" stroke="#000000" d="M98.5,-986.8015C98.5,-976.3976 98.5,-963.1215 98.5,-951.3768"/>
 <polygon fill="#000000" stroke="#000000" points="102.0001,-951.1476 98.5,-941.1476 95.0001,-951.1476 102.0001,-951.1476"/>
 <text text-anchor="start" x="98.5" y="-961" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">[2 * 3 == 6] &#160;&#160;</text>
 </g>
 <!-- _arithmetic_s5 -->
-<g id="node16" class="node">
+<g id="node17" class="node">
 <title>_arithmetic_s5</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="126.5,-859 70.5,-859 70.5,-823 126.5,-823 126.5,-859"/>
 <text text-anchor="start" x="92.1646" y="-837.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s5</text>
 <path fill="none" stroke="#000000" stroke-width="2" d="M82.8333,-824C82.8333,-824 114.1667,-824 114.1667,-824 119.8333,-824 125.5,-829.6667 125.5,-835.3333 125.5,-835.3333 125.5,-846.6667 125.5,-846.6667 125.5,-852.3333 119.8333,-858 114.1667,-858 114.1667,-858 82.8333,-858 82.8333,-858 77.1667,-858 71.5,-852.3333 71.5,-846.6667 71.5,-846.6667 71.5,-835.3333 71.5,-835.3333 71.5,-829.6667 77.1667,-824 82.8333,-824"/>
 </g>
 <!-- _arithmetic_s4&#45;&gt;_arithmetic_s5 -->
-<g id="edge12" class="edge">
+<g id="edge14" class="edge">
 <title>_arithmetic_s4&#45;&gt;_arithmetic_s5</title>
 <path fill="none" stroke="#000000" d="M98.5,-904.8015C98.5,-894.3976 98.5,-881.1215 98.5,-869.3768"/>
 <polygon fill="#000000" stroke="#000000" points="102.0001,-869.1476 98.5,-859.1476 95.0001,-869.1476 102.0001,-869.1476"/>
 <text text-anchor="start" x="98.5" y="-879" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">[21 / 3 == 7] &#160;&#160;</text>
 </g>
 <!-- _arithmetic_s6 -->
-<g id="node17" class="node">
+<g id="node18" class="node">
 <title>_arithmetic_s6</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="126.5,-777 70.5,-777 70.5,-741 126.5,-741 126.5,-777"/>
 <text text-anchor="start" x="92.1646" y="-755.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s6</text>
 <path fill="none" stroke="#000000" stroke-width="2" d="M82.8333,-742C82.8333,-742 114.1667,-742 114.1667,-742 119.8333,-742 125.5,-747.6667 125.5,-753.3333 125.5,-753.3333 125.5,-764.6667 125.5,-764.6667 125.5,-770.3333 119.8333,-776 114.1667,-776 114.1667,-776 82.8333,-776 82.8333,-776 77.1667,-776 71.5,-770.3333 71.5,-764.6667 71.5,-764.6667 71.5,-753.3333 71.5,-753.3333 71.5,-747.6667 77.1667,-742 82.8333,-742"/>
 </g>
 <!-- _arithmetic_s5&#45;&gt;_arithmetic_s6 -->
-<g id="edge13" class="edge">
+<g id="edge15" class="edge">
 <title>_arithmetic_s5&#45;&gt;_arithmetic_s6</title>
 <path fill="none" stroke="#000000" d="M98.5,-822.8015C98.5,-812.3976 98.5,-799.1215 98.5,-787.3768"/>
 <polygon fill="#000000" stroke="#000000" points="102.0001,-787.1476 98.5,-777.1476 95.0001,-787.1476 102.0001,-787.1476"/>
 <text text-anchor="start" x="98.5" y="-797" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">[256 == 2 ** 2 ** 3] &#160;&#160;</text>
 </g>
 <!-- _arithmetic_s7 -->
-<g id="node18" class="node">
+<g id="node19" class="node">
 <title>_arithmetic_s7</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="126.5,-695 70.5,-695 70.5,-659 126.5,-659 126.5,-695"/>
 <text text-anchor="start" x="92.1646" y="-673.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s7</text>
 <path fill="none" stroke="#000000" stroke-width="2" d="M82.8333,-660C82.8333,-660 114.1667,-660 114.1667,-660 119.8333,-660 125.5,-665.6667 125.5,-671.3333 125.5,-671.3333 125.5,-682.6667 125.5,-682.6667 125.5,-688.3333 119.8333,-694 114.1667,-694 114.1667,-694 82.8333,-694 82.8333,-694 77.1667,-694 71.5,-688.3333 71.5,-682.6667 71.5,-682.6667 71.5,-671.3333 71.5,-671.3333 71.5,-665.6667 77.1667,-660 82.8333,-660"/>
 </g>
 <!-- _arithmetic_s6&#45;&gt;_arithmetic_s7 -->
-<g id="edge14" class="edge">
+<g id="edge16" class="edge">
 <title>_arithmetic_s6&#45;&gt;_arithmetic_s7</title>
 <path fill="none" stroke="#000000" d="M98.5,-740.8015C98.5,-730.3976 98.5,-717.1215 98.5,-705.3768"/>
 <polygon fill="#000000" stroke="#000000" points="102.0001,-705.1476 98.5,-695.1476 95.0001,-705.1476 102.0001,-705.1476"/>
@@ -234,103 +247,103 @@
 </g>
 <!-- _boolean_logic -->
 <!-- _arithmetic_s7&#45;&gt;_boolean_logic -->
-<g id="edge15" class="edge">
+<g id="edge17" class="edge">
 <title>_arithmetic_s7&#45;&gt;_boolean_logic</title>
 <path fill="none" stroke="#000000" d="M98.5,-658.661C98.5,-651.2376 98.5,-642.2479 98.5,-633.0279"/>
 <polygon fill="#000000" stroke="#000000" points="102.0001,-632.9962 98.5,-622.9962 95.0001,-632.9963 102.0001,-632.9962"/>
 <text text-anchor="middle" x="99.8895" y="-634" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
 </g>
 <!-- _boolean_logic_initial -->
-<g id="node20" class="node">
+<g id="node21" class="node">
 <title>_boolean_logic_initial</title>
 <ellipse fill="#000000" stroke="#000000" stroke-width="2" cx="64.5" cy="-579.5" rx="5.5" ry="5.5"/>
 </g>
 <!-- _boolean_logic_s1 -->
-<g id="node21" class="node">
+<g id="node22" class="node">
 <title>_boolean_logic_s1</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="92.5,-528 36.5,-528 36.5,-492 92.5,-492 92.5,-528"/>
 <text text-anchor="start" x="58.1646" y="-506.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s1</text>
 <path fill="none" stroke="#000000" stroke-width="2" d="M48.8333,-493C48.8333,-493 80.1667,-493 80.1667,-493 85.8333,-493 91.5,-498.6667 91.5,-504.3333 91.5,-504.3333 91.5,-515.6667 91.5,-515.6667 91.5,-521.3333 85.8333,-527 80.1667,-527 80.1667,-527 48.8333,-527 48.8333,-527 43.1667,-527 37.5,-521.3333 37.5,-515.6667 37.5,-515.6667 37.5,-504.3333 37.5,-504.3333 37.5,-498.6667 43.1667,-493 48.8333,-493"/>
 </g>
 <!-- _boolean_logic_initial&#45;&gt;_boolean_logic_s1 -->
-<g id="edge16" class="edge">
+<g id="edge18" class="edge">
 <title>_boolean_logic_initial&#45;&gt;_boolean_logic_s1</title>
 <path fill="none" stroke="#000000" d="M64.5,-573.5745C64.5,-565.7003 64.5,-551.2498 64.5,-538.1135"/>
 <polygon fill="#000000" stroke="#000000" points="68.0001,-538.0109 64.5,-528.011 61.0001,-538.011 68.0001,-538.0109"/>
 <text text-anchor="middle" x="65.8895" y="-548" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000"> </text>
 </g>
 <!-- _boolean_logic_s2 -->
-<g id="node22" class="node">
+<g id="node23" class="node">
 <title>_boolean_logic_s2</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="92.5,-446 36.5,-446 36.5,-410 92.5,-410 92.5,-446"/>
 <text text-anchor="start" x="58.1646" y="-424.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s2</text>
 <path fill="none" stroke="#000000" stroke-width="2" d="M48.8333,-411C48.8333,-411 80.1667,-411 80.1667,-411 85.8333,-411 91.5,-416.6667 91.5,-422.3333 91.5,-422.3333 91.5,-433.6667 91.5,-433.6667 91.5,-439.3333 85.8333,-445 80.1667,-445 80.1667,-445 48.8333,-445 48.8333,-445 43.1667,-445 37.5,-439.3333 37.5,-433.6667 37.5,-433.6667 37.5,-422.3333 37.5,-422.3333 37.5,-416.6667 43.1667,-411 48.8333,-411"/>
 </g>
 <!-- _boolean_logic_s1&#45;&gt;_boolean_logic_s2 -->
-<g id="edge17" class="edge">
+<g id="edge19" class="edge">
 <title>_boolean_logic_s1&#45;&gt;_boolean_logic_s2</title>
 <path fill="none" stroke="#000000" d="M64.5,-491.8015C64.5,-481.3976 64.5,-468.1215 64.5,-456.3768"/>
 <polygon fill="#000000" stroke="#000000" points="68.0001,-456.1476 64.5,-446.1476 61.0001,-456.1476 68.0001,-456.1476"/>
 <text text-anchor="start" x="64.5" y="-466" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">[true] &#160;&#160;</text>
 </g>
 <!-- _boolean_logic_s3 -->
-<g id="node23" class="node">
+<g id="node24" class="node">
 <title>_boolean_logic_s3</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="92.5,-364 36.5,-364 36.5,-328 92.5,-328 92.5,-364"/>
 <text text-anchor="start" x="58.1646" y="-342.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s3</text>
 <path fill="none" stroke="#000000" stroke-width="2" d="M48.8333,-329C48.8333,-329 80.1667,-329 80.1667,-329 85.8333,-329 91.5,-334.6667 91.5,-340.3333 91.5,-340.3333 91.5,-351.6667 91.5,-351.6667 91.5,-357.3333 85.8333,-363 80.1667,-363 80.1667,-363 48.8333,-363 48.8333,-363 43.1667,-363 37.5,-357.3333 37.5,-351.6667 37.5,-351.6667 37.5,-340.3333 37.5,-340.3333 37.5,-334.6667 43.1667,-329 48.8333,-329"/>
 </g>
 <!-- _boolean_logic_s2&#45;&gt;_boolean_logic_s3 -->
-<g id="edge18" class="edge">
+<g id="edge20" class="edge">
 <title>_boolean_logic_s2&#45;&gt;_boolean_logic_s3</title>
 <path fill="none" stroke="#000000" d="M64.5,-409.8015C64.5,-399.3976 64.5,-386.1215 64.5,-374.3768"/>
 <polygon fill="#000000" stroke="#000000" points="68.0001,-374.1476 64.5,-364.1476 61.0001,-374.1476 68.0001,-374.1476"/>
 <text text-anchor="start" x="64.5" y="-384" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">[false or true] &#160;&#160;</text>
 </g>
 <!-- _boolean_logic_s4 -->
-<g id="node24" class="node">
+<g id="node25" class="node">
 <title>_boolean_logic_s4</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="92.5,-282 36.5,-282 36.5,-246 92.5,-246 92.5,-282"/>
 <text text-anchor="start" x="58.1646" y="-260.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s4</text>
 <path fill="none" stroke="#000000" stroke-width="2" d="M48.8333,-247C48.8333,-247 80.1667,-247 80.1667,-247 85.8333,-247 91.5,-252.6667 91.5,-258.3333 91.5,-258.3333 91.5,-269.6667 91.5,-269.6667 91.5,-275.3333 85.8333,-281 80.1667,-281 80.1667,-281 48.8333,-281 48.8333,-281 43.1667,-281 37.5,-275.3333 37.5,-269.6667 37.5,-269.6667 37.5,-258.3333 37.5,-258.3333 37.5,-252.6667 43.1667,-247 48.8333,-247"/>
 </g>
 <!-- _boolean_logic_s3&#45;&gt;_boolean_logic_s4 -->
-<g id="edge19" class="edge">
+<g id="edge21" class="edge">
 <title>_boolean_logic_s3&#45;&gt;_boolean_logic_s4</title>
 <path fill="none" stroke="#000000" d="M64.5,-327.8015C64.5,-317.3976 64.5,-304.1215 64.5,-292.3768"/>
 <polygon fill="#000000" stroke="#000000" points="68.0001,-292.1476 64.5,-282.1476 61.0001,-292.1476 68.0001,-292.1476"/>
 <text text-anchor="start" x="64.5" y="-302" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">[true and not false] &#160;&#160;</text>
 </g>
 <!-- _boolean_logic_s5 -->
-<g id="node25" class="node">
+<g id="node26" class="node">
 <title>_boolean_logic_s5</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="92.5,-200 36.5,-200 36.5,-164 92.5,-164 92.5,-200"/>
 <text text-anchor="start" x="58.1646" y="-178.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s5</text>
 <path fill="none" stroke="#000000" stroke-width="2" d="M48.8333,-165C48.8333,-165 80.1667,-165 80.1667,-165 85.8333,-165 91.5,-170.6667 91.5,-176.3333 91.5,-176.3333 91.5,-187.6667 91.5,-187.6667 91.5,-193.3333 85.8333,-199 80.1667,-199 80.1667,-199 48.8333,-199 48.8333,-199 43.1667,-199 37.5,-193.3333 37.5,-187.6667 37.5,-187.6667 37.5,-176.3333 37.5,-176.3333 37.5,-170.6667 43.1667,-165 48.8333,-165"/>
 </g>
 <!-- _boolean_logic_s4&#45;&gt;_boolean_logic_s5 -->
-<g id="edge20" class="edge">
+<g id="edge22" class="edge">
 <title>_boolean_logic_s4&#45;&gt;_boolean_logic_s5</title>
 <path fill="none" stroke="#000000" d="M64.5,-245.8015C64.5,-235.3976 64.5,-222.1215 64.5,-210.3768"/>
 <polygon fill="#000000" stroke="#000000" points="68.0001,-210.1476 64.5,-200.1476 61.0001,-210.1476 68.0001,-210.1476"/>
 <text text-anchor="start" x="64.5" y="-220" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">[not (true and false or false)] &#160;&#160;</text>
 </g>
 <!-- _boolean_logic_s6 -->
-<g id="node26" class="node">
+<g id="node27" class="node">
 <title>_boolean_logic_s6</title>
 <polygon fill="transparent" stroke="transparent" stroke-width="2" points="92.5,-118 36.5,-118 36.5,-82 92.5,-82 92.5,-118"/>
 <text text-anchor="start" x="58.1646" y="-96.2" font-family="Helvetica,sans-Serif" font-size="12.00" fill="#000000">s6</text>
 <path fill="none" stroke="#000000" stroke-width="2" d="M48.8333,-83C48.8333,-83 80.1667,-83 80.1667,-83 85.8333,-83 91.5,-88.6667 91.5,-94.3333 91.5,-94.3333 91.5,-105.6667 91.5,-105.6667 91.5,-111.3333 85.8333,-117 80.1667,-117 80.1667,-117 48.8333,-117 48.8333,-117 43.1667,-117 37.5,-111.3333 37.5,-105.6667 37.5,-105.6667 37.5,-94.3333 37.5,-94.3333 37.5,-88.6667 43.1667,-83 48.8333,-83"/>
 </g>
 <!-- _boolean_logic_s5&#45;&gt;_boolean_logic_s6 -->
-<g id="edge21" class="edge">
+<g id="edge23" class="edge">
 <title>_boolean_logic_s5&#45;&gt;_boolean_logic_s6</title>
 <path fill="none" stroke="#000000" d="M64.5,-163.8015C64.5,-153.3976 64.5,-140.1215 64.5,-128.3768"/>
 <polygon fill="#000000" stroke="#000000" points="68.0001,-128.1476 64.5,-118.1476 61.0001,-128.1476 68.0001,-128.1476"/>
 <text text-anchor="start" x="64.5" y="-138" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">[not (false or false and true)] &#160;&#160;</text>
 </g>
 <!-- _boolean_logic_s6&#45;&gt;_final -->
-<g id="edge22" class="edge">
+<g id="edge24" class="edge">
 <title>_boolean_logic_s6&#45;&gt;_final</title>
 <path fill="none" stroke="#000000" d="M64.5,-81.7521C64.5,-74.0806 64.5,-64.9093 64.5,-56.1197"/>
 <polygon fill="#000000" stroke="#000000" points="68.0001,-56.0895 64.5,-46.0895 61.0001,-56.0895 68.0001,-56.0895"/>

+ 5 - 1
test/test_files/features/expressions/test_expressions.xml

@@ -1,11 +1,12 @@
 <?xml version="1.0" ?>
 <test>
   <statechart>
-    <semantics/>
+    <semantics big_step_maximality="take_many"/>
     <tree>
       <state initial="comparisons">
 
         <state id="comparisons" initial="s1">
+          <transition event="e" target="." port="in"/>
           <state id="s1">
             <transition cond="1 == 1" target="../s2"/>
           </state>
@@ -77,6 +78,9 @@
       </state>
     </tree>
   </statechart>
+  <input>
+    <input_event name="e" port="in" time="0 d"/>
+  </input>
   <output>
     <big_step>
       <event name="all_good" port="out"/>

+ 2 - 2
test/test_files/semantics/event_lifeline/statechart_flat.svg

@@ -42,7 +42,7 @@
 <title>_a&#45;&gt;_b</title>
 <path fill="none" stroke="#000000" d="M53,-147.8711C53,-142.4482 53,-136.3229 53,-130.2494"/>
 <polygon fill="#000000" stroke="#000000" points="56.5001,-130.21 53,-120.21 49.5001,-130.21 56.5001,-130.21"/>
-<text text-anchor="start" x="53" y="-131" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">^e &#160;&#160;</text>
+<text text-anchor="start" x="53" y="-131" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">e^f &#160;&#160;</text>
 </g>
 <!-- _c -->
 <g id="node4" class="node">
@@ -58,7 +58,7 @@
 <title>_b&#45;&gt;_c</title>
 <path fill="none" stroke="#000000" d="M53,-73.9916C53,-68.476 53,-62.474 53,-56.5881"/>
 <polygon fill="#000000" stroke="#000000" points="56.5001,-56.249 53,-46.2491 49.5001,-56.2491 56.5001,-56.249"/>
-<text text-anchor="start" x="53" y="-57" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">e &#160;&#160;</text>
+<text text-anchor="start" x="53" y="-57" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">f &#160;&#160;</text>
 </g>
 </g>
 </svg>

+ 3 - 3
test/test_files/semantics/event_lifeline/statechart_flat.xml

@@ -5,15 +5,15 @@
   <tree>
     <state initial="a">
       <state id="a">
-        <transition target="/b">
-          <raise event="e"/>
+        <transition event="e" target="/b">
+          <raise event="f"/>
         </transition>
       </state>
       <state id="b">
         <onentry>
           <raise event="in_b" port="out"/>
         </onentry>
-        <transition event="e" target="/c"/>
+        <transition event="f" target="/c"/>
       </state>
       <state id="c">
         <onentry>

+ 3 - 0
test/test_files/semantics/event_lifeline/test_flat_nextbs.xml

@@ -5,6 +5,9 @@
       big_step_maximality="*"
       internal_event_lifeline="queue"/>
   </statechart>
+  <input>
+      <input_event name="e" time="0 d"/>
+  </input>
   <output>
     <big_step>
       <event name="in_b" port="out"/>