소스 검색

Rust: progress with outputting events

Joeri Exelmans 4 년 전
부모
커밋
eca28025e7
4개의 변경된 파일120개의 추가작업 그리고 78개의 파일을 삭제
  1. 34 16
      src/sccd/cd/codegen/sccdlib.rs
  2. 7 5
      src/sccd/statechart/codegen/code_generation.txt
  3. 71 56
      src/sccd/statechart/codegen/rust.py
  4. 8 1
      src/sccd/test/codegen/rust.py

+ 34 - 16
src/sccd/cd/codegen/sccdlib.rs

@@ -3,17 +3,27 @@ use std::collections::binary_heap::PeekMut;
 use std::cmp::Ordering;
 
 type Timestamp = usize; // unsigned integer, platform's word size
+type OutputCallback = fn(&str, &str);
 
-trait SC<EventType> {
-  fn fair_step(&mut self, event: Option<EventType>);
+pub trait State {
+  fn enter_actions(output: OutputCallback);
+  fn exit_actions(output: OutputCallback);
+  fn enter_default(output: OutputCallback);
+
+  fn exit_current(&self, output: OutputCallback);
+}
+
+pub trait SC<EventType> {
+  fn init(&self, output: OutputCallback);
+  fn fair_step(&mut self, event: Option<EventType>, output: OutputCallback);
 }
 
-enum Target<'a, EventType> {
+pub enum Target<'a, EventType> {
   Narrowcast(&'a mut dyn SC<EventType>),
   Broadcast,
 }
 
-struct Entry<'a, EventType> {
+pub struct Entry<'a, EventType> {
   timestamp: Timestamp,
   event: EventType,
   target: Target<'a, EventType>,
@@ -40,26 +50,34 @@ impl<'a, EventType> PartialEq for Entry<'a, EventType> {
 impl<'a, EventType> Eq for Entry<'a, EventType> {}
 
 
-struct Controller<'a, EventType> {
+pub struct Controller<'a, EventType> {
   queue: BinaryHeap<Entry<'a, EventType>>,
   simtime: Timestamp,
+  output_callback: fn(&str, &str),
 }
 
-impl<'a, EventType> Default for Controller<'a, EventType> {
-  fn default() -> Self {
-    Self {
-      queue: BinaryHeap::new(),
-      simtime: 0,
-    }
-  }
-}
+// impl<'a, EventType> Default for Controller<'a, EventType> {
+//   fn default() -> Self {
+//     Self {
+//       queue: BinaryHeap::new(),
+//       simtime: 0,
+//     }
+//   }
+// }
 
-enum Until {
+pub enum Until {
   Timestamp(Timestamp),
   Eternity,
 }
 
 impl<'a, EventType: Copy> Controller<'a, EventType> {
+  fn new(output_callback: fn(&str, &str)) -> Self {
+    Self {
+      queue: BinaryHeap::new(),
+      simtime: 0,
+      output_callback,
+    }
+  }
   fn add_input(&mut self, entry: Entry<'a, EventType>) {
     self.queue.push(entry);
   }
@@ -81,14 +99,14 @@ impl<'a, EventType: Copy> Controller<'a, EventType> {
 
           match &mut entry.target {
             Target::Narrowcast(sc) => {
-              sc.fair_step(Some(e));
+              sc.fair_step(Some(e), self.output_callback);
             },
             Target::Broadcast => {
               println!("broadcast not implemented!")
             },
           };
 
-          PeekMut::<'_, Entry<'_, EventType>>::pop(entry);
+          PeekMut::<'_, Entry<'a, EventType>>::pop(entry);
         },
         None => { break 'running; },
       }

+ 7 - 5
src/sccd/statechart/codegen/code_generation.txt

@@ -9,19 +9,21 @@ Roadmap
 
   Milestone 1: Initial compilation to Rust
   
-    - entering and exiting states correctly
+    - (DONE) entering and exiting states correctly
+    - (DONE) incoming event triggers the right transition
+    - a port of the Controller class to Rust
+    - one implemented action: raise output event
+
     - no history
     - no action language
       - guards evals and action stmts are just logged to console
       - guards always true, actions no effect
-
-    - incoming event triggers the right transition
-    
     - no event parameters
 
     - main operation:
-       statechart_process(:StatechartState, :Event) -> StatechartState
+       fair_step(:StatechartState, :Event) -> StatechartState
           old state -> new state
+        = "Take One"-maximality big-step
           
     - fixed semantics (YAKINDU-like)
 

+ 71 - 56
src/sccd/statechart/codegen/rust.py

@@ -43,6 +43,13 @@ def ident_arena_label(state: State) -> str:
     else:
         return "arena" + snake_case(state)
 
+def compile_actions(actions: List[Action], w: IndentingWriter):
+    for a in actions:
+        if isinstance(a, RaiseOutputEvent):
+            # TODO: evaluate event parameters
+            w.writeln("output(\"%s\", \"%s\");" % (a.outport, a.name))
+        else:
+            w.writeln("println!(\"UNIMPLEMENTED: %s\");" % a.render())
 
 def compile_statechart(sc: Statechart, globals: Globals, w: IndentingWriter):
 
@@ -104,50 +111,9 @@ def compile_statechart(sc: Statechart, globals: Globals, w: IndentingWriter):
 
     # Write "enter/exit state" functions
 
-    # This fragment should be moved to a library:
-    w.writeln("pub trait State {")
-    w.writeln("  fn enter_actions();")
-    w.writeln("  fn exit_actions();")
-    w.writeln("  fn enter_default();")
-    w.writeln("}")
-    w.writeln()
-
-    def write_enter_exit(state: State, children: List[State]):
-        if isinstance(state, HistoryState):
-            return None # we got no time for pseudo-states!
-
-        w.writeln("impl State for %s {" % ident_type(state))
-
-        w.writeln("  fn enter_actions() {")
-        w.writeln("    println!(\"enter %s\");" % state.opt.full_name);
-        for a in state.enter:
-            w.writeln("    println!(\"%s\");" % a.render())
-        w.writeln("  }")
-
-        w.writeln("  fn exit_actions() {")
-        w.writeln("    println!(\"exit %s\");" % state.opt.full_name);
-        for a in state.exit:
-            w.writeln("    println!(\"%s\");" % a.render())
-        w.writeln("  }")
-
-        w.writeln("  fn enter_default() {")
-        w.writeln("    %s::enter_actions();" % ident_type(state))
-        if isinstance(state, ParallelState):
-            for child in children:
-                w.writeln("    %s::enter_default();" % ident_type(child))
-        else:
-            if state.default_state is not None:
-                w.writeln("    %s::enter_default();" % ident_type(state.default_state))
-        w.writeln("  }")
-
-        w.writeln("}")
-        w.writeln()
-        return state
+    # Write "default" constructor
 
-
-    # Write "enter default state" functions
-
-    def write_enter_default(state: State, children: List[State]):
+    def write_default(state: State, children: List[State]):
         if isinstance(state, HistoryState):
             return None # we got no time for pseudo-states!
 
@@ -175,11 +141,57 @@ def compile_statechart(sc: Statechart, globals: Globals, w: IndentingWriter):
         w.writeln()
         return state
 
+    def write_enter_exit(state: State, children: List[State]):
+        if isinstance(state, HistoryState):
+            return None # we got no time for pseudo-states!
+
+        w.writeln("impl State for %s {" % ident_type(state))
+
+        w.writeln("  fn enter_actions(output: OutputCallback) {")
+        w.writeln("    println!(\"enter %s\");" % state.opt.full_name);
+        w.indent(); w.indent()
+        compile_actions(state.enter, w)
+        w.dedent(); w.dedent()
+        w.writeln("  }")
+
+        w.writeln("  fn exit_actions(output: OutputCallback) {")
+        w.writeln("    println!(\"exit %s\");" % state.opt.full_name);
+        w.indent(); w.indent()
+        compile_actions(state.exit, w)
+        w.dedent(); w.dedent()
+        w.writeln("  }")
+
+        w.writeln("  fn enter_default(output: OutputCallback) {")
+        w.writeln("    %s::enter_actions(output);" % ident_type(state))
+        if isinstance(state, ParallelState):
+            for child in children:
+                w.writeln("    %s::enter_default(output);" % ident_type(child))
+        else:
+            if state.default_state is not None:
+                w.writeln("    %s::enter_default(output);" % ident_type(state.default_state))
+        w.writeln("  }")
+
+        w.writeln("  fn exit_current(&self, output: OutputCallback) {")
+        if isinstance(state, ParallelState):
+            for child in children:
+                w.writeln("    self.%s.exit_current(output);" % ident_field(child));
+        else:
+            if len(children) > 0:
+                w.writeln("    match self {")
+                for child in children:
+                    w.writeln("      Self::%s(s) => { s.exit_current(output); }," % ident_enum_variant(child))
+                w.writeln("    }")
+        w.writeln("  }")
+
+        w.writeln("}")
+        w.writeln()
+        return state
+
     visit_tree(tree.root, lambda s: s.children,
         child_first=[
             write_state_type,
+            write_default,
             write_enter_exit,
-            write_enter_default,
         ])
 
     # Write event type
@@ -209,9 +221,13 @@ def compile_statechart(sc: Statechart, globals: Globals, w: IndentingWriter):
     w.writeln("    };")
     w.writeln("  }")
     w.writeln("}")
+    w.writeln()
 
     w.writeln("impl SC<Event> for Statechart {")
-    w.writeln("  fn fair_step(&mut self, event: Option<Event>) {")
+    w.writeln("  fn init(&self, output: OutputCallback) {")
+    w.writeln("    %s::enter_default(output);" % ident_type(tree.root))
+    w.writeln("  }")
+    w.writeln("  fn fair_step(&mut self, event: Option<Event>, output: OutputCallback) {")
     w.writeln("    println!(\"fair step\");")
     w.writeln("    let %s = &mut self.current_state;" % ident_var(tree.root))
 
@@ -234,7 +250,7 @@ def compile_statechart(sc: Statechart, globals: Globals, w: IndentingWriter):
         # (2) The descendants of S, if S is the transition target
         def write_exit(exit_path: List[State]):
             if len(exit_path) == 0:
-                w.writeln("%s.exit();" % ident_var(s))
+                w.writeln("%s.exit_current(output);" % ident_var(s))
             else:
                 s = exit_path[0]
                 if isinstance(s, HistoryState):
@@ -244,12 +260,12 @@ def compile_statechart(sc: Statechart, globals: Globals, w: IndentingWriter):
                         if exit_path[1] is c:
                             write_exit(exit_path[1:]) # continue recursively
                         else:
-                            w.writeln("%s.exit();" % ident_var(c))
+                            w.writeln("%s.exit_current(output);" % ident_var(c))
                 elif isinstance(s, State):
                     if s.default_state is not None:
                         # Or-state
                         write_exit(exit_path[1:]) # continue recursively with the next child on the exit path
-                w.writeln("%s::exit_actions();" % ident_type(s))
+                w.writeln("%s::exit_actions(output);" % ident_type(s))
 
         def write_new_configuration(enter_path: List[State]):
             if len(enter_path) > 0:
@@ -285,19 +301,19 @@ def compile_statechart(sc: Statechart, globals: Globals, w: IndentingWriter):
                 s = enter_path[0]
                 if len(enter_path) == 1:
                     # Target state.
-                    w.writeln("%s::enter_default();" % ident_type(s))
+                    w.writeln("%s::enter_default(output);" % ident_type(s))
                 else:
                     if isinstance(s, ParallelState):
                         for c in s.children:
                             if enter_path[1] is c:
-                                w.writeln("%s::enter_actions();" % ident_type(c))
+                                w.writeln("%s::enter_actions(output);" % ident_type(c))
                                 write_enter(enter_path[1:]) # continue recursively
                             else:
-                                w.writeln("%s::enter_default();" % ident_type(c))
+                                w.writeln("%s::enter_default(output);" % ident_type(c))
                     elif isinstance(s, State):
                         if len(s.children) > 0:
                             # Or-state
-                            w.writeln("%s::enter_actions();" & ident_type(s))
+                            w.writeln("%s::enter_actions(output);" & ident_type(s))
                             write_enter(enter_path[1:]) # continue recursively with the next child on the enter path
                         else:
                             # The following should never occur
@@ -331,8 +347,7 @@ def compile_statechart(sc: Statechart, globals: Globals, w: IndentingWriter):
 
                 if len(t.actions) > 0:
                     w.writeln("// Transition's actions")
-                    for a in t.actions:
-                        w.writeln("println!(%s);" % a.render())
+                    compile_actions(t.actions, w)
 
                 w.writeln("// Enter actions")
                 write_enter(enter_path)
@@ -366,8 +381,8 @@ def compile_statechart(sc: Statechart, globals: Globals, w: IndentingWriter):
                     w.writeln("match %s {" % ident_var(state))
                     for child in state.children:
                         w.indent()
-                        # w.writeln("%s::%s(%s) => {" % (ident_type(state), ident_enum_variant(child), ident_var(child)))
-                        w.writeln("%s::%s(_) => {" % (ident_type(state), ident_enum_variant(child)))
+                        w.writeln("%s::%s(%s) => {" % (ident_type(state), ident_enum_variant(child), ident_var(child)))
+                        # w.writeln("%s::%s(_) => {" % (ident_type(state), ident_enum_variant(child)))
                         w.indent()
                         write_transitions(child)
                         w.dedent()

+ 8 - 1
src/sccd/test/codegen/rust.py

@@ -22,8 +22,15 @@ def compile_test(variants: List[TestVariant], w: IndentingWriter):
 
     for v in variants:
         w.writeln("// Test variant")
-        w.writeln("let mut controller: Controller<Event> = Default::default();")
+        # w.writeln("let mut raised = Vec::<&str>::new();")
+        # w.writeln("let output = |port, event| {")
+        w.writeln("fn output(port: &str, event: &str) {")
+        w.writeln("  println!(\"^{}:{}\", port, event);")
+        # w.writeln("  raised.push(event);")
+        w.writeln("}")
+        w.writeln("let mut controller = Controller::<Event>::new(output);")
         w.writeln("let mut sc: Statechart = Default::default();")
+        w.writeln("sc.init(output);")
         for i in v.input:
             if len(i.events) > 1:
                 raise Exception("Multiple simultaneous input events not supported")