소스 검색

Allow arbitrary expressions in 'after' attribute of transition

Joeri Exelmans 5 년 전
부모
커밋
29316eb04a
5개의 변경된 파일12개의 추가작업 그리고 3개의 파일을 삭제
  1. 4 0
      src/sccd/runtime/expression.py
  2. 2 1
      src/sccd/runtime/statechart_state.py
  3. 1 1
      src/sccd/runtime/statechart_syntax.py
  4. 3 1
      src/sccd/runtime/xml_loader2.py
  5. 2 0
      src/sccd/schema/grammar.g

+ 4 - 0
src/sccd/runtime/expression.py

@@ -12,6 +12,8 @@ class DataModel:
 
 
 class Expression(ABC):
+    # Evaluation should NOT have side effects.
+    # Motivation is that the evaluation of a guard condition cannot have side effects.
     @abstractmethod
     def eval(self, events, datamodel):
         pass
@@ -25,7 +27,9 @@ class LHS(Expression):
     def eval(self, events, datamodel):
         return self.lhs(events, datamodel).value
 
+# A statement is NOT an expression.
 class Statement(ABC):
+    # Execution typically has side effects.
     @abstractmethod
     def exec(self, events, datamodel):
         pass

+ 2 - 1
src/sccd/runtime/statechart_state.py

@@ -128,10 +128,11 @@ class StatechartState:
 
   def start_timers(self, triggers: List[AfterTrigger]):
       for after in triggers:
+          delay = after.delay.eval([], self.data_model)
           self.output.append(OutputEvent(
               Event(id=after.id, name=after.name, parameters=[after.nextTimerId()]),
               target=InstancesTarget([self.instance]),
-              time_offset=after.delay))
+              time_offset=delay))
 
   # Return whether the current configuration includes ALL the states given.
   def in_state(self, state_strings: List[str]) -> bool:

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

@@ -146,7 +146,7 @@ class Trigger:
 
 class AfterTrigger(Trigger):
     # id: unique within the statechart
-    def __init__(self, id: int, name: str, delay: Timestamp):
+    def __init__(self, id: int, name: str, delay: Expression):
         super().__init__(id=id, name=name, port="")
         self.delay = delay
 

+ 3 - 1
src/sccd/runtime/xml_loader2.py

@@ -152,9 +152,11 @@ def load_state_tree(namespace: ModelNamespace, tree_node) -> StateTree:
     port = t_node.get("port")
     after = t_node.get("after")
     if after is not None:
+      after_expr = expr_parser.parse(after, start="expr")
+      # print(after_expr)
       name = "_after%d" % next_after_id # transition gets unique event name
       next_after_id += 1
-      trigger = AfterTrigger(namespace.assign_event_id(name), name, Timestamp(after))
+      trigger = AfterTrigger(namespace.assign_event_id(name), name, after_expr)
     elif name is not None:
       trigger = Trigger(namespace.assign_event_id(name), name, port)
       namespace.add_inport(port)

+ 2 - 0
src/sccd/schema/grammar.g

@@ -98,6 +98,8 @@ INT: /[0-9]+/
 
 // Statement parsing
 
+block: stmt (";" stmt)
+
 ?stmt: assignment
 
 assignment: lhs "=" expr