Explorar el Código

Added some documentation + 2 bugs in the form of tests

Joeri Exelmans hace 5 años
padre
commit
950b98fd8d

+ 2 - 0
src/sccd/controller/controller.py

@@ -33,6 +33,8 @@ class Controller:
 
         self.simulated_time = 0 # integer
 
+        # Our instances should not have 'full access' to the global event queue (e.g. they shouldn't pop due events, that's the controller's task!). They are only allowed to schedule and cancel scheduled events. Hence we pass them 2 callbacks:
+
         def schedule_after(after, event, instances):
             entry = Controller.EventQueueEntry(event, instances)
             return self.queue.add(self.simulated_time + after, entry)

+ 1 - 1
src/sccd/statechart/parser/xml.py

@@ -17,7 +17,7 @@ def check_duration_type(type):
       msg += "\n Hint: Did you forget a duration unit suffix? ('s', 'ms', ...)"
     raise XmlError(msg)
 
-# path: path for finding external statecharts
+# path: filesystem path for finding external statecharts
 def statechart_parser_rules(globals, path, load_external = True, parse_f = parse_f) -> Rules:
   import os
   def parse_statechart(el):

+ 1 - 1
src/sccd/statechart/static/statechart.py

@@ -113,7 +113,7 @@ class Statechart(Freezable):
 
     # Union of internally raised and input events. Basically all the events that a transition could be triggered by.
     self.internal_events: Bitmap = internal_events
-    # All internally raised events in the statechart, may overlap with input events.
+    # All internally raised events in the statechart, may overlap with input events, as an event can be both an input event and internally raised.
     self.internally_raised_events: Bitmap = internally_raised_events
 
     # Mapping from inport to set of event IDs - currently unused

+ 7 - 3
src/sccd/statechart/static/tree.py

@@ -35,13 +35,15 @@ class State(Freezable):
     # Subset of descendants that are always entered when this state is the target of a transition, minus history states.
     def _static_target_states(self) -> Bitmap:
         if self.default_state:
-            return self.opt.state_id_bitmap | self.default_state._static_target_states()
+            # this state + recursion on 'default state'
+            return self.opt.state_id_bitmap | self.default_state._static_target_states() 
         else:
+            # only this state
             return self.opt.state_id_bitmap
 
     # States that are always entered when this state is part of the "enter path", but not the actual target of a transition.
     def _static_additional_target_states(self, exclude: 'State') -> Tuple[Bitmap, List['HistoryState']]:
-        return self.opt.state_id_bitmap
+        return self.opt.state_id_bitmap # only this state
 
     def __repr__(self):
         return "State(\"%s\")" % (self.short_name)
@@ -86,9 +88,11 @@ class DeepHistoryState(HistoryState):
 class ParallelState(State):
 
     def _static_target_states(self) -> Bitmap:
+        # this state + recursive on all children that are not a history state
         return bm_union(c._static_target_states() for c in self.children if not isinstance(c, HistoryState)) | self.opt.state_id_bitmap
 
     def _static_additional_target_states(self, exclude: 'State') -> Bitmap:
+        # 
         return self._static_target_states() & ~exclude._static_target_states()
 
     def __repr__(self):
@@ -199,7 +203,7 @@ class Transition(Freezable):
         self.trigger: Trigger = _empty_trigger
 
         self.opt: Optional['TransitionOptimization'] = None        
-                    
+
     def __str__(self):
         return termcolor.colored("%s 🡪 %s" % (self.source.opt.full_name, self.targets[0].opt.full_name), 'green')
 

+ 3 - 0
src/sccd/util/xml_parser.py

@@ -206,6 +206,9 @@ def parse(src_file, rules: RulesWDone, ignore_unmatched = False, decorate_except
   if len(results) > 0:
     return results[0] # return first item, since we expect at most one item since an XML file has only one root node
 
+
+# Utility functions to do more with less lines of code:
+
 def require_attribute(el, attr):
   val = el.get(attr)
   if val is None:

+ 17 - 0
test/test_files/todo/test_should_fail.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" ?>
+<test>
+  <statechart>
+    <!-- this test succeeds, but it should fail:
+         the transition's guard should have no access to the function 'f' declared in the transition's action code.
+         to fix this, the transition's action code should have its own scope (and hence, stack frame),
+         and so should the transition's guard.
+         the "transition scope" should only be used for event parameters.  -->
+    <root>
+      <state id="A">
+        <transition event="e(i: int)" guard="f(i) == 0" target=".">
+          <code> f = func(j: int) { return j+1; }; </code>
+        </transition>
+      </state>
+    </root>
+  </statechart>
+</test>

+ 15 - 0
test/test_files/todo/test_todo_history.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" ?>
+<test>
+  <statechart>
+    <!-- there's a bug in calculating the history value for a shallow history state:
+         the history value will only include the exited state that is a direct child of the parent of the history state.
+         the correct behavior is to include also
+           1) if the child is a parallelstate, all of the direct child's children
+           2) if the child is a regular state, the direct child's 'default' state
+         both, recursively. -->
+    <root>
+      <state id="A">
+      </state>
+    </root>
+  </statechart>
+</test>