Переглянути джерело

Add statechart from Day & Atlee (doesn't work yet, need to implement: 1. event parameters, 2. user defined functions, 3. builtin functions 'log10' and 'int')

Joeri Exelmans 5 роки тому
батько
коміт
73c7897945

Різницю між файлами не показано, бо вона завелика
+ 12 - 108
README.md


+ 3 - 2
src/sccd/parser/statechart_parser.py

@@ -253,14 +253,14 @@ class StateParser(ActionParser):
     # and state tree constructed.
     transitions.append((el, source, actions))
 
-# Parses <statechart> element and all its children.
+# Parses <tree> element and all its children.
+# In practice, this can't really parse a <tree> element because any encountered expression may contain identifiers pointing to model variables declared outside the <tree> element. To parse a <tree>, either manually add those variables to the 'scope', or, more likely, use a StatechartParser instance which will do this for you while parsing the <datamodel> node.
 class TreeParser(StateParser):
 
   def __init__(self):
     super().__init__()
     self.statechart = XmlParser.Context("statechart")
 
-
   # <tree>
 
   def start_tree(self, el):
@@ -361,6 +361,7 @@ class TreeParser(StateParser):
 
     statechart.tree = StateTree(root)
 
+# Parses <statechart> element and all its children.
 class StatechartParser(TreeParser):
 
   def __init__(self, load_external = True):

+ 6 - 4
src/sccd/syntax/expression.py

@@ -4,9 +4,11 @@ from dataclasses import *
 from sccd.util.duration import *
 from sccd.syntax.scope import *
 
-# to inspect types in Python 3.7
-# Python 3.8 already has this built in
-import typing_inspect
+# to inspect types in Python 3.6 and 3.7
+# Python 3.8 already has this in its 'typing' module
+import sys
+if sys.version_info.minor < 8:
+    from typing_inspect import get_args
 
 class Expression(ABC):
     # Must be called exactly once on each expression, before any call to eval is made.
@@ -76,7 +78,7 @@ class FunctionCall(Expression):
         function_type = self.function.init_rvalue(scope)
         if not isinstance(function_type, Callable):
             raise Exception("Function call: Expression '%s' is not callable" % self.function.render())
-        formal_types, return_type = typing_inspect.get_args(function_type)
+        formal_types, return_type = get_args(function_type)
         self.type = return_type
 
         actual_types = [p.init_rvalue(scope) for p in self.parameters]

+ 78 - 0
test/test_files/day_atlee/statechart_fig1_redailer.xml

@@ -0,0 +1,78 @@
+<?xml version="1.0" ?>
+<statechart>
+  <semantics input_event_lifeline="whole"/>
+
+  <datamodel>
+    <var id="c" expr="0"/>
+    <var id="lp" expr="0"/>
+    <var id="p" expr="0"/>
+
+    <func id="digit(i:int, pos:int)">
+      return i // 10**pos % 10;
+    </func>
+
+    <func id="numdigits(i:int)">
+      return int(log10(i)) + 1;
+    </func>
+  </datamodel>
+
+  <tree>
+    <state>
+      <parallel id="Dialing">
+        <state id="Dialer" initial="WaitForDial">
+          <state id="WaitForDial">
+            <!-- t1 -->
+            <transition event="dial(d:int), not redial" cond="c &lt; 10" target=".">
+              <code>
+                c += 1;
+                lp = lp * 10 + d;
+              </code>
+              <raise port="out" event="out(d)"/>
+            </transition>
+            <!-- t2 -->
+            <transition event="dial(d:int), redial" cond="c == 0" target="../DialDigits">
+              <code>
+                lp = d;
+                c = 1;
+              </code>
+              <raise port="out" event="out(d)"/>
+            </transition>
+          </state>
+          <state id="DialDigits">
+            <!-- t3 -->
+            <transition event="dial(d:int)" cond="c &lt; 10" target=".">
+              <code>
+                lp = lp * 10 + d;
+                c += 1;
+              </code>
+              <raise port="out" event="out(d)"/>
+            </transition>
+            <!-- t4 -->
+            <transition cond="c == 10" target="../WaitForDial"/>
+          </state>
+        </state>
+
+        <state id="Redialer" initial="WaitForRedial">
+          <state id="WaitForRedial">
+            <!-- t5 -->
+            <transition event="redial" cond="c == 0" target="../RedialDigits">
+              <code>
+                p = lp;
+              </code>
+              <raise event="dial(digit(lp, 1))"/>
+            </transition>
+          </state>
+          <state id="RedialDigits">
+            <!-- t6 -->
+            <transition cond="c &lt; numdigits(p)" target=".">
+              <raise event="dial(digit(p, c+1))"/>
+            </transition>
+            <!-- t7 -->
+            <transition cond="c == numdigits(p)" target="../WaitForRedial"/>
+          </state>
+        </state>
+
+      </parallel>
+    </state>
+  </tree>
+</statechart>