Преглед на файлове

Rust: prefix local variables with "local_" to prevent naming collisions

Joeri Exelmans преди 4 години
родител
ревизия
320638f099

+ 4 - 10
notes.txt

@@ -3,11 +3,10 @@ Notes about the future of SCCD
 
 Long-term vision:
 
-  - execute SCCD models in web browsers
-    - through supporting JavaScript (or TypeScript) as:
-        - a port of the runtime
-        - a target language
-    - through supporting C as target language, and compiling to WebAssembly
+  - code generation, work in progress:
+    - Rust as a target. Very portable:
+      - compile to WebAssembly (SCCD in browser)
+      - call from Python, Java, ...
 
   - dynamic creation/destruction of instances (the "CD" part of SCCD)
 
@@ -16,11 +15,6 @@ Long-term vision:
         - convert drawio (XML) models to SCCDXML
         - statechart editing plugin
 
-  - code generation
-    - generating portable C code (or: Rust) may be the most flexible option:
-      - compile to WebAssembly
-      - call from Python, Java, ...
-
   - improve statechart "interface" definition: in/out event, variables (+ observers?), functions, objects
     -> YAKINDU as inspiration
 

+ 3 - 0
rust/README.txt

@@ -0,0 +1,3 @@
+This directory contains
+ 1) shared stuff, imported by SCCD model-generated Rust code
+ 2) "the runtime", which is just a bunch of primitives for running the generated code.

+ 7 - 3
src/sccd/action_lang/codegen/rust.py

@@ -66,7 +66,7 @@ class ScopeHelper():
             writer.writeln("let mut scope = %s {" % type_name)
             writer.writeln("  _base: scope,")
             for v in self.current().scope.variables[start:end]:
-                writer.writeln("  %s," % v.name)
+                writer.writeln("  %s: local_%s," % (v.name, v.name))
             writer.writeln("};")
 
         self.current().committed = end
@@ -257,6 +257,7 @@ class ActionLangRustGenerator(Visitor):
         # self.w.wno(") ")
 
     def visit_ParamDecl(self, expr):
+        self.w.wno("local_")
         self.w.wno(expr.name)
         self.w.wno(": ")
         expr.formal_type.accept(self)
@@ -302,13 +303,16 @@ class ActionLangRustGenerator(Visitor):
 
         if lval.is_init:
             self.w.wno("let mut ")
+            self.w.wno("local_" + lval.name)
         else:
             if lval.offset < 0:
                 self.w.wno("parent%d." % self.scope.current().scope.nested_levels(lval.offset))
+                self.w.wno(lval.name)
             elif lval.offset < self.scope.current().committed:
                 self.w.wno("scope.")
-
-        self.w.wno(lval.name)
+                self.w.wno(lval.name)
+            else:
+                self.w.wno("local_" + lval.name)
 
     def visit_SCCDClosureObject(self, type):
         self.w.wno("(%s, " % self.scope.type(type.scope, type.scope.size()))

+ 1 - 0
src/sccd/statechart/codegen/code_generation.txt

@@ -48,6 +48,7 @@ Tests currently passing (flag --rust):
 Currently unimplemented:
 
     - event parameters
+    - builtin functions: e.g. INSTATE (should be turned into a macro)
     - memory protocol semantics
     - 'queuing' internal event semantics
     - concurrency semantics

+ 2 - 2
src/sccd/test/cmd/to_rust.py

@@ -4,9 +4,9 @@ import os
 
 if __name__ == "__main__":
     parser = argparse.ArgumentParser(
-        description="Generate Rust code. Rust code is written to stdout.")
+        description="Generate Rust crate from statechart / class diagram / test model. A statechart or class diagram model becomes a library. A test models becomes a binary (main function runs the test).")
     parser.add_argument('--output', metavar='DIRNAME', type=str, default="codegen", help="Name of directory (Rust crate) to create.")
-    parser.add_argument('path', metavar='PATH', type=str, help="A SCCD statechart or test XML file. A statechart can be compiled to a Rust library. A test can be compiled to a Rust executable (the main-function runs the test).")
+    parser.add_argument('path', metavar='PATH', type=str, help="A SCCD statechart, class diagram or test XML file.")
     args = parser.parse_args()
 
     from sccd.test.codegen.write_crate import write_crate

+ 12 - 3
src/sccd/test/codegen/rust.py

@@ -10,7 +10,7 @@ class TestRustGenerator(ClassDiagramRustGenerator):
     def visit_TestVariant(self, variant):
         variant.cd.accept(self)
 
-        self.w.writeln("pub fn run() {")
+        self.w.writeln("pub fn run_test() {")
         self.w.indent()
 
         self.w.writeln("use sccd::controller;")
@@ -21,21 +21,30 @@ class TestRustGenerator(ClassDiagramRustGenerator):
             self.w.writeln("debug_print_sizes::<controller::TimerId>();")
         self.w.writeln();
 
+        self.w.writeln("// Setup ...")
         self.w.writeln("let mut raised = Vec::<statechart::OutEvent>::new();")
         self.w.writeln("let mut output = |out: statechart::OutEvent| {")
         self.w.writeln("  raised.push(out);")
         self.w.writeln("};")
         self.w.writeln("let mut controller = controller::Controller::<InEvent>::new();")
         self.w.writeln("let mut sc: Statechart::<controller::TimerId> = Default::default();")
+        self.w.writeln()
+        self.w.writeln("// Initialize statechart (execute actions of entering default states)")
         self.w.writeln("sc.init(&mut controller, &mut output);")
+        self.w.writeln()
+        self.w.writeln("// Add test input")
         for i in variant.input:
             if len(i.events) > 1:
                 raise UnsupportedFeature("Multiple simultaneous input events not supported")
             elif len(i.events) == 0:
                 raise UnsupportedFeature("Test declares empty bag of input events")
             self.w.writeln("controller.set_timeout(%d, InEvent::%s);" % (i.timestamp.opt, ident_event_type(i.events[0].name)))
+        self.w.writeln()
 
+        self.w.writeln("// Run simulation, as-fast-as-possible")
         self.w.writeln("controller.run_until(&mut sc, controller::Until::Eternity, &mut output);")
+        self.w.writeln()
+        self.w.writeln("// Check if output is correct")
         self.w.writeln("assert_eq!(raised, [%s]);" % ", ".join('statechart::OutEvent{port:"%s", event:"%s"}' % (e.port, e.name) for o in variant.output for e in o))
 
         self.w.dedent()
@@ -43,7 +52,7 @@ class TestRustGenerator(ClassDiagramRustGenerator):
 
     def visit_Test(self, test):
         for i, v in enumerate(test.variants):
-            self.w.writeln("mod variant%d {" % i)
+            self.w.writeln("mod variant%d {" % (i+1))
             self.w.indent()
             v.accept(self)
             self.w.dedent()
@@ -55,7 +64,7 @@ class TestRustGenerator(ClassDiagramRustGenerator):
         for i, v in enumerate(test.variants):
             self.w.writeln("  eprintln!();")
             self.w.writeln("  eprintln!(\"Test variant %d of %d\");" % (i+1, len(test.variants)))
-            self.w.writeln("  variant%d::run();" % i)
+            self.w.writeln("  variant%d::run_test();" % (i+1))
             self.w.writeln("  eprintln!(\"Passed.\");")
         self.w.writeln("}")
         self.w.writeln()

+ 1 - 5
src/sccd/test/codegen/write_crate.py

@@ -6,11 +6,7 @@ from functools import partial
 import sccd
 RUST_DIR = os.path.dirname(sccd.__file__) + "/../../rust"
 
-# High-level function, that takes a ...
-#  - statechart-xml
-#  - class-diagram-xml
-#  - test-xml
-# ... file and generates from it a Rust crate, which is written to filesystem as a directory.
+# Quick and dirty high-level function, taking a statechart / class diagram / test model in XML format and generating from it a Rust crate, which is written to the filesystem as a directory.
 def write_crate(src, target):
     path = os.path.dirname(src)