Explorar o código

Created SCCD Rust library

Joeri Exelmans %!s(int64=5) %!d(string=hai) anos
pai
achega
6f76cb9440

+ 5 - 0
src/rust/.gitignore

@@ -0,0 +1,5 @@
+target
+
+# Since this package contains only libraries, we don't have to check in the lock file
+# according to: https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock

+ 7 - 0
src/rust/Cargo.toml

@@ -0,0 +1,7 @@
+[workspace]
+
+members = [
+  "action_lang",
+  "statechart",
+  "controller",
+]

+ 11 - 0
src/rust/action_lang/Cargo.toml

@@ -0,0 +1,11 @@
+[package]
+name = "action_lang"
+version = "0.1.0"
+authors = ["Joeri Exelmans <joeri.exelmans@gmail.com>"]
+edition = "2018"
+
+[dependencies]
+
+[lib]
+name = "action_lang"
+path = "action_lang.rs"

+ 47 - 0
src/rust/action_lang/action_lang.rs

@@ -0,0 +1,47 @@
+mod sccd {
+  // Helpers used by from-action-lang-generated Rust code
+  mod action_lang {
+    // This macro lets a struct "inherit" the data members of another struct
+    // The inherited struct is added as a struct member and the Deref and DerefMut
+    // traits are implemented to return a reference to the base struct
+    #[macro_export]
+    macro_rules! inherit_struct {
+      ($name: ident ($base: ty) { $($element: ident: $ty: ty),* $(,)? } ) => {
+        #[derive(Copy, Clone)]
+        struct $name {
+          _base: $base,
+          $($element: $ty),*
+        }
+        impl Deref for $name {
+          type Target = $base;
+          fn deref(&self) -> &$base {
+            &self._base
+          }
+        }
+        impl DerefMut for $name {
+          fn deref_mut(&mut self) -> &mut $base {
+            &mut self._base
+          }
+        }
+      }
+    }
+
+    // "Base struct" for all scopes
+    #[derive(Copy, Clone)]
+    pub struct Empty{}
+
+    // A closure object is a pair of a functions first argument and that function.
+    // The call may be part of an larger expression, and therefore we cannot just write 'let' statements to assign the pair's elements to identifiers which we need for the call.
+    // This macro does exactly that, in an anonymous Rust closure, which is immediately called.
+    #[macro_export]
+    macro_rules! call_closure {
+      ($closure: expr, $($param: expr),*  $(,)?) => {
+      (||{
+        let scope = &mut $closure.0;
+        let function = &mut $closure.1;
+        return function($($param),* scope);
+      })()
+      };
+    }
+  }
+}

+ 11 - 0
src/rust/controller/Cargo.toml

@@ -0,0 +1,11 @@
+[package]
+name = "controller"
+version = "0.1.0"
+authors = ["Joeri Exelmans <joeri.exelmans@gmail.com>"]
+edition = "2018"
+
+[dependencies]
+
+[lib]
+name = "controller"
+path = "controller.rs"

+ 113 - 0
src/rust/controller/controller.rs

@@ -0,0 +1,113 @@
+mod sccd {
+  // Implementation of "Controller", a primitive for simulation of statecharts.
+  mod controller {
+    use std::collections::BinaryHeap;
+    use std::cmp::Ordering;
+    use std::cmp::Reverse;
+    use std::ops::Deref;
+    use std::ops::DerefMut;
+
+    use sccd::statechart::Timestamp;
+
+    pub type TimerId = u16;
+
+    #[derive(Default, Copy, Clone, Ord, PartialOrd, PartialEq, Eq)]
+    pub struct EntryId {
+      timestamp: Timestamp,
+
+      // This field maintains FIFO order for equally timestamped entries.
+      n: TimerId,
+    }
+
+    pub struct QueueEntry<InEvent> {
+      id: EntryId,
+      event: InEvent,
+    }
+    impl<InEvent> Ord for QueueEntry<InEvent> {
+      fn cmp(&self, other: &Self) -> Ordering {
+        self.id.cmp(&other.id)
+      }
+    }
+    impl<InEvent> PartialOrd for QueueEntry<InEvent> {
+      fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+      }
+    }
+    impl<InEvent> PartialEq for QueueEntry<InEvent> {
+      fn eq(&self, other: &Self) -> bool {
+        self.id == other.id
+      }
+    }
+    impl<InEvent> Eq for QueueEntry<InEvent> {}
+
+    // Right now, "removed" queue entries are put into a second BinaryHeap.
+    // This is not very efficient. Queue entries should be allocated on the heap,
+    // (preferrably using a pool allocator), and directly marked as "removed".
+    // Optionally, if the number of removed items exceeds a threshold, a sweep could remove "removed" entries.
+
+    pub struct Controller<InEvent> {
+      simtime: Timestamp,
+      next_id: TimerId,
+      queue: BinaryHeap<Reverse<QueueEntry<InEvent>>>,
+      removed: BinaryHeap<Reverse<EntryId>>,
+    }
+
+    impl<InEvent> Scheduler<InEvent, EntryId> for Controller<InEvent> {
+      fn set_timeout(&mut self, delay: Timestamp, event: InEvent) -> EntryId {
+        let id = EntryId{ timestamp: self.simtime + delay, n: self.next_id };
+        let entry = QueueEntry::<InEvent>{ id, event };
+        self.queue.push(Reverse(entry));
+        self.next_id += 1; // TODO: will overflow eventually :(
+        return id
+      }
+      fn unset_timeout(&mut self, id: EntryId) {
+        self.removed.push(Reverse(id));
+      }
+    }
+
+    pub enum Until {
+      Timestamp(Timestamp),
+      Eternity,
+    }
+
+    impl<InEvent: Copy> Controller<InEvent> {
+      fn new() -> Self {
+        Self {
+          simtime: 0,
+          next_id: 0,
+          queue: BinaryHeap::with_capacity(8),
+          removed: BinaryHeap::with_capacity(4),
+        }
+      }
+      fn run_until<StatechartType: SC<InEvent, Controller<InEvent>, OutputCallback>, OutputCallback: FnMut(OutEvent)>(&mut self, sc: &mut StatechartType, until: Until, output: &mut OutputCallback) {
+        'running: loop {
+          if let Some(Reverse(entry)) = self.queue.peek() {
+            // Check if event was removed
+            if let Some(Reverse(removed)) = self.removed.peek() {
+              if entry.id == *removed {
+                self.queue.pop();
+                self.removed.pop();
+                continue;
+              }
+            }
+            // Check if event too far in the future
+            if let Until::Timestamp(t) = until {
+              if entry.id.timestamp > t {
+                println!("break, timestamp {}, t {}", entry.id.timestamp, t);
+                break 'running;
+              }
+            }
+            // OK, handle event
+            self.simtime = entry.id.timestamp;
+            // eprintln!("time is now {}", self.simtime);
+            sc.big_step(Some(entry.event), self, output);
+            self.queue.pop();
+          }
+          else {
+            break 'running;
+          }
+        }
+      }
+    }
+  }
+}

+ 11 - 0
src/rust/statechart/Cargo.toml

@@ -0,0 +1,11 @@
+[package]
+name = "statechart"
+version = "0.1.0"
+authors = ["Joeri Exelmans <joeri.exelmans@gmail.com>"]
+edition = "2018"
+
+[dependencies]
+
+[lib]
+name = "statechart"
+path = "statechart.rs"

+ 93 - 0
src/rust/statechart/statechart.rs

@@ -0,0 +1,93 @@
+mod sccd {
+  // Helpers and traits implemented by, or used by from-statechart-generated Rust code.
+  mod statechart {
+
+    pub trait EventLifeline<T> {
+      fn get(&self) -> &T;
+      fn get_mut(&mut self) -> &mut T;
+      fn cycle(&mut self);
+    }
+
+    #[derive(Default)]
+    pub struct SameRoundLifeline<T> {
+      current: T,
+    }
+
+    impl<T: Default> EventLifeline<T> for SameRoundLifeline<T> {
+      fn get(&self) -> &T {
+        &self.current
+      }
+      fn get_mut(&mut self) -> &mut T {
+        &mut self.current
+      }
+      fn cycle(&mut self) {
+        // Reset
+        self.current = Default::default()
+      }
+    }
+
+    enum Which {
+      One,
+      Two,
+    }
+    impl Default for Which {
+      fn default() -> Self {
+        Self::One
+      }
+    }
+
+    #[derive(Default)]
+    pub struct NextRoundLifeline<T> {
+      one: T,
+      two: T,
+
+      current: Which,
+    }
+
+    impl<T: Default> EventLifeline<T> for NextRoundLifeline<T> {
+      fn get(&self) -> &T {
+        match self.current {
+          Which::One => &self.one,
+          Which::Two => &self.two,
+        }
+      }
+      fn get_mut(&mut self) -> &mut T {
+        match self.current {
+          Which::One => &mut self.one,
+          Which::Two => &mut self.two,
+        }
+      }
+      fn cycle(&mut self) {
+        match self.current {
+          Which::One => {
+            self.one = Default::default();
+            self.current = Which::Two;
+          },
+          Which::Two => {
+            self.two = Default::default();
+            self.current = Which::One;
+          },
+        };
+      }
+    }
+
+    pub type Timestamp = u32;
+
+    pub trait Scheduler<InEvent, EntryId> {
+      fn set_timeout(&mut self, delay: Timestamp, event: InEvent) -> EntryId;
+      fn unset_timeout(&mut self, id: EntryId);
+    }
+
+    pub trait SC<InEvent, EntryId, Sched: Scheduler<InEvent, EntryId>, OutputCallback> {
+      fn init(&mut self, sched: &mut Sched, output: &mut OutputCallback);
+      fn big_step(&mut self, event: Option<InEvent>, sched: &mut Sched, output: &mut OutputCallback);
+    }
+
+    // TODO: Does not belong in "common", this should become a statechart-specific enum-type.
+    #[derive(Debug, Eq, PartialEq)]
+    pub struct OutEvent {
+      port: &'static str,
+      event: &'static str,
+    }
+  }
+}