diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index c26b651..d2a0934 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -github: [FL03] +github: [ FL03 ] diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d1b4660..cf186bb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,16 +3,16 @@ updates: - package-ecosystem: cargo directory: / schedule: - interval: weekly + interval: monthly - package-ecosystem: github-actions directory: / schedule: - interval: weekly + interval: monthly - package-ecosystem: cargo directory: /rstm schedule: interval: weekly - package-ecosystem: cargo - directory: /rstm-core + directory: /core schedule: interval: weekly diff --git a/.github/workflows/crates.yml b/.github/workflows/crates.yml index e9c761d..f689017 100644 --- a/.github/workflows/crates.yml +++ b/.github/workflows/crates.yml @@ -6,6 +6,7 @@ concurrency: env: BASENAME: ${{ github.event.repository.name }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} CARGO_TERM_COLOR: always on: @@ -19,43 +20,18 @@ jobs: base: env: CRATE_NAME: ${{ github.event.repository.name }}-${{ matrix.suffix }} - name: Publish (${{ matrix.suffix }}) + name: Publish Base Modules runs-on: ubuntu-latest strategy: matrix: suffix: [ core ] steps: - uses: actions/checkout@v4 - - name: cache - uses: actions/cache@v4 - with: - key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} - path: | - ~/.cargo/registry - ~/.cargo/git - target/debug - target/release - restore-keys: | - cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} - cargo-${{ runner.os }}- - cargo- - - run: cargo publish --all-features -v -p ${{ env.CRATE_NAME }} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + - run: cargo publish --all-features -v -p ${{ env.CRATE_NAME }} publish: - name: Publish (${{ github.event.repository.name }}) + name: Publish SDK needs: base runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: cache - uses: actions/cache@v4 - with: - key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} - path: | - ~/.cargo/registry - ~/.cargo/git - target/release - restore-keys: | - cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} - cargo-${{ runner.os }}- - cargo- - run: cargo publish --all-features -v -p ${{ github.event.repository.name }} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 932548a..2b72ebc 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -10,11 +10,9 @@ env: on: pull_request: - branches: [ main ] - push: - tags: [ v*.*.* ] + branches: [ main, v*.*.* ] release: - types: [ published ] + types: [ created ] repository_dispatch: types: [ rust ] schedule: @@ -28,33 +26,31 @@ jobs: name: Build strategy: matrix: - platform: [ ubuntu-latest ] + platform: [ macos-latest, ubuntu-latest, windows-latest ] toolchain: [ stable, nightly ] runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v4 - - name: setup (rustup) + - name: rustup run: | rustup default ${{ matrix.toolchain }} rustup update - - name: build (workspace) + - name: build run: cargo build --all-features -r -v --workspace - - name: cache - uses: actions/cache@v4 + - name: test + run: cargo test -v --workspace -F full + - name: benchmark + if: matrix.toolchain == 'nightly' + run: cargo bench -F full -v --workspace + - uses: actions/cache@v4 with: - key: cargo-${{ matrix.toolchain }}-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} + key: ${{ runner.os }}-cargo-${{ matrix.toolchain }}-${{ hashFiles('**/Cargo.lock') }} path: | ~/.cargo/registry ~/.cargo/git target/debug target/release restore-keys: | - cargo-${{ matrix.toolchain }}-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} - cargo-${{ matrix.toolchain }}-${{ runner.os }}- - cargo-${{ matrix.toolchain }}- - - name: cargo test (workspace) --full - run: cargo test -v --workspace -F full - - name: cargo (bench) - if: matrix.toolchain == 'nightly' - run: cargo bench -F full -v --workspace - + ${{ runner.os }}-cargo-${{ matrix.toolchain }}- + ${{ runner.os }}-cargo- + ${{ runner.os }}- diff --git a/Cargo.toml b/Cargo.toml index 40a9b7d..93e4efa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ keywords = [ "turing", "turing-machine", "utm" ] license = "Apache-2.0" readme = "README.md" repository = "https://github.com/FL03/rstm.git" -version = "0.0.3" +version = "0.0.4" [profile.dev] opt-level = 0 diff --git a/README.md b/README.md index 270d810..98fc100 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,17 @@ # rstm - +[![license](https://img.shields.io/crates/l/rstm.svg)](https://crates.io/crates/rstm) [![crates.io](https://img.shields.io/crates/v/rstm.svg)](https://crates.io/crates/rstm) [![docs.rs](https://docs.rs/rstm/badge.svg)](https://docs.rs/rstm) + [![clippy](https://github.com/FL03/rstm/actions/workflows/clippy.yml/badge.svg)](https://github.com/FL03/rstm/actions/workflows/clippy.yml) [![rust](https://github.com/FL03/rstm/actions/workflows/rust.yml/badge.svg)](https://github.com/FL03/rstm/actions/workflows/rust.yml) -[![license](https://img.shields.io/crates/l/rstm.svg)](https://crates.io/crates/rstm) -[![lines of code](https://tokei.rs/b1/github/FL03/rstm?category=code)](https://tokei.rs/b1/github/FL03/rstm?category=code) - *** _**The library is currently in the early stages of development and is still settling in on a feel for the api.**_ -This library focuses on building concrete implementations for Turing Machines. +Welcome to `rstm`! This crate provides a simple and easy-to-use interface for creating and executing Turing machines. The crate is designed to be flexible and extensible, allowing developers to create and execute a wide range of Turing machines. Furthermore, the crate focuses on efficiency and leverages feature-gating to reduce overhead. ## Getting Started @@ -33,17 +31,19 @@ cargo build --all-features --workspace #### _Run an example_ ```bash -cargo run -f F --example actor +cargo run -f F --example {actor} ``` ## Usage -### Creating a new ruleset +### Rulesets -Programs are essentially collections of rules that define the behavior of the machine. Facilitating the creation of these rules is the `ruleset!` macro. The macro allows developers to define a set of rules for the machine in a concise and readable manner while further emulating the transition function defined by "On topological dynamics of Turing machines" by Petr Kůrka; `δ: Q x A -> Q x A x {-1, 0, 1}.` +To faciliate the creation of rules for the machine, the crate provides a `ruleset!` macro. The macro mimics the +structure of the transition function $\delta$ defined by "On topological dynamics of Turing machines" by Petr Kůrka. +$$\delta : Q\times{A}\rarr{Q\times{A}\times{(0, \pm{1})}}$$ -`ruleset!` is a macro that allows you to define a set of rules for the machine. The syntax is as follows: +The syntax of the macro is as follows: ```rust ruleset![ @@ -52,11 +52,37 @@ Programs are essentially collections of rules that define the behavior of the ma ] ``` -The macro is hygenic, meaning developers will not need to import the `Direction` enum nor its variants in order to use the macro. +The macro expands into a `Vec` where `Rule` is structure consisting of two other structures, namely: `Head` and the `Tail`. Each of these structures is a direct representation of the two sides of the transition function defined above +#### _Rules_ +```rust + pub struct Rule { + pub head: Head, + pub tail: Tail, + } +``` -#### Example: Building a ruleset for a three-state, two-symbol Turing machine +where `Head` and `Tail` are defined as follows: + +```rust + pub struct Head { + pub state: Q, + pub symbol: S, + } + + pub struct Tail { + pub direction: Direction, + pub state: Q, + pub symbol: S, + } +``` + +**Note:** the macro is hygenic, meaning developers will not need to import the `Direction` enum nor its variants in order to use the macro. + +#### _Example usage_ + +The following example demonstrates the use of the `ruleset!` macro to define a set of rules for a three-state, two-symbol Turing machine. ```rust use rstm::ruleset; @@ -74,6 +100,8 @@ The macro is hygenic, meaning developers will not need to import the `Direction` ### Examples +#### _Executing a program using an `Actor`_ + ```rust extern crate rstm; @@ -81,11 +109,10 @@ The macro is hygenic, meaning developers will not need to import the `Direction` fn main() -> Result<(), Box> { tracing_subscriber::fmt().with_target(false).init(); - // initialize the tape data - let alpha = vec![0i8; 10]; + let alpha = vec![0u8; 10]; // initialize the state of the machine - let initial_state = State(0); + let initial_state = State::::default(); // define the ruleset for the machine let rules = ruleset![ (0, 0) -> Right(1, 0), @@ -95,14 +122,11 @@ The macro is hygenic, meaning developers will not need to import the `Direction` (-1, 0) -> Left(0, 0), (-1, 1) -> Left(1, 1), ]; - - let program = Program::new() - .initial_state(initial_state) - .rules(rules) - .build(); - + // create a new program from the ruleset + let program = Program::from_iter(rules); // create a new instance of the machine - let tm = dbg!(Actor::from_state(initial_state).with_tape(alpha)); + let tm = dbg!(Actor::new(alpha, initial_state, 0)); + // execute the program tm.execute(program).run()?; Ok(()) } @@ -111,8 +135,3 @@ The macro is hygenic, meaning developers will not need to import the `Direction` ## Contributing Pull requests are welcome. Any improvements or modifactions should first be disccussed using a pull-request and/or by opening an issue. Additionally, please make sure to update tests as appropriate and to adhear to the feature gates. - -## License - -* [Apache-2.0](https://choosealicense.com/licenses/apache-2.0/) -* [MIT](https://choosealicense.com/licenses/mit/) diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/clippy.toml @@ -0,0 +1 @@ + diff --git a/core/Cargo.toml b/core/Cargo.toml index 48eb54e..a6fa2ad 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -14,7 +14,7 @@ version.workspace = true [features] default = [ - "alloc", + "std", ] full = [ @@ -44,11 +44,11 @@ tracing = [ ] # ********* [FF] Environments ********* -# std = [ -# "alloc", -# "serde?/std", -# "strum/std", -# ] +std = [ + "alloc", + "serde?/std", + "strum/std", +] [lib] bench = true @@ -56,6 +56,18 @@ crate-type = ["cdylib", "rlib"] doctest = false test = true +[[test]] +name = "actor" +required-features = ["alloc"] + +[[test]] +name = "rules" +required-features = ["alloc"] + +[[test]] +name = "tape" +required-features = ["alloc"] + [dependencies] thiserror.workspace = true paste.workspace = true diff --git a/core/src/actors/actor.rs b/core/src/actors/actor.rs index e0f6cb9..34d5ad1 100644 --- a/core/src/actors/actor.rs +++ b/core/src/actors/actor.rs @@ -2,52 +2,77 @@ Appellation: actor Contrib: FL03 */ -#[doc(inline)] -pub use self::builder::ActorBuilder; - use super::Executor; -use crate::rules::Program; -use crate::{Direction, Error, Head, State, Tail}; +use crate::rules::Ruleset; +use crate::{Direction, Error, Head, State}; -/// An [Actor] describes a Turing machine with a moving head (TMH). +/// An [Actor] is an implementation of a Turing machine with a moving head (TMH). +/// +/// The model contains the following components: /// -/// [Actor]'s abstractly define actionable surfaces capable of executing a [Program]. +/// - `alpha`: the input alphabet +/// - `head`: the head of the tape #[derive(Clone, Default, Eq, Hash, PartialEq, PartialOrd)] -pub struct Actor { - /// the input alphabet - pub(crate) alpha: Vec, +pub struct Actor { /// the head of the tape pub(crate) head: Head, + /// the input alphabet + pub(crate) tape: Vec, } -impl Actor { - pub fn new() -> ActorBuilder { - ActorBuilder::new() +impl Actor { + pub fn new(alpha: impl IntoIterator, state: State, symbol: usize) -> Self { + Self { + head: Head { state, symbol }, + tape: Vec::from_iter(alpha), + } } /// Constructs a new [Actor] with the given state; assumes the tape is empty and the head /// is located at `0`. - pub fn from_state(State(state): State) -> Self { + pub fn from_state(state: State) -> Self { Self { - alpha: Vec::new(), - head: Head { - state: State(state), - symbol: 0, - }, + head: Head { state, symbol: 0 }, + tape: Vec::new(), } } - - pub fn with_tape(self, alpha: I) -> Self + /// Consumes the current instance and returns a new instance with the given alphabet + pub fn with_alpha(self, alpha: I) -> Self where - I: IntoIterator, + I: IntoIterator, { Self { - alpha: Vec::from_iter(alpha), + tape: Vec::from_iter(alpha), + ..self + } + } + /// Consumes the current instance and returns a new instance with the given head + pub fn with_head(self, head: Head) -> Self { + Self { head, ..self } + } + /// Consumes the current instance and returns a new instance with the given position + pub fn with_position(self, symbol: usize) -> Self { + Self { + head: Head { + symbol, + ..self.head + }, + ..self + } + } + /// Consumes the current instance and returns a new instance with the given state + pub fn with_state(self, state: State) -> Self { + Self { + head: Head { state, ..self.head }, ..self } } - /// Returns an immutable reference to the tape alphabet as a slice - pub fn alpha(&self) -> &[S] { - &self.alpha + /// Returns an immutable reference to the tape, as a slice + pub fn tape(&self) -> &[A] { + &self.tape + } + /// Returns a mutable reference of the tape as a slice + pub fn tape_mu(&mut self) -> &mut [A] { + &mut self.tape } /// Returns an immutable reference to the head of the tape pub const fn head(&self) -> &Head { @@ -65,59 +90,51 @@ impl Actor { symbol: self.head.symbol, } } + /// Returns the current position of the head on the tape + pub fn position(&self) -> usize { + self.head().symbol + } /// Returns an instance of the state with an immutable reference to the inner value pub fn state(&self) -> State<&Q> { - self.head.state() + self.head().state() } /// Returns an instance of the state with a mutable reference to the inner value pub fn state_mut(&mut self) -> State<&mut Q> { - self.head.state_mut() + self.head_mut().state_mut() } /// Executes the given program; the method is lazy, meaning it will not compute immediately /// but will return an [Executor] that is better suited for managing the runtime. - pub fn execute(self, program: Program) -> Executor { + pub fn execute(self, program: Ruleset) -> Executor { Executor::new(self, program) } - /// Reads the current symbol at the head of the tape - #[cfg_attr( - feature = "tracing", - tracing::instrument(skip_all, name = "get", target = "actor") - )] - pub fn get(&self) -> Option<&S> { - self.alpha().get(self.position()) - } /// Checks if the tape is empty pub fn is_empty(&self) -> bool { - self.alpha.is_empty() + self.tape.is_empty() } /// Checks if the tape is halted pub fn is_halted(&self) -> bool where Q: 'static, { - self.head.state.is_halt() + self.head().state.is_halt() } /// Returns the length of the tape #[inline] pub fn len(&self) -> usize { - self.alpha.len() - } - /// Returns the current position of the head on the tape - pub fn position(&self) -> usize { - self.head.symbol + self.tape.len() } /// Reads the current symbol at the head of the tape #[cfg_attr( feature = "tracing", tracing::instrument(skip_all, name = "read", target = "actor") )] - pub fn read(&self) -> Result, Error> { + pub fn read(&self) -> Result, Error> { #[cfg(feature = "tracing")] tracing::trace!("Reading the tape..."); - self.alpha + self.tape .get(self.position()) .map(|symbol| Head { - state: self.head.state(), + state: self.state(), symbol, }) .ok_or(Error::index_out_of_bounds(self.position(), self.len())) @@ -128,7 +145,7 @@ impl Actor { feature = "tracing", tracing::instrument(skip_all, name = "write", target = "actor") )] - pub fn write(&mut self, value: S) { + pub fn write(&mut self, value: A) { #[cfg(feature = "tracing")] tracing::trace!("Writing to the tape..."); let pos = self.position(); @@ -136,34 +153,47 @@ impl Actor { if pos == usize::MAX { #[cfg(feature = "tracing")] tracing::trace!("Prepending to the tape..."); + // prepend to the tape + self.tape.insert(0, value); } else if pos >= self.len() { #[cfg(feature = "tracing")] tracing::trace!("Appending to the tape..."); // append to the tape - self.alpha.push(value); + self.tape.push(value); } else { - self.alpha[pos] = value; + self.tape[pos] = value; } } - /// Performs a single step of the Turing machine + /// Performs a single step of the Turing machine; returns the previous head of the tape. + /// Each step writes the given symbol to the tape, updates the state of the head, and moves + /// the head by a single unit in the specified direction. #[cfg_attr( feature = "tracing", tracing::instrument(skip_all, name = "handle", target = "actor") )] - pub(crate) fn handle(&mut self, direction: Direction, State(state): State, symbol: S) { + pub(crate) fn step( + &mut self, + direction: Direction, + state: State, + symbol: A, + ) -> Head { #[cfg(feature = "tracing")] tracing::trace!("Transitioning the actor..."); // write the symbol to the tape self.write(symbol); // update the head of the actor - self.head = Head { - state: State(state), - symbol: direction.apply_unsigned(self.head.symbol), - }; + self.head.replace(state, self.position() + direction) } +} - pub(crate) fn process(&mut self, rule: Tail) { - self.handle(rule.direction, rule.state, rule.symbol); +use crate::traits::transform::Handle; +use crate::Tail; + +impl Handle> for Actor { + type Output = Head; + + fn handle(&mut self, args: Tail) -> Self::Output { + self.step(args.direction, args.state, args.symbol) } } @@ -172,7 +202,7 @@ where S: core::fmt::Debug, { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - for (i, c) in self.alpha.iter().enumerate() { + for (i, c) in self.tape.iter().enumerate() { if i == self.position() { write!(f, "[{c:?}]")?; } else { @@ -188,7 +218,7 @@ where S: core::fmt::Display, { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - for (i, c) in self.alpha.iter().enumerate() { + for (i, c) in self.tape.iter().enumerate() { if i == self.position() { write!(f, "[{c}]")?; } else { @@ -198,72 +228,3 @@ where Ok(()) } } - -mod builder { - use super::*; - use core::iter::FromIterator; - - #[derive(Default)] - pub struct ActorBuilder { - alpha: Vec, - state: Option>, - symbol: usize, - } - - impl ActorBuilder { - pub(crate) fn new() -> Self { - Self { - alpha: Vec::new(), - state: None, - symbol: 0, - } - } - - pub fn alpha(self, alpha: I) -> Self - where - I: IntoIterator, - { - Self { - alpha: Vec::from_iter(alpha), - ..self - } - } - - pub fn head(self, head: Head) -> Self { - Self { - state: Some(head.state), - symbol: head.symbol, - ..self - } - } - - pub fn state(self, State(state): State) -> Self { - Self { - state: Some(State(state)), - ..self - } - } - - pub fn position(self, symbol: usize) -> Self { - Self { symbol, ..self } - } - - pub fn build(self) -> Actor - where - Q: Default, - { - let ActorBuilder { - alpha, - state, - symbol, - } = self; - Actor { - alpha, - head: Head { - state: state.unwrap_or_default(), - symbol, - }, - } - } - } -} diff --git a/core/src/actors/exec.rs b/core/src/actors/exec.rs index 3207ed9..e33571c 100644 --- a/core/src/actors/exec.rs +++ b/core/src/actors/exec.rs @@ -3,18 +3,24 @@ Contrib: FL03 */ use super::Actor; -use crate::{Error, Head, Program, Symbolic}; +use crate::{Error, Head, Ruleset, State, Symbolic}; -/// +/// # [Executor] +/// +/// The [Executor] struct is directly responsible for the execution of a program. From a rustic +/// perspective, the [Executor] is an iterator that reads the current symbol at the head of +/// the tape, pub struct Executor { + /// the actor that will be executing the program pub(crate) actor: Actor, - pub(crate) program: Program, + /// the program being executed + pub(crate) program: Ruleset, /// the number of steps taken by the actor pub(crate) steps: usize, } impl Executor { - pub(crate) fn new(actor: Actor, program: Program) -> Self { + pub(crate) fn new(actor: Actor, program: Ruleset) -> Self { Self { actor, program, @@ -28,7 +34,7 @@ impl Executor { { Self { actor, - program: Program { + program: Ruleset { initial_state: Default::default(), rules: Vec::new(), }, @@ -36,13 +42,17 @@ impl Executor { } } /// Load a program into the executor - pub fn load(self, program: Program) -> Self { + pub fn load(self, program: Ruleset) -> Self { Executor { program, ..self } } pub const fn actor(&self) -> &Actor { &self.actor } + + pub fn current_state(&self) -> State<&'_ Q> { + self.actor.state() + } /// Reads the current symbol at the head of the tape pub fn read(&self) -> Result, Error> { self.actor.read() @@ -90,18 +100,24 @@ where } // read the tape let head = if let Ok(cur) = self.read() { - cur.cloned() + cur } else { #[cfg(feature = "tracing")] - tracing::warn!("Unable to locate the value of the head..."); - Head::from_state(self.actor.state().cloned()) + tracing::warn!("[Index Error] the current position ({pos}) of the head is out of bounds, assuming the symbol to be its default value...", pos = self.actor.head.symbol); + Head { + state: self.actor.state(), + symbol: &S::default(), + } }; // execute the program - if let Some(tail) = self.program.get(&head).cloned() { + if let Some(tail) = self.program.get(head.state, head.symbol) { + let next = tail.as_head().cloned(); // process the instruction - self.actor.process(tail.clone()); + let _prev = self + .actor + .step(tail.direction, tail.state.clone(), tail.symbol); // return the head - return Some(tail.into_head()); + Some(next) } else { #[cfg(feature = "tracing")] tracing::error!("No symbol found at {}", self.actor.position()); diff --git a/core/src/actors/mod.rs b/core/src/actors/mod.rs index b8cff85..d684b58 100644 --- a/core/src/actors/mod.rs +++ b/core/src/actors/mod.rs @@ -2,6 +2,10 @@ Appellation: actors Contrib: FL03 */ +//! # Actors +//! +//! An actor describes an abstract model of computation that may find the solution to any +//! computable sequence, or algorithm. #[doc(inline)] pub use self::{actor::Actor, exec::Executor}; @@ -13,13 +17,51 @@ pub(crate) mod prelude { pub use super::exec::Executor; } -use crate::{rules::Program, Alphabet}; +use crate::{Direction, Error, Head, Rule, State}; -pub trait Model { - type Alpha: Alphabet; +#[doc(hidden)] +pub trait GetRule { + fn get(&self, state: State<&Q>, symbol: &S) -> Option<&Rule>; + + fn get_mut(&mut self, state: State<&Q>, symbol: &S) -> Option<&mut Rule>; +} + +#[doc(hidden)] +pub trait Dynamical { + type Output; + + fn step(&mut self, f: F) -> Self::Output; +} + +#[doc(hidden)] +pub trait Engine { + fn load(&mut self, program: I) + where + I: IntoIterator>; + + fn handle(&mut self, direction: Direction, state: State, symbol: S) -> Head; + + fn run(&mut self) -> Result<(), Error>; } -pub trait Runtime { - fn load(&mut self, program: Program); - fn run(&mut self); +impl Engine for Executor +where + Q: Clone + PartialEq + 'static, + S: crate::Symbolic, +{ + fn load(&mut self, program: I) + where + I: IntoIterator>, + { + self.program.rules.clear(); + self.program.extend(program); + } + + fn handle(&mut self, direction: Direction, state: State, symbol: S) -> Head { + self.actor.step(direction, state, symbol) + } + + fn run(&mut self) -> Result<(), crate::Error> { + Executor::run(self) + } } diff --git a/core/src/error.rs b/core/src/error.rs index 5a41b0d..569c226 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -14,8 +14,18 @@ pub enum StateError { } #[derive( - Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, strum::VariantNames, thiserror::Error, + Clone, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + strum::EnumDiscriminants, + strum::VariantNames, + thiserror::Error, )] +#[strum_discriminants(derive(Hash, Ord, PartialOrd))] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum Error { #[error("[Execution Error] {0}")] @@ -28,6 +38,8 @@ pub enum Error { StateError(#[from] StateError), #[error("[Transformation Error]: {0}")] TransformationError(String), + #[error("[Type Error] {0}")] + TypeError(String), #[error("[Unknown Error] {0}")] Unknown(String), } @@ -53,6 +65,10 @@ impl Error { Error::TransformationError(message.to_string()) } + pub fn type_error(message: impl ToString) -> Self { + Error::TypeError(message.to_string()) + } + pub fn unknown(message: impl ToString) -> Self { Error::Unknown(message.to_string()) } @@ -64,6 +80,23 @@ impl Error { pub fn state_not_found() -> Self { Error::StateError(StateError::StateNotFound) } + + pub fn message(&self) -> String { + match self { + Error::ExecutionError(message) => message.clone(), + Error::IndexOutOfBounds { index, len } => { + format!( + "Out of Bounds: {} is out of bounds for a length of {}", + index, len + ) + } + Error::RuntimeError(message) => message.clone(), + Error::StateError(err) => err.to_string(), + Error::TransformationError(message) => message.clone(), + Error::TypeError(message) => message.clone(), + Error::Unknown(message) => message.clone(), + } + } } impl From<&str> for Error { diff --git a/core/src/lib.rs b/core/src/lib.rs index 0051933..9cebb5f 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -4,8 +4,22 @@ */ //! # rstm-core //! +//! The `rstm-core` crate provides the core functionality for the `rstm` library. +//! +//! ## Features +//! +//! ### Components +//! +//! - [x] Rules +//! - [x] States +//! +//! ### Tapes +//! +//! - [x] [StdTape] +//! - [x] [HashTape](tape::hash_tape::HashTape) -// #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::new_ret_no_self)] #[cfg(feature = "alloc")] extern crate alloc; @@ -13,11 +27,9 @@ extern crate alloc; pub use self::{ actors::Actor, error::Error, - rules::{Program, Rule}, - shift::Direction, + rules::{Rule, Ruleset}, state::State, - tape::StdTape, - traits::*, + traits::prelude::*, types::prelude::*, }; @@ -34,23 +46,20 @@ pub(crate) mod macros { #[macro_use] pub(crate) mod seal; -#[doc(hidden)] pub mod actors; pub mod error; +pub mod mem; pub mod rules; -pub mod shift; pub mod state; -pub mod tape; pub mod traits; pub mod types; pub mod prelude { - pub use crate::actors::prelude::*; - pub use crate::error::Error; - pub use crate::rules::prelude::*; - pub use crate::shift::prelude::*; - pub use crate::state::prelude::*; - pub use crate::tape::prelude::*; - pub use crate::traits::prelude::*; - pub use crate::types::prelude::*; + pub use super::actors::prelude::*; + pub use super::error::Error; + pub use super::mem::prelude::*; + pub use super::rules::prelude::*; + pub use super::state::prelude::*; + pub use super::traits::prelude::*; + pub use super::types::prelude::*; } diff --git a/core/src/macros/states.rs b/core/src/macros/states.rs index ada53e4..6755abb 100644 --- a/core/src/macros/states.rs +++ b/core/src/macros/states.rs @@ -3,6 +3,31 @@ Contrib: FL03 */ +macro_rules! getter { + (@impl $vis:vis $T:ident($($field:tt)*)) => { + $vis fn into_inner(self) -> Self::Inner { + self.$($field)* + } + + $vis const fn get(&self) -> &Self::Inner { + &self.$($field)* + } + + $vis fn get_mut(&mut self) -> &mut Self::Inner { + &mut self.$($field)* + } + + $vis fn set(&mut self, inner: Self::Inner) { + self.$($field)* = inner; + } + }; + ($($vis:vis $T:ident($($field:tt)*)),* $(,)?) => { + $( + getter!(@impl $vis $T($($field)*)); + )* + }; +} + #[macro_export] macro_rules! state { ($state:expr) => { diff --git a/core/src/mem/mod.rs b/core/src/mem/mod.rs new file mode 100644 index 0000000..74573bd --- /dev/null +++ b/core/src/mem/mod.rs @@ -0,0 +1,16 @@ +/* + Appellation: mem + Contrib: FL03 +*/ +//! # Memory (mem) +//! +//! +#[doc(inline)] +pub use self::store::*; + +pub mod store; +pub mod tape; + +pub(crate) mod prelude { + pub use super::tape::prelude::*; +} diff --git a/core/src/mem/store.rs b/core/src/mem/store.rs new file mode 100644 index 0000000..8317c93 --- /dev/null +++ b/core/src/mem/store.rs @@ -0,0 +1,215 @@ +/* + Appellation: store + Contrib: FL03 +*/ + +/// [RawMemory] is a trait that provides a common interface for memory storage. +pub trait RawMemory { + type Elem; + + private!(); + + fn is_empty(&self) -> bool { + self.len() == 0 + } + + fn len(&self) -> usize; +} + +pub trait Memory: RawMemory { + fn to_vec(&self) -> Vec + where + Self::Elem: Clone; +} + +pub trait MemoryMut: Memory { + fn clear(&mut self); + + fn insert(&mut self, index: usize, elem: Self::Elem); + + fn remove(&mut self, index: usize) -> Option; +} + +/// [Sequential] extends the base trait [RawMemory] to provide sequential access to memory. +pub trait SeqMemory: Memory { + fn as_ptr(&self) -> *const Self::Elem; + + fn as_mut_ptr(&mut self) -> *mut Self::Elem; + + fn as_slice(&self) -> &[Self::Elem]; + + fn as_mut_slice(&mut self) -> &mut [Self::Elem]; + + fn get(&self, index: I) -> Option<&I::Output> + where + I: core::slice::SliceIndex<[Self::Elem]>; + + fn get_mut(&mut self, index: I) -> Option<&mut I::Output> + where + I: core::slice::SliceIndex<[Self::Elem]>; +} + +#[doc(hidden)] +pub trait Fetch { + type Output; + + fn fetch(&self, index: K) -> Option<&Self::Output>; + + fn fetch_mut(&mut self, index: K) -> Option<&mut Self::Output>; +} + +/* + ************* Implementations ************* +*/ +impl Fetch for [T] +where + I: core::slice::SliceIndex<[T]>, + I::Output: Sized, +{ + type Output = I::Output; + + fn fetch(&self, index: I) -> Option<&Self::Output> { + <[T]>::get(self, index) + } + + fn fetch_mut(&mut self, index: I) -> Option<&mut Self::Output> { + <[T]>::get_mut(self, index) + } +} + +impl RawMemory for [T] { + type Elem = T; + + seal!(); + + fn is_empty(&self) -> bool { + <[T]>::is_empty(self) + } + + fn len(&self) -> usize { + <[T]>::len(self) + } +} + +impl Memory for [T] { + fn to_vec(&self) -> Vec + where + T: Clone, + { + <[T]>::to_vec(self) + } +} + +impl SeqMemory for [T] { + fn as_ptr(&self) -> *const T { + <[T]>::as_ptr(self) + } + + fn as_mut_ptr(&mut self) -> *mut T { + <[T]>::as_mut_ptr(self) + } + + fn as_slice(&self) -> &[T] { + self + } + + fn as_mut_slice(&mut self) -> &mut [T] { + self + } + + fn get(&self, index: I) -> Option<&I::Output> + where + I: core::slice::SliceIndex<[T]>, + { + <[T]>::get(self, index) + } + + fn get_mut(&mut self, index: I) -> Option<&mut I::Output> + where + I: core::slice::SliceIndex<[T]>, + { + <[T]>::get_mut(self, index) + } +} + +#[cfg(feature = "alloc")] +mod impl_alloc { + use alloc::vec::Vec; + + impl super::RawMemory for Vec { + type Elem = T; + + seal!(); + + fn is_empty(&self) -> bool { + Vec::is_empty(self) + } + + fn len(&self) -> usize { + Vec::len(self) + } + } + + impl super::Memory for Vec { + fn to_vec(&self) -> Vec + where + T: Clone, + { + self.clone() + } + } + + impl super::SeqMemory for Vec + where + Vec: AsRef<[T]>, + { + fn as_ptr(&self) -> *const T { + Vec::as_ptr(self) + } + + fn as_mut_ptr(&mut self) -> *mut T { + Vec::as_mut_ptr(self) + } + + fn as_slice(&self) -> &[T] { + Vec::as_slice(self) + } + + fn as_mut_slice(&mut self) -> &mut [T] { + Vec::as_mut_slice(self) + } + + fn get(&self, index: I) -> Option<&I::Output> + where + I: core::slice::SliceIndex<[T]>, + { + <[T]>::get(self, index) + } + + fn get_mut(&mut self, index: I) -> Option<&mut I::Output> + where + I: core::slice::SliceIndex<[T]>, + { + <[T]>::get_mut(self, index) + } + } +} + +#[cfg(feature = "std")] +mod impl_std { + use std::collections::HashMap; + + impl super::RawMemory for HashMap { + type Elem = V; + + seal!(); + + fn is_empty(&self) -> bool { + HashMap::is_empty(self) + } + + fn len(&self) -> usize { + HashMap::len(self) + } + } +} diff --git a/core/src/mem/tape/hash_tape.rs b/core/src/mem/tape/hash_tape.rs new file mode 100644 index 0000000..928e773 --- /dev/null +++ b/core/src/mem/tape/hash_tape.rs @@ -0,0 +1,171 @@ +/* + Appellation: hash_tape + Contrib: FL03 +*/ + +use crate::Direction; +use std::collections::hash_map::{self, HashMap}; + +pub(crate) type Hdx = isize; + +#[derive(Clone, Debug, Default)] +pub struct HashTape { + index: Hdx, + store: HashMap, + ticks: usize, +} + +impl HashTape { + pub fn new() -> HashTape { + HashTape { + index: 0, + store: HashMap::new(), + ticks: 0, + } + } + + pub fn from_data(data: HashMap) -> HashTape { + HashTape { + index: 0, + store: data, + ticks: 0, + } + } + + pub fn from_iter(iter: I) -> HashTape + where + I: IntoIterator, + { + HashTape { + index: 0, + store: HashMap::from_iter(iter), + ticks: 0, + } + } + + pub fn from_seq(seq: I) -> HashTape + where + I: IntoIterator, + { + let iter = seq.into_iter().enumerate().map(|(i, v)| (i as Hdx, v)); + Self::from_iter(iter) + } + /// Returns the current position of the head. + pub fn current_position(&self) -> Hdx { + self.index + } + /// Returns the total number of steps taken by the head. + pub fn ticks(&self) -> usize { + self.ticks + } + /// clears the tape. + pub fn clear(&mut self) { + self.index = 0; + self.store.clear(); + self.ticks = 0; + } + /// Returns the entry in the tape at the current index. + pub fn current_entry(&mut self) -> hash_map::Entry { + self.store.entry(self.index) + } + /// Returns a mutable entry in the tape at the given index. + pub fn entry(&mut self, index: Hdx) -> hash_map::Entry { + self.store.entry(index) + } + + /// Returns true if the tape contains the given index. + pub fn contains_key(&self, index: Hdx) -> bool { + self.store.contains_key(&index) + } + /// Returns true if the tape contains the given value. + pub fn contains_value(&self, value: &V) -> bool + where + V: PartialEq, + { + self.values().any(|v| v == value) + } + /// Returns a reference to the value at the given index. + pub fn get(&self, index: Hdx) -> Option<&V> { + self.store.get(&index) + } + /// Returns a mutable reference to the value at the given index. + pub fn get_mut(&mut self, index: Hdx) -> Option<&mut V> { + self.store.get_mut(&index) + } + /// Inserts a value at the given index. + pub fn insert(&mut self, index: Hdx, value: V) { + self.store.insert(index, value); + } + /// Returns true if the tape is empty. + pub fn is_empty(&self) -> bool { + self.store.is_empty() + } + /// Returns an immutable iterator over the tape. + pub fn iter(&self) -> hash_map::Iter { + self.store.iter() + } + /// Returns a mutable iterator over the tape. + pub fn iter_mut(&mut self) -> hash_map::IterMut { + self.store.iter_mut() + } + /// Returns an iterator over the keys of the tape. + pub fn keys(&self) -> hash_map::Keys { + self.store.keys() + } + /// Returns the number of elements in the tape. + pub fn len(&self) -> usize { + self.store.len() + } + /// Returns a mutable reference to the value of the head at the current position; on empty, + /// the given value is inserted and returned. + pub fn or_insert(&mut self, default: V) -> &mut V { + self.store.entry(self.index).or_insert(default) + } + /// Returns a mutable reference to the value of the head at the current position; on empty, + /// the function is evaluated and the result is inserted and returned. + pub fn or_insert_with(&mut self, default: F) -> &mut V + where + F: FnOnce() -> V, + { + self.store.entry(self.index).or_insert_with(default) + } + /// Returns a mutable reference to the value of the head at the current position; if the + /// value is not present, the default value is inserted and returned. + pub fn or_default(&mut self) -> &mut V + where + V: Default, + { + self.store.entry(self.index).or_default() + } + /// Removes the value at the given index. + pub fn remove(&mut self, index: Hdx) -> Option { + self.store.remove(&index) + } + /// Returns an iterator over the values of the tape. + pub fn values(&self) -> hash_map::Values { + self.store.values() + } + + /// Shifts the cursor in the given direction. + pub fn shift(&mut self, direction: Direction) { + self.index += direction; + self.ticks += 1; + } + + /// Returns a reference to the value at the current cursor position. + pub fn read(&self) -> Option<&V> { + self.store.get(&self.index) + } + + pub fn read_mut(&mut self) -> &mut V + where + V: Default, + { + self.or_default() + } + + pub fn write(&mut self, step: Direction, value: V) { + let _ = self.store.insert(self.index, value); + self.shift(step); + } +} diff --git a/core/src/mem/tape/mod.rs b/core/src/mem/tape/mod.rs new file mode 100644 index 0000000..ba76b51 --- /dev/null +++ b/core/src/mem/tape/mod.rs @@ -0,0 +1,136 @@ +/* + Appellation: tape + Contrib: FL03 +*/ +//! # Tape +//! +//! Idealized Turing machines consider a tape, or memory, that is infinite in both directions. +//! This tape is a one-dimensional array of symbols manipulated by the tape head according to +//! some set of pre-defined rules. +#[doc(inline)] +pub use self::tape::StdTape; + +pub(crate) mod tape; + +#[cfg(feature = "std")] +pub mod hash_tape; + +pub(crate) mod prelude { + pub use super::tape::StdTape; +} + +use core::option::Option; + +#[doc(hidden)] +pub trait RawTape { + type Elem; + + private!(); +} + +#[doc(hidden)] +pub trait Tape: RawTape { + type Index; + + fn clear(&mut self); + + fn get(&self, index: &Self::Index) -> Option<&A>; + + fn get_mut(&mut self, index: &Self::Index) -> Option<&mut A>; + + fn insert(&mut self, index: Self::Index, symbol: A); + + fn is_empty(&self) -> bool; + + fn len(&self) -> usize; +} + +/* + ************* Implementations ************* +*/ +#[cfg(feature = "alloc")] +use alloc::vec::Vec; +#[cfg(feature = "std")] +use std::collections::HashMap; + +#[cfg(feature = "alloc")] +impl RawTape for Vec { + type Elem = A; + + seal!(); +} + +#[cfg(feature = "std")] +impl RawTape for HashMap { + type Elem = A; + + seal!(); +} + +#[cfg(feature = "alloc")] +impl Tape for Vec { + type Index = usize; + + fn clear(&mut self) { + Vec::clear(self); + } + + fn get(&self, key: &Self::Index) -> Option<&V> { + match key { + key if *key < self.len() => Some(&self[*key]), + _ => None, + } + } + + fn get_mut(&mut self, key: &Self::Index) -> Option<&mut V> { + match key { + key if *key < self.len() => Some(&mut self[*key]), + _ => None, + } + } + + fn insert(&mut self, key: Self::Index, value: V) { + Vec::insert(self, key, value); + } + + fn is_empty(&self) -> bool { + Vec::is_empty(self) + } + + fn len(&self) -> usize { + Vec::len(self) + } +} + +#[cfg(feature = "std")] +impl Tape for HashMap +where + K: Eq + std::hash::Hash, + V: Eq + std::hash::Hash, +{ + type Index = K; + + fn clear(&mut self) { + HashMap::clear(self); + } + + fn get(&self, key: &K) -> Option<&V> { + HashMap::get(self, key) + } + + fn get_mut(&mut self, key: &K) -> Option<&mut V> { + HashMap::get_mut(self, key) + } + + fn insert(&mut self, key: K, value: V) { + HashMap::insert(self, key, value); + } + + fn is_empty(&self) -> bool { + HashMap::is_empty(self) + } + + fn len(&self) -> usize { + HashMap::len(self) + } +} diff --git a/core/src/tape/tape.rs b/core/src/mem/tape/tape.rs similarity index 88% rename from core/src/tape/tape.rs rename to core/src/mem/tape/tape.rs index 2bf1c58..38c18de 100644 --- a/core/src/tape/tape.rs +++ b/core/src/mem/tape/tape.rs @@ -8,10 +8,11 @@ use core::cell::Cell; #[cfg(feature = "alloc")] use alloc::vec::Vec; -/// In-line with the Turing machine model, the [`StdTape`] is a one-dimensional surface evenly -/// divided into cells capable of storing symbols. The tape is infinite in both directions -/// allowing the head, or actor, to move without bounds, extending the tape as needed. -/// +/// # [StdTape] +/// +/// [StdTae] is a basic implementation of a tape, a one-dimensional surface evenly divided into +/// cells capable of storing symbols. The tape is infinite in both directions allowing the +/// head, or actor, to move without bounds, extending the tape as needed. /// /// Here, the tape employs the use of a [Vec] to store symbols while leveraging a /// [usize] to keep track of the current position of the tape head. Moreover, the tape @@ -24,15 +25,21 @@ use alloc::vec::Vec; #[derive(Clone, Eq, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct StdTape { - cursor: usize, + index: usize, store: Vec, ticks: Cell, } +impl Default for StdTape { + fn default() -> Self { + Self::new() + } +} + impl StdTape { pub fn new() -> Self { StdTape { - cursor: 0, + index: 0, store: Vec::::new(), ticks: Cell::default(), } @@ -40,7 +47,7 @@ impl StdTape { /// Constructs a new tape from an iterator. pub fn from_iter(iter: impl IntoIterator) -> Self { StdTape { - cursor: 0, + index: 0, store: Vec::from_iter(iter), ticks: Cell::default(), } @@ -48,7 +55,7 @@ impl StdTape { /// Constructs a new, empty tape with the specified capacity. pub fn with_capacity(capacity: usize) -> Self { StdTape { - cursor: 0, + index: 0, store: Vec::::with_capacity(capacity), ticks: Cell::default(), } @@ -114,21 +121,21 @@ impl StdTape { } /// Returns the current position of the tape head; pub fn position(&self) -> usize { - self.cursor + self.index } /// Attempts to read the symbol at the current position of the tape head. pub fn read(&self) -> Result<&S, Error> { - self.get(self.cursor) - .ok_or(Error::index_out_of_bounds(self.cursor, self.len())) + self.get(self.index) + .ok_or(Error::index_out_of_bounds(self.index, self.len())) } /// Writes the given symbol to the tape at the current position of the tape head. pub fn write(&mut self, symbol: S) { - if self.cursor == usize::MAX { + if self.index == usize::MAX { self.store.insert(0, symbol); - } else if self.cursor == self.store.len() { + } else if self.index == self.store.len() { self.store.push(symbol); } else { - self.store[self.cursor] = symbol; + self.store[self.index] = symbol; } } @@ -143,7 +150,7 @@ impl StdTape { } fn shift(&mut self, direction: Direction) -> usize { - self.cursor = direction.apply_unsigned(self.cursor) % self.store.len(); + self.index = direction.apply_unsigned(self.index) % self.store.len(); self.position() } } @@ -151,7 +158,7 @@ impl StdTape { impl StdTape { pub fn from_str(input: &str) -> StdTape { StdTape { - cursor: 0, + index: 0, store: input.chars().collect(), ticks: Cell::default(), } @@ -203,7 +210,7 @@ where fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { for (i, c) in self.store.iter().enumerate() { match c { - s if i == self.cursor => write!(f, "[{s:?}]")?, + s if i == self.index => write!(f, "[{s:?}]")?, _ => write!(f, "{c:?}")?, } } @@ -218,7 +225,7 @@ where fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { for (i, c) in self.store.iter().enumerate() { match c { - b if i == self.cursor => write!(f, "[{b}]")?, + b if i == self.index => write!(f, "[{b}]")?, _ => write!(f, "{c}")?, } } diff --git a/core/src/rules/builders/instructions.rs b/core/src/rules/builders/instructions.rs deleted file mode 100644 index a5182a9..0000000 --- a/core/src/rules/builders/instructions.rs +++ /dev/null @@ -1,10 +0,0 @@ -/* - Appellation: instructions - Contrib: FL03 -*/ - -#[derive(Default)] -pub struct RulesetBuilder { - pub(crate) initial_state: Option>, - pub(crate) rules: Vec>, -} \ No newline at end of file diff --git a/core/src/rules/builders/program.rs b/core/src/rules/builders/program.rs deleted file mode 100644 index ecbfc07..0000000 --- a/core/src/rules/builders/program.rs +++ /dev/null @@ -1,45 +0,0 @@ -/* - Appellation: builder - Contrib: FL03 -*/ -use crate::rules::{Program, Rule}; -use crate::State; - -#[derive(Default)] -pub struct ProgramBuilder { - initial_state: Option>, - rules: Vec>, -} - -impl ProgramBuilder { - pub fn new() -> Self { - Self { - initial_state: None, - rules: Vec::new(), - } - } - - pub fn initial_state(self, State(state): State) -> Self { - Self { - initial_state: Some(State(state)), - ..self - } - } - - pub fn rules(self, rules: I) -> Self - where - I: IntoIterator>, - { - Self { - rules: Vec::from_iter(rules), - ..self - } - } - - pub fn build(self) -> Program { - Program { - initial_state: self.initial_state.unwrap(), - rules: self.rules, - } - } -} diff --git a/core/src/rules/builders/rule.rs b/core/src/rules/builders/rule.rs index 439fddc..91f58c7 100644 --- a/core/src/rules/builders/rule.rs +++ b/core/src/rules/builders/rule.rs @@ -75,3 +75,9 @@ impl RuleBuilder { } } } + +impl From> for Rule { + fn from(builder: RuleBuilder) -> Self { + builder.build() + } +} diff --git a/core/src/rules/mod.rs b/core/src/rules/mod.rs index bdd22b8..51d66a2 100644 --- a/core/src/rules/mod.rs +++ b/core/src/rules/mod.rs @@ -3,33 +3,40 @@ Contrib: FL03 */ #[doc(inline)] -pub use self::{ - builders::{ProgramBuilder, RuleBuilder}, - program::Program, - rule::Rule, -}; +pub use self::{builders::RuleBuilder, program::Ruleset, rule::Rule, ruleset::RuleMap}; pub(crate) mod program; pub(crate) mod rule; - -pub mod workload; +pub mod ruleset; #[doc(hidden)] -pub(crate) mod builders { - pub use self::{program::ProgramBuilder, rule::RuleBuilder}; +mod builders { + pub use self::rule::RuleBuilder; - mod program; mod rule; } pub(crate) mod prelude { - pub use super::program::Program; + pub use super::program::Ruleset; pub use super::rule::Rule; pub use super::{Directive, Scope, Transition}; } use crate::{Direction, Head, State, Symbolic, Tail}; +pub trait Program { + type Key: Scope; + type Val: Directive; + + fn contains_key(&self, key: &Self::Key) -> bool; + + fn get(&self, key: &Self::Key) -> Option<&Self::Val>; + + fn insert(&mut self, key: Self::Key, val: Self::Val) -> Option; + + fn remove(&mut self, key: &Self::Key) -> Option; +} + pub trait Transition { fn direction(&self) -> Direction; @@ -69,7 +76,7 @@ pub trait Transition { pub trait Scope { fn current_state(&self) -> State<&'_ Q>; - fn symbol(&self) -> &S; + fn current_symbol(&self) -> &S; } /// [`Directive`] is a trait describing the `tail` of a typical Turing machine; @@ -78,12 +85,78 @@ pub trait Directive { fn next_state(&self) -> State<&'_ Q>; - fn value(&self) -> &S; + fn next_symbol(&self) -> &S; } /* ************* Implementations ************* */ +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +#[cfg(feature = "std")] +use std::collections::HashMap; + +#[cfg(feature = "alloc")] +impl Program for Vec> +where + Q: PartialEq, + A: PartialEq, +{ + type Key = Head; + type Val = Tail; + + fn contains_key(&self, key: &Head) -> bool { + self.iter().any(|rule| rule.head() == key) + } + + fn get(&self, key: &Head) -> Option<&Tail> { + self.iter().find_map(|rule| { + if rule.head() == key { + Some(rule.tail()) + } else { + None + } + }) + } + + fn insert(&mut self, key: Head, val: Tail) -> Option> { + self.iter_mut() + .find(|rule| rule.head() == &key) + .map(|rule| core::mem::replace(rule.tail_mut(), val)) + } + + fn remove(&mut self, key: &Head) -> Option> { + let index = self.iter().position(|rule| rule.head() == key)?; + Some(self.remove(index).tail) + } +} + +#[cfg(feature = "std")] +impl Program for HashMap, Tail> +where + Q: Eq + core::hash::Hash, + A: Eq + core::hash::Hash, +{ + type Key = Head; + type Val = Tail; + + fn contains_key(&self, key: &Head) -> bool { + self.contains_key(key) + } + + fn get(&self, key: &Head) -> Option<&Tail> { + self.get(key) + } + + fn insert(&mut self, key: Head, val: Tail) -> Option> { + self.insert(key, val) + } + + fn remove(&mut self, key: &Head) -> Option> { + self.remove(key) + } +} impl Transition for A where @@ -103,82 +176,82 @@ where } fn symbol(&self) -> &S { - self.symbol() + self.current_symbol() } fn write_symbol(&self) -> &S { - self.value() + self.next_symbol() } } -impl Scope for Rule { +impl Scope for (State, S) { fn current_state(&self) -> State<&'_ Q> { - self.head.state.to_ref() + self.0.to_ref() } - fn symbol(&self) -> &S { - &self.symbol() + fn current_symbol(&self) -> &S { + &self.1 } } -impl Directive for Rule { - fn direction(&self) -> Direction { - self.direction() - } - - fn next_state(&self) -> State<&'_ Q> { - self.tail().state() +impl Scope for crate::Head { + fn current_state(&self) -> State<&'_ Q> { + self.state() } - fn value(&self) -> &S { - &self.write_symbol() + fn current_symbol(&self) -> &S { + &self.symbol } } -impl Scope for crate::Head { +impl Scope for Rule { fn current_state(&self) -> State<&'_ Q> { - self.state() + self.head.state.to_ref() } - fn symbol(&self) -> &S { - &self.symbol + fn current_symbol(&self) -> &S { + self.symbol() } } -impl Directive for crate::Tail { +impl Directive for (Direction, State, S) { fn direction(&self) -> Direction { - self.direction + self.0 } fn next_state(&self) -> State<&'_ Q> { - self.state() + self.1.to_ref() } - fn value(&self) -> &S { - &self.symbol() + fn next_symbol(&self) -> &S { + &self.2 } } -impl Scope for (State, S) { - fn current_state(&self) -> State<&'_ Q> { - self.0.to_ref() +impl Directive for crate::Tail { + fn direction(&self) -> Direction { + self.direction } - fn symbol(&self) -> &S { - &self.1 + fn next_state(&self) -> State<&'_ Q> { + self.state() + } + + fn next_symbol(&self) -> &S { + self.symbol() } } -impl Directive for (Direction, State, S) { +impl Directive for Rule { fn direction(&self) -> Direction { - self.0 + self.direction() } fn next_state(&self) -> State<&'_ Q> { - self.1.to_ref() + self.tail().state() } - fn value(&self) -> &S { - &self.2 + fn next_symbol(&self) -> &S { + self.write_symbol() } } diff --git a/core/src/rules/program.rs b/core/src/rules/program.rs index 99fa028..798e2b2 100644 --- a/core/src/rules/program.rs +++ b/core/src/rules/program.rs @@ -2,66 +2,71 @@ Appellation: program Contrib: FL03 */ -use super::{ProgramBuilder, Rule}; +#![cfg(feature = "alloc")] +use super::Rule; use crate::{Head, State, Tail}; -use std::vec; +use alloc::vec::{self, Vec}; -type Ruleset = Vec>; - -// type Ruleset = std::collections::HashMap, Tail>; +type RuleVec = Vec>; #[derive(Clone, Debug, Default)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Program { - pub(crate) initial_state: State, - pub(crate) rules: Ruleset, +pub struct Ruleset { + pub(crate) initial_state: Option>, + pub(crate) rules: RuleVec, } -impl Program { - pub fn new() -> ProgramBuilder { - ProgramBuilder::new() +impl Ruleset { + pub fn new() -> Self { + Self { + initial_state: None, + rules: Vec::new(), + } } - - pub fn from_iter(instructions: impl IntoIterator>) -> Self + /// Create a new instance of the [Program] from the given rules. + pub fn from_iter(iter: I) -> Self where - Q: Default, + I: IntoIterator>, { Self { - initial_state: State::default(), - rules: Ruleset::from_iter(instructions), + initial_state: None, + rules: Vec::from_iter(iter), } } - - pub fn from_state(State(initial_state): State) -> Self { + /// Create a new instance of the [Program] using the given initial state. + pub fn from_state(initial_state: State) -> Self { Self { - initial_state: State(initial_state), - rules: Ruleset::new(), + initial_state: Some(initial_state), + rules: Vec::new(), } } - /// - pub fn with_initial_state(self, State(state): State) -> Self { + /// Configures the program to use the given initial state. + pub fn with_initial_state(self, state: State) -> Self { Self { - initial_state: State(state), + initial_state: Some(state), ..self } } - /// - pub fn with_instructions(self, instructions: impl IntoIterator>) -> Self { + /// Configures the program with the given rules; + pub fn with_rules(self, instructions: I) -> Self + where + I: IntoIterator>, + { Self { - rules: Ruleset::from_iter(instructions), + rules: Vec::from_iter(instructions), ..self } } /// Returns an owned reference to the initial state of the program. - pub fn initial_state(&self) -> State<&'_ Q> { - self.initial_state.to_ref() + pub fn initial_state(&self) -> Option> { + self.initial_state.as_ref().map(|state| state.to_ref()) } /// Returns a reference to the instructions. - pub const fn instructions(&self) -> &Ruleset { + pub const fn instructions(&self) -> &RuleVec { &self.rules } /// Returns a mutable reference to the instructions. - pub fn instructions_mut(&mut self) -> &mut Ruleset { + pub fn instructions_mut(&mut self) -> &mut RuleVec { &mut self.rules } /// Returns an iterator over the elements. @@ -73,27 +78,27 @@ impl Program { self.rules.iter_mut() } /// Returns a collection of tails for a given head. - pub fn get(&self, head: &Head) -> Option<&Tail> + pub fn get(&self, State(state): State<&Q>, symbol: &S) -> Option<&Tail> where Q: PartialEq, S: PartialEq, { self.iter().find_map(|i| { - if i.head() == head { + if i.head().state() == state && i.head().symbol() == symbol { Some(i.tail()) } else { None } }) } - /// Returns a mutable collection of tails for a given head. - pub fn get_mut(&mut self, head: &Head) -> Option<&mut Tail> + /// Returns a mutable reference to a the tail matching the given head. + pub fn get_mut(&mut self, State(state): State<&Q>, symbol: &S) -> Option<&mut Tail> where Q: PartialEq, S: PartialEq, { self.iter_mut().find_map(|i| { - if i.head() == head { + if i.head().state() == state && i.head().symbol() == symbol { Some(i.tail_mut()) } else { None @@ -101,6 +106,20 @@ impl Program { }) } /// Returns a collection of tails for a given head. + pub fn get_by_head(&self, head: &Head) -> Option<&Tail> + where + Q: PartialEq, + S: PartialEq, + { + self.iter().find_map(|i| { + if i.head() == head { + Some(i.tail()) + } else { + None + } + }) + } + /// Returns a collection of tails for a given head. pub fn get_ref(&self, head: Head<&'_ Q, &'_ S>) -> Option> where Q: PartialEq, @@ -114,21 +133,29 @@ impl Program { } }) } + /// Returns a collection of rules whose head contains a match for the given state. + pub fn filter_by_state(&self, state: State<&Q>) -> Vec<&Rule> + where + Q: PartialEq, + S: PartialEq, + { + self.iter().filter(|i| *i.head() == state).collect() + } } -impl AsRef<[Rule]> for Program { +impl AsRef<[Rule]> for Ruleset { fn as_ref(&self) -> &[Rule] { &self.rules } } -impl AsMut<[Rule]> for Program { +impl AsMut<[Rule]> for Ruleset { fn as_mut(&mut self) -> &mut [Rule] { &mut self.rules } } -impl core::ops::Deref for Program { +impl core::ops::Deref for Ruleset { type Target = [Rule]; fn deref(&self) -> &Self::Target { @@ -136,13 +163,13 @@ impl core::ops::Deref for Program { } } -impl core::ops::DerefMut for Program { +impl core::ops::DerefMut for Ruleset { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.rules } } -impl core::ops::Index> for Program +impl core::ops::Index> for Ruleset where Q: PartialEq, S: PartialEq, @@ -150,38 +177,38 @@ where type Output = Tail; fn index(&self, index: Head) -> &Self::Output { - self.get(&index).unwrap() + self.get_by_head(&index).unwrap() } } -impl From> for Program { - fn from(instructions: Ruleset) -> Self { +impl From>> for Ruleset { + fn from(instructions: Vec>) -> Self { Self { - initial_state: State::default(), + initial_state: Some(State::default()), rules: instructions, } } } -impl Extend> for Program { +impl Extend> for Ruleset { fn extend>>(&mut self, iter: I) { self.rules.extend(iter) } } -impl FromIterator> for Program +impl FromIterator> for Ruleset where Q: Default, { fn from_iter>>(iter: I) -> Self { Self { - initial_state: State::default(), - rules: Ruleset::from_iter(iter), + initial_state: Some(State::default()), + rules: RuleVec::from_iter(iter), } } } -impl IntoIterator for Program { +impl IntoIterator for Ruleset { type Item = Rule; type IntoIter = vec::IntoIter; diff --git a/core/src/rules/rule.rs b/core/src/rules/rule.rs index 289a39b..37c8530 100644 --- a/core/src/rules/rule.rs +++ b/core/src/rules/rule.rs @@ -4,45 +4,45 @@ */ use super::RuleBuilder; -use crate::prelude::{Direction, Head, State, Tail}; +use crate::{Head, State, Tail}; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Ord, PartialOrd)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Rule { - pub head: Head, - pub tail: Tail, +pub struct Rule { + pub head: Head, + pub tail: Tail, } -impl Rule { - pub fn new() -> RuleBuilder { +impl Rule { + pub fn new() -> RuleBuilder { RuleBuilder::new() } /// Returns an immutable reference to the [Head] - pub const fn head(&self) -> &Head { + pub const fn head(&self) -> &Head { &self.head } /// Returns a mutable reference to the [Head] - pub fn head_mut(&mut self) -> &mut Head { + pub fn head_mut(&mut self) -> &mut Head { &mut self.head } /// Returns an instance of the [Head] whose elements are immutable references - pub fn head_ref(&self) -> Head<&'_ Q, &'_ S> { + pub fn head_ref(&self) -> Head<&'_ Q, &'_ A> { self.head().to_ref() } /// Returns an immutable reference to the [Tail] of the [Instruction] - pub const fn tail(&self) -> &Tail { + pub const fn tail(&self) -> &Tail { &self.tail } /// Returns a mutable reference to the [Tail] of the [Instruction] - pub fn tail_mut(&mut self) -> &mut Tail { + pub fn tail_mut(&mut self) -> &mut Tail { &mut self.tail } /// Returns an instance of the [Tail] whose elements are immutable references - pub fn tail_ref(&self) -> Tail<&'_ Q, &'_ S> { + pub fn tail_ref(&self) -> Tail<&'_ Q, &'_ A> { self.tail().to_ref() } /// Returns the direction of the shift - pub fn direction(&self) -> Direction { + pub fn direction(&self) -> crate::Direction { self.tail().direction() } /// Returns the current [State] of the system @@ -50,15 +50,15 @@ impl Rule { self.head().state() } /// Returns the symbol of the [Head] - pub const fn symbol(&self) -> &S { + pub const fn symbol(&self) -> &A { self.head().symbol() } /// Returns the next [Head] of the system - pub fn next_head(&self) -> Head<&'_ Q, &'_ S> { - self.tail().to_head_ref() + pub fn next_head(&self) -> Head<&'_ Q, &'_ A> { + self.tail().as_head() } /// Consumes the current object and returns the next [Head] of the system - pub fn into_next_head(self) -> Head { + pub fn into_next_head(self) -> Head { self.tail.into_head() } /// Returns the next [State] of the system @@ -66,20 +66,20 @@ impl Rule { self.tail().state() } /// Returns the value which for which the current object will be replaced with - pub const fn write_symbol(&self) -> &S { + pub const fn write_symbol(&self) -> &A { self.tail().symbol() } /// Consumes the current object and returns a 2-tuple consisting of the [Head] and [Tail] - pub fn into_tuple(self) -> (Head, Tail) { + pub fn into_tuple(self) -> (Head, Tail) { (self.head, self.tail) } } -impl<'a, Q, S> Rule<&'a Q, &'a S> { - pub fn cloned(&self) -> Rule +impl<'a, Q, A> Rule<&'a Q, &'a A> { + pub fn cloned(&self) -> Rule where Q: Clone, - S: Clone, + A: Clone, { Rule { head: self.head.cloned(), @@ -87,10 +87,10 @@ impl<'a, Q, S> Rule<&'a Q, &'a S> { } } - pub fn copied(&self) -> Rule + pub fn copied(&self) -> Rule where Q: Copy, - S: Copy, + A: Copy, { Rule { head: self.head.copied(), @@ -99,68 +99,97 @@ impl<'a, Q, S> Rule<&'a Q, &'a S> { } } -impl core::convert::AsRef> for Rule { - fn as_ref(&self) -> &Head { - self.head() +mod impls { + use super::Rule; + use crate::{Head, Tail}; + + impl core::convert::AsRef> for Rule { + fn as_ref(&self) -> &Head { + self.head() + } } -} -impl core::convert::AsRef> for Rule { - fn as_ref(&self) -> &Tail { - self.tail() + impl core::convert::AsRef> for Rule { + fn as_ref(&self) -> &Tail { + self.tail() + } } -} -impl core::convert::AsMut> for Rule { - fn as_mut(&mut self) -> &mut Head { - self.head_mut() + impl core::convert::AsMut> for Rule { + fn as_mut(&mut self) -> &mut Head { + self.head_mut() + } } -} -impl core::convert::AsMut> for Rule { - fn as_mut(&mut self) -> &mut Tail { - self.tail_mut() + impl core::convert::AsMut> for Rule { + fn as_mut(&mut self) -> &mut Tail { + self.tail_mut() + } } -} -impl core::borrow::Borrow> for Rule { - fn borrow(&self) -> &Head { - self.head() + impl core::borrow::Borrow> for Rule { + fn borrow(&self) -> &Head { + self.head() + } } -} -impl core::borrow::Borrow> for Rule { - fn borrow(&self) -> &Tail { - self.tail() + impl core::borrow::Borrow> for Rule { + fn borrow(&self) -> &Tail { + self.tail() + } } -} -impl core::borrow::BorrowMut> for Rule { - fn borrow_mut(&mut self) -> &mut Head { - self.head_mut() + impl core::borrow::BorrowMut> for Rule { + fn borrow_mut(&mut self) -> &mut Head { + self.head_mut() + } } -} -impl core::borrow::BorrowMut> for Rule { - fn borrow_mut(&mut self) -> &mut Tail { - self.tail_mut() + impl core::borrow::BorrowMut> for Rule { + fn borrow_mut(&mut self) -> &mut Tail { + self.tail_mut() + } } -} -impl From<(Head, Tail)> for Rule { - fn from((head, tail): (Head, Tail)) -> Self { - Self { head, tail } + impl PartialEq<(Head, Tail)> for Rule + where + Q: PartialEq, + S: PartialEq, + { + fn eq(&self, other: &(Head, Tail)) -> bool { + self.head == other.0 && self.tail == other.1 + } } -} -impl From> for (Head, Tail) { - fn from(rule: Rule) -> Self { - (rule.head, rule.tail) + impl PartialEq> for Rule + where + Q: PartialEq, + S: PartialEq, + { + fn eq(&self, other: &Head) -> bool { + &self.head == other + } } -} -impl From> for Rule { - fn from(builder: RuleBuilder) -> Self { - builder.build() + impl PartialEq> for Rule + where + Q: PartialEq, + S: PartialEq, + { + fn eq(&self, other: &Tail) -> bool { + &self.tail == other + } + } + + impl From<(Head, Tail)> for Rule { + fn from((head, tail): (Head, Tail)) -> Self { + Self { head, tail } + } + } + + impl From> for (Head, Tail) { + fn from(rule: Rule) -> Self { + (rule.head, rule.tail) + } } } diff --git a/core/src/rules/workload.rs b/core/src/rules/ruleset.rs similarity index 59% rename from core/src/rules/workload.rs rename to core/src/rules/ruleset.rs index a66d7d6..0a2bcf7 100644 --- a/core/src/rules/workload.rs +++ b/core/src/rules/ruleset.rs @@ -2,73 +2,57 @@ Appellation: workload Contrib: FL03 */ -// #![cfg(feature = "std")] +#![cfg(feature = "std")] use super::Rule; use crate::{Head, State, Tail}; use std::collections::hash_map::{self, HashMap}; #[derive(Clone, Debug, Default)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct RuleSet -where - Q: Eq + core::hash::Hash, - S: Eq + core::hash::Hash, -{ - pub(crate) initial_state: State, +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +pub struct RuleMap { + pub(crate) initial_state: Option>, pub(crate) rules: HashMap, Tail>, } -impl RuleSet -where - Q: Eq + core::hash::Hash, - S: Eq + core::hash::Hash, -{ - pub fn new() -> Self - where - Q: Default, - { +impl RuleMap { + pub fn new() -> Self { Self { - initial_state: State::default(), + initial_state: None, rules: HashMap::new(), } } - pub fn from_state(State(initial_state): State) -> Self { + pub fn from_state(initial_state: State) -> Self { Self { - initial_state: State(initial_state), + initial_state: Some(initial_state), rules: HashMap::new(), } } pub fn from_iter(iter: I) -> Self where - Q: Default, + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, I: IntoIterator, Tail)>, { Self { - initial_state: State::default(), + initial_state: None, rules: HashMap::from_iter(iter), } } pub fn from_rules(iter: I) -> Self where - Q: Default, + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, I: IntoIterator>, { - let mut rules = HashMap::new(); - for rule in iter { - rules.insert(rule.head, rule.tail); - } - Self { - initial_state: State::default(), - rules, - } + Self::from_iter(iter.into_iter().map(|rule| (rule.head, rule.tail))) } - - pub fn with_initial_state(self, State(state): State) -> Self { + /// configures the ruleset with the given initial state + pub fn with_initial_state(self, state: State) -> Self { Self { - initial_state: State(state), + initial_state: Some(state), ..self } } @@ -76,93 +60,191 @@ where pub fn with_instructions( self, instructions: impl IntoIterator, Tail)>, - ) -> Self { - let mut rules = HashMap::new(); - for (head, tail) in instructions { - rules.insert(head, tail); + ) -> Self + where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, + { + Self { + rules: HashMap::from_iter(instructions), + ..self } - Self { rules, ..self } } + /// Returns an instance of [State] which owns a reference to the interval value. - pub fn initial_state(&self) -> State<&'_ Q> { - self.initial_state.to_ref() + pub fn initial_state(&self) -> Option> { + self.initial_state.as_ref().map(|state| state.to_ref()) } + /// Returns an immutable reference to the set of rules. pub const fn rules(&self) -> &HashMap, Tail> { &self.rules } + /// Returns a mutable reference to the set of rules. pub fn rules_mut(&mut self) -> &mut HashMap, Tail> { &mut self.rules } + /// Clears the set of rules. pub fn clear(&mut self) { self.rules.clear(); } + /// Returns an the entry for the given head within the set of rules. - pub fn entry(&mut self, head: Head) -> hash_map::Entry, Tail> { + pub fn entry(&mut self, head: Head) -> hash_map::Entry, Tail> + where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, + { self.rules.entry(head) } /// Returns an immutable reference to the tail of the rule for the given head; returns none /// if the head is not found. - pub fn get(&self, head: &Head) -> Option<&Tail> { + pub fn get_head(&self, head: &Head) -> Option<&Tail> + where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, + { self.rules.get(head) } + /// Returns a mutable reference to the tail of the rule for the given head; returns none if /// the head is not found. - pub fn get_mut(&mut self, head: &Head) -> Option<&mut Tail> { + pub fn get_mut(&mut self, head: &Head) -> Option<&mut Tail> + where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, + { self.rules.get_mut(head) } + /// Returns the tail of the rule for the given head; returns none if the head is not found /// within the set of rules. - pub fn get_ref(&self, head: &Head) -> Option> { - self.get(head).map(|tail| tail.to_ref()) + pub fn get_ref(&self, head: &Head) -> Option> + where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, + { + self.get_head(head).map(|tail| tail.to_ref()) } + /// Inserts a new rule into the set of rules. - pub fn insert(&mut self, head: Head, tail: Tail) { + pub fn insert(&mut self, head: Head, tail: Tail) + where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, + { self.rules.insert(head, tail); } + /// Inserts a new rule into the set of rules. - pub fn insert_rule(&mut self, rule: Rule) { + pub fn insert_rule(&mut self, rule: Rule) + where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, + { self.insert(rule.head, rule.tail); } + /// Returns true if the set of rules is empty. pub fn is_empty(&self) -> bool { self.rules.is_empty() } + /// Returns the number of rules in the set. pub fn len(&self) -> usize { self.rules.len() } + /// Returns a mutable reference to the tail of the rule for the given head; inserts the /// tail if the head is not found. - pub fn or_insert(&mut self, head: Head, tail: Tail) -> &mut Tail { + pub fn or_insert(&mut self, head: Head, tail: Tail) -> &mut Tail + where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, + { self.rules.entry(head).or_insert(tail) } + /// Returns a mutable reference to the tail of the rule for the given head; inserts the /// tail if the head is not found. pub fn or_insert_with(&mut self, head: Head, f: F) -> &mut Tail where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, F: FnOnce() -> Tail, { self.rules.entry(head).or_insert_with(f) } + /// Returns a mutable reference to the tail of the rule for the given head; inserts the /// default tail if the head is not found. pub fn or_insert_default(&mut self, head: Head) -> &mut Tail where - Q: Default, - S: Default, + Q: Default + Eq + core::hash::Hash, + S: Default + Eq + core::hash::Hash, { self.or_insert(head, Tail::default()) } + /// Removes a rule from the set of rules. - pub fn remove(&mut self, head: &Head) -> Option> { + pub fn remove(&mut self, head: &Head) -> Option> + where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, + { self.rules.remove(head) } } -impl core::iter::Extend<(Head, Tail)> for RuleSet +#[cfg(feature = "serde")] +impl<'a, Q, S> serde::Deserialize<'a> for RuleMap +where + Q: Eq + core::hash::Hash + serde::Deserialize<'a>, + S: Eq + core::hash::Hash + serde::Deserialize<'a>, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'a>, + { + struct RuleSetVisitor { + _phantom: core::marker::PhantomData<(Q, S)>, + } + + impl<'a, Q, S> serde::de::Visitor<'a> for RuleSetVisitor + where + Q: Eq + core::hash::Hash + serde::Deserialize<'a>, + S: Eq + core::hash::Hash + serde::Deserialize<'a>, + { + type Value = RuleMap; + + fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { + formatter.write_str("a map of rules") + } + + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'a>, + { + let mut rules = HashMap::with_capacity(map.size_hint().unwrap_or(0)); + while let Some((head, tail)) = map.next_entry()? { + rules.insert(head, tail); + } + Ok(RuleMap { + initial_state: None, + rules, + }) + } + } + + deserializer.deserialize_map(RuleSetVisitor { + _phantom: core::marker::PhantomData, + }) + } +} + +impl core::iter::Extend<(Head, Tail)> for RuleMap where Q: Eq + core::hash::Hash, S: Eq + core::hash::Hash, @@ -177,7 +259,7 @@ where } } -impl core::iter::Extend> for RuleSet +impl core::iter::Extend> for RuleMap where Q: Eq + core::hash::Hash, S: Eq + core::hash::Hash, @@ -192,7 +274,7 @@ where } } -impl core::ops::Index> for RuleSet +impl core::ops::Index> for RuleMap where Q: Eq + core::hash::Hash, S: Eq + core::hash::Hash, @@ -204,7 +286,7 @@ where } } -impl core::iter::FromIterator<(Head, Tail)> for RuleSet +impl core::iter::FromIterator<(Head, Tail)> for RuleMap where Q: Default + Eq + core::hash::Hash, S: Eq + core::hash::Hash, @@ -217,7 +299,7 @@ where } } -impl core::iter::FromIterator> for RuleSet +impl core::iter::FromIterator> for RuleMap where Q: Default + Eq + core::hash::Hash, S: Eq + core::hash::Hash, @@ -230,7 +312,7 @@ where } } -impl core::iter::IntoIterator for RuleSet +impl core::iter::IntoIterator for RuleMap where Q: Eq + core::hash::Hash, S: Eq + core::hash::Hash, @@ -243,7 +325,7 @@ where } } -impl<'a, Q, S> core::iter::IntoIterator for &'a RuleSet +impl<'a, Q, S> core::iter::IntoIterator for &'a RuleMap where Q: Eq + core::hash::Hash, S: Eq + core::hash::Hash, @@ -256,7 +338,7 @@ where } } -impl<'a, Q, S> core::iter::IntoIterator for &'a mut RuleSet +impl<'a, Q, S> core::iter::IntoIterator for &'a mut RuleMap where Q: Eq + core::hash::Hash, S: Eq + core::hash::Hash, diff --git a/core/src/state/halt/mod.rs b/core/src/state/halt/mod.rs index 338b783..70bab1c 100644 --- a/core/src/state/halt/mod.rs +++ b/core/src/state/halt/mod.rs @@ -2,8 +2,10 @@ Appellation: halt Contrib: FL03 */ -//! This module is responsible for defining the [Halt] state of a Turing machine. +//! # Halting State //! +//! For all intents and purposes, the halt state is an imaginary state not actually considered +//! by the machine's state space. //! #[doc(inline)] pub use self::{state::Halt, wrap::HaltState}; @@ -11,13 +13,102 @@ pub use self::{state::Halt, wrap::HaltState}; pub(crate) mod state; pub(crate) mod wrap; -use crate::state::RawState; +use super::RawState; #[doc(hidden)] -pub trait Haltable { - const HALT: bool = true; - - type State: RawState; +pub trait Haltable { + type State: RawState; private!(); + + fn is_halted(&self) -> bool; +} + +#[doc(hidden)] +pub trait HaltableExt: Haltable { + fn get(self) -> Option; + + fn get_mut(&mut self) -> Option<&mut Q>; + + fn map(self, f: F) -> Option + where + F: FnOnce(Q) -> U, + Self: Sized, + { + self.get().map(f) + } +} + +/* + ************* Implementations ************* +*/ +use crate::state::State; + +impl Haltable for Halt { + type State = State; + + seal!(); + + fn is_halted(&self) -> bool { + true + } +} + +impl Haltable for State> { + type State = State; + + seal!(); + + fn is_halted(&self) -> bool { + true + } +} +impl Haltable for State> { + type State = State; + + seal!(); + + fn is_halted(&self) -> bool { + self.get_ref().is_none() + } +} + +impl Haltable for Option> { + type State = State; + + seal!(); + + fn is_halted(&self) -> bool { + self.is_none() + } +} + +impl HaltableExt for Halt { + fn get(self) -> Option { + Some(self.0) + } + + fn get_mut(&mut self) -> Option<&mut Q> { + Some(&mut self.0) + } +} + +impl HaltableExt for Option> { + fn get(self) -> Option { + self.map(|state| state.get()) + } + + fn get_mut(&mut self) -> Option<&mut Q> { + self.as_mut().map(|state| state.get_mut()) + } +} + +impl HaltableExt for State> { + fn get(self) -> Option { + self.get() + } + + fn get_mut(&mut self) -> Option<&mut Q> { + self.get_mut().as_mut() + } } diff --git a/core/src/state/halt/state.rs b/core/src/state/halt/state.rs index dd7162e..359662f 100644 --- a/core/src/state/halt/state.rs +++ b/core/src/state/halt/state.rs @@ -2,7 +2,7 @@ Appellation: halting Contrib: FL03 */ -use crate::state::{halt::Haltable, State}; +use crate::state::{RawState, State}; #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -12,27 +12,57 @@ impl Halt { pub fn new(halt: Q) -> Self { Self(halt) } - - pub fn into_inner(self) -> Q { + #[inline] + /// Consumes the halted state and returns the inner value. + pub fn get(self) -> Q { self.0 } - - pub const fn get(&self) -> &Q { + /// Returns an immutable reference to the inner value of the halted state. + pub const fn get_ref(&self) -> &Q { &self.0 } - + /// Returns a mutable reference to the inner value of the halted state. pub fn get_mut(&mut self) -> &mut Q { &mut self.0 } - + /// Replaces the inner value of the halted state with the given value, returning the + /// previous value. + pub fn replace(&mut self, halt: Q) -> Q { + core::mem::replace(&mut self.0, halt) + } + /// Resets the inner value of the halted state to the default value of the type. + pub fn reset(&mut self) + where + Q: Default, + { + self.set(Default::default()); + } + /// Sets the inner value of the halted state to that of the given value. pub fn set(&mut self, halt: Q) { self.0 = halt; } - /// Converts the halted state into a new [State] with an immutable reference to the inner value. + /// Swaps the inner value of the halted state with that of the given state. + pub fn swap(&mut self, other: &mut S) + where + S: RawState, + { + core::mem::swap(&mut self.0, other.get_mut()); + } + /// Takes the inner value of the halted state and replaces it with the default value of + /// the type. + pub fn take(&mut self) -> Q + where + Q: Default, + { + core::mem::take(&mut self.0) + } + /// Converts the halted state into a new [State] with an immutable reference to the inner + /// value. pub fn as_state(&self) -> State> { State(Halt(&self.0)) } - /// Converts the halted state into a new [State] with a mutable reference to the inner value. + /// Converts the halted state into a new [State] with a mutable reference to the inner + /// value. pub fn as_state_mut(&mut self) -> State> { State(Halt(&mut self.0)) } @@ -88,12 +118,8 @@ impl From> for Halt { } } -impl Haltable for Halt { - type State = State; - seal!(); -} - -impl Haltable for State> { - type State = State; - seal!(); +impl From> for State { + fn from(Halt(state): Halt) -> Self { + Self(state) + } } diff --git a/core/src/state/halt/wrap.rs b/core/src/state/halt/wrap.rs index 6355915..469fa95 100644 --- a/core/src/state/halt/wrap.rs +++ b/core/src/state/halt/wrap.rs @@ -6,8 +6,23 @@ use crate::state::{Halt, State}; /// [HaltState] extends the [State] by allowing for an 'imaginary' state that is not actually /// part of the machine's state space. -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, strum::EnumDiscriminants, strum::EnumIs,)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize), strum_discriminants(derive(serde::Deserialize, serde::Serialize)))] +#[derive( + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + strum::EnumDiscriminants, + strum::EnumIs, +)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + strum_discriminants(derive(serde::Deserialize, serde::Serialize)) +)] #[strum_discriminants(name(HaltTag), derive(Hash, Ord, PartialOrd))] pub enum HaltState { Halt(Halt), @@ -41,8 +56,8 @@ impl HaltState { pub fn get(&self) -> &Q { match self { - Self::State(inner) => inner.get(), - Self::Halt(inner) => inner.get(), + Self::State(inner) => inner.get_ref(), + Self::Halt(inner) => inner.get_ref(), } } @@ -77,4 +92,4 @@ impl From> for HaltState { fn from(halt: Halt) -> Self { Self::Halt(halt) } -} \ No newline at end of file +} diff --git a/core/src/state/impls/impl_ext.rs b/core/src/state/impls/impl_ext.rs new file mode 100644 index 0000000..c54e2b1 --- /dev/null +++ b/core/src/state/impls/impl_ext.rs @@ -0,0 +1,103 @@ +/* + Appellation: impl_ext + Contrib: FL03 +*/ +use crate::state::State; + +/* + ************* References ************* +*/ +impl core::convert::AsRef for State { + fn as_ref(&self) -> &Q { + &self.0 + } +} + +impl core::convert::AsMut for State { + fn as_mut(&mut self) -> &mut Q { + &mut self.0 + } +} + +impl core::borrow::Borrow for State { + fn borrow(&self) -> &Q { + &self.0 + } +} + +impl core::borrow::BorrowMut for State { + fn borrow_mut(&mut self) -> &mut Q { + &mut self.0 + } +} + +impl core::ops::Deref for State { + type Target = Q; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl core::ops::DerefMut for State { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/* + ************* Comparisons ************* +*/ +impl core::cmp::PartialEq for State +where + Q: core::cmp::PartialEq, +{ + fn eq(&self, other: &Q) -> bool { + self.get_ref().eq(other) + } +} + +impl core::cmp::PartialOrd for State +where + Q: core::cmp::PartialOrd, +{ + fn partial_cmp(&self, other: &Q) -> Option { + self.get_ref().partial_cmp(other) + } +} + +/* + ************* Conversions ************* +*/ +impl From for State { + fn from(state: Q) -> Self { + State(state) + } +} + +/* + ************* Markers ************* +*/ +unsafe impl core::marker::Send for State where Q: core::marker::Send {} + +unsafe impl core::marker::Sync for State where Q: core::marker::Sync {} + +/* + ************* Formatting ************* +*/ +macro_rules! impl_fmt { + ($($trait:ident),*) => { + $( + impl core::fmt::$trait for State + where + Q: core::fmt::$trait, + { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::$trait::fmt(&self.0, f) + } + } + )* + }; +} + +impl_fmt!(Binary, Display, LowerExp, LowerHex, Octal, UpperExp, UpperHex); diff --git a/core/src/state/impls/impl_ops.rs b/core/src/state/impls/impl_ops.rs new file mode 100644 index 0000000..ce64f0a --- /dev/null +++ b/core/src/state/impls/impl_ops.rs @@ -0,0 +1,236 @@ +/* + Appellation: impl_ops + Contrib: FL03 +*/ +use crate::state::State; + +impl core::ops::Neg for State +where + Q: core::ops::Neg, +{ + type Output = State; + + fn neg(self) -> Self::Output { + State(core::ops::Neg::neg(self.0)) + } +} + +impl<'a, Q> core::ops::Neg for &'a State +where + &'a Q: core::ops::Neg, +{ + type Output = State<<&'a Q as core::ops::Neg>::Output>; + + fn neg(self) -> Self::Output { + State(core::ops::Neg::neg(self.get_ref())) + } +} + +impl core::ops::Not for State +where + Q: core::ops::Not, +{ + type Output = State; + + fn not(self) -> Self::Output { + State(core::ops::Not::not(self.0)) + } +} + +impl<'a, Q> core::ops::Not for &'a State +where + &'a Q: core::ops::Not, +{ + type Output = State<<&'a Q as core::ops::Not>::Output>; + + fn not(self) -> Self::Output { + State(core::ops::Not::not(self.get_ref())) + } +} + +impl num::traits::Num for State +where + Q: num::traits::Num, +{ + type FromStrRadixErr = Q::FromStrRadixErr; + + fn from_str_radix(str: &str, radix: u32) -> Result { + Q::from_str_radix(str, radix).map(State) + } +} + +impl num::traits::One for State +where + Q: PartialEq + num::traits::One, +{ + fn one() -> Self { + State(Q::one()) + } + + fn is_one(&self) -> bool { + self.0.is_one() + } +} + +impl num::traits::Zero for State +where + Q: num::traits::Zero, +{ + fn zero() -> Self { + State(Q::zero()) + } + + fn is_zero(&self) -> bool { + self.0.is_zero() + } +} + +macro_rules! impl_assign_ops { + (@impl $trait:ident::$call:ident) => { + impl ::core::ops::$trait for $crate::state::State { + fn $call(&mut self, rhs: $crate::state::State) { + ::core::ops::$trait::$call(self.get_mut(), rhs.get()) + } + } + + impl ::core::ops::$trait for $crate::state::State { + fn $call(&mut self, rhs: Q) { + ::core::ops::$trait::$call(self.get_mut(), rhs) + } + } + }; + (alt: $($trait:ident::$call:ident),* $(,)?) => { + paste::paste! { + impl_assign_ops!($([<$trait Assign>]::[<$call _assign>]),*); + } + }; + ($($trait:ident::$call:ident),* $(,)?) => { + $( + impl_assign_ops!(@impl $trait::$call); + )* + }; +} + +macro_rules! impl_ops { + (@impl $trait:ident::$call:ident) => { + impl ::core::ops::$trait for State + where + Q: ::core::ops::$trait, + { + type Output = State; + + fn $call(self, rhs: State) -> Self::Output { + State(::core::ops::$trait::$call(self.get(), rhs.get())) + } + } + + impl<'a, Q> ::core::ops::$trait<&'a State> for State + where + Q: ::core::ops::$trait<&'a Q>, + { + type Output = State; + + fn $call(self, rhs: &'a State) -> Self::Output { + State(::core::ops::$trait::$call(self.get(), rhs.get_ref())) + } + } + + impl<'a, Q> ::core::ops::$trait> for &'a State + where + &'a Q: ::core::ops::$trait, + { + type Output = State<<&'a Q as ::core::ops::$trait>::Output>; + + fn $call(self, rhs: State) -> Self::Output { + State(::core::ops::$trait::$call(self.get_ref(), rhs.get())) + } + } + + impl<'a, Q> ::core::ops::$trait<&'a State> for &'a State + where + &'a Q: ::core::ops::$trait<&'a Q>, + { + type Output = State<<&'a Q as ::core::ops::$trait<&'a Q>>::Output>; + + fn $call(self, rhs: &'a State) -> Self::Output { + State(::core::ops::$trait::$call(self.get_ref(), rhs.get_ref())) + } + } + }; + (@inner $trait:ident::$call:ident) => { + impl ::core::ops::$trait for State + where + Q: ::core::ops::$trait, + { + type Output = State; + + fn $call(self, rhs: Q) -> Self::Output { + State(::core::ops::$trait::$call(self.get(), rhs)) + } + } + + impl<'a, Q> ::core::ops::$trait<&'a Q> for State + where + Q: ::core::ops::$trait<&'a Q>, + { + type Output = State; + + fn $call(self, rhs: &'a Q) -> Self::Output { + State(::core::ops::$trait::$call(self.get(), rhs)) + } + } + + impl<'a, Q> ::core::ops::$trait for State<&'a Q> + where + &'a Q: ::core::ops::$trait, + { + type Output = State<<&'a Q as ::core::ops::$trait>::Output>; + + fn $call(self, rhs: Q) -> Self::Output { + State(::core::ops::$trait::$call(self.get(), rhs)) + } + } + + impl<'a, Q> ::core::ops::$trait for &'a State + where + &'a Q: ::core::ops::$trait, + { + type Output = State<<&'a Q as ::core::ops::$trait>::Output>; + + fn $call(self, rhs: Q) -> Self::Output { + State(::core::ops::$trait::$call(self.get_ref(), rhs)) + } + } + + impl<'a, Q> ::core::ops::$trait<&'a Q> for &'a State + where + &'a Q: ::core::ops::$trait<&'a Q>, + { + type Output = State<<&'a Q as ::core::ops::$trait<&'a Q>>::Output>; + + fn $call(self, rhs: &'a Q) -> Self::Output { + State(::core::ops::$trait::$call(self.get_ref(), rhs)) + } + } + }; + ($($trait:ident::$call:ident),* $(,)?) => { + $( + impl_ops!(@impl $trait::$call); + impl_ops!(@inner $trait::$call); + impl_assign_ops!(alt: $trait::$call); + )* + }; +} + +impl_ops! { + Add::add, + BitAnd::bitand, + BitOr::bitor, + BitXor::bitxor, + Div::div, + Mul::mul, + Rem::rem, + Shl::shl, + Shr::shr, + Sub::sub, +} diff --git a/core/src/state/impls/impl_repr.rs b/core/src/state/impls/impl_repr.rs new file mode 100644 index 0000000..adb332c --- /dev/null +++ b/core/src/state/impls/impl_repr.rs @@ -0,0 +1,152 @@ +/* + Appellation: impl_repr + Contrib: FL03 +*/ +use crate::error::Error; +use crate::state::{Halt, State}; +use core::mem::MaybeUninit; + +impl<'a, Q> State<&'a Q> { + /// Clones the internal state and returning a new instance of [State] + pub fn cloned(&self) -> State + where + Q: Clone, + { + State(self.0.clone()) + } + /// Copies the internal state and returning a new instance of [State] + pub fn copied(&self) -> State + where + Q: Copy, + { + State(*self.0) + } +} + +impl<'a, Q> State<&'a mut Q> { + /// Clones the internal state and returning a new instance of [State] + pub fn cloned(&self) -> State + where + Q: Clone, + { + State(self.0.clone()) + } + /// Copies the internal state and returning a new instance of [State] + pub fn copied(&self) -> State + where + Q: Copy, + { + State(*self.0) + } +} + +impl State<*const Q> { + /// Creates a new instance of state with a raw pointer to the inner value. + pub fn from_ptr(ptr: *const Q) -> Self { + Self(ptr) + } +} + +impl State<*mut Q> { + /// Creates a new instance of state with a mutable raw pointer to the inner value. + pub fn from_mut_ptr(ptr: *mut Q) -> Self { + Self(ptr) + } +} + +impl State> { + /// Creates a new instance of state with an initialized inner value. + pub fn init(value: Q) -> Self { + Self(MaybeUninit::new(value)) + } + /// Creates a new instance of state with an uninitialized inner value. + pub const fn uninit() -> Self { + Self(MaybeUninit::uninit()) + } + /// Converts the state into a new instance of [State] with an initialized state. + pub fn assume_init(self) -> State { + State(unsafe { self.get().assume_init() }) + } + /// Writes a value to the inner state. + pub fn write(&mut self, value: Q) -> &mut Q { + self.get_mut().write(value) + } +} + +impl State<()> { + /// Creates a new instance of [State] with an empty state. + pub fn empty() -> Self { + Self(()) + } +} + +impl State { + pub fn from_true() -> Self { + Self(true) + } + + pub fn from_false() -> Self { + Self(false) + } + + pub fn is_true(&self) -> bool { + self.get() + } + + pub fn is_false(&self) -> bool { + !self.get() + } +} + +impl State> { + /// Attempts to downcast the state to a concrete type `Q`; returns an error if the state + /// is not of type `Q`. + pub fn downcast(self) -> Result>, Error> + where + Q: core::any::Any, + { + self.get() + .downcast() + .map(State) + .map_err(|_| Error::type_error("Failed to downcast state")) + } + /// Returns an immutable reference to the state if it is of type `Q`; returns `None` + /// otherwise. + pub fn downcast_ref(&self) -> Option> + where + Q: core::any::Any, + { + self.get_ref().downcast_ref().map(State) + } + + /// Returns a mutable reference to the state if it is of type `Q`; returns `None` + /// otherwise. + pub fn downcast_mut(&mut self) -> Option> + where + Q: core::any::Any, + { + self.get_mut().downcast_mut().map(State) + } +} + +impl State> { + /// Creates a new instance of state whose inner state is [Option::None]. + pub fn none() -> Self { + Self(None) + } + /// Creates a new instance of state whose inner state is [Option::Some]. + pub fn some(value: Q) -> Self { + Self(Some(value)) + } +} + +impl State> { + /// Creates a new instance of [State] from a [Halt] state. + pub fn halted(Halt(inner): Halt) -> Self { + Self(Halt(inner)) + } + /// Converts the halted state into an unhalted state. + pub fn unhalt(self) -> State { + State(self.get().get()) + } +} diff --git a/core/src/state/mod.rs b/core/src/state/mod.rs index 8683728..7105567 100644 --- a/core/src/state/mod.rs +++ b/core/src/state/mod.rs @@ -3,61 +3,91 @@ Contrib: FL03 */ #[doc(inline)] -pub use self::{halt::*, state::State, states::*}; +pub use self::{halt::*, state::State}; pub(crate) mod state; pub mod halt; -pub(crate) mod states { - #[doc(inline)] - pub use self::binary::*; - - pub(crate) mod binary; +mod impls { + pub mod impl_ext; + pub mod impl_ops; + pub mod impl_repr; } - pub(crate) mod prelude { - pub use super::halt::*; + pub use super::halt::Haltable; pub use super::state::State; - pub use super::states::*; + #[cfg(feature = "std")] pub use super::AnyState; + pub use super::MaybeState; } +#[cfg(feature = "std")] +/// A type alias for a [State] whose inner value is the dynamically sized type of a boxed [`Any`](core::any::Any). +pub type AnyState = State>; +/// A type alias for a [State] whose inner value is a [core::mem::MaybeUninit] of generic type `Q`. +pub type MaybeState = State>; -pub type AnyState = State>; - -pub trait BaseState: Eq + PartialOrd + core::hash::Hash {} - +/// [RawState] is a trait describing objects capable of being used as states in our library. +/// The trait contains a single associated trait, the context, or inner value of the state. #[doc(hidden)] pub trait RawState { - type Ctx; -} + type Q; -#[doc(hidden)] -pub trait Stated: RawState { - fn cloned(&self) -> Self - where - Q: Clone; - fn copied(&self) -> Self - where - Q: Copy; + private!(); + + fn get(self) -> Self::Q; + + fn get_ref(&self) -> &Self::Q; + + fn get_mut(&mut self) -> &mut Self::Q; + + fn set(&mut self, inner: Self::Q); } #[doc(hidden)] -pub trait Stateful { - type State: RawState; +pub trait Apply { + type Output; + + fn apply(self, f: F) -> Self::Output + where + F: FnOnce(Q) -> R; } /* ************* Implementations ************* */ -impl RawState for Halt { - type Ctx = Q; -} +macro_rules! impl_raw_state { + (@impl $state:ident($($field:tt)*)) => { + impl RawState for $state { + type Q = Q; + + seal!(); + + fn get(self) -> Q { + self.$($field)* + } + + fn get_ref(&self) -> &Q { + &self.$($field)* + } + + fn get_mut(&mut self) -> &mut Q { + &mut self.$($field)* + } -impl RawState for State { - type Ctx = Q; + fn set(&mut self, inner: Q) { + self.$($field)* = inner; + } + } + }; + ($($T:ident($($field:tt)*)),* $(,)?) => { + $( + impl_raw_state!(@impl $T($($field)*)); + )* + }; } -impl Stateful for State { - type State = State; +impl_raw_state! { + Halt(0), + State(0), } diff --git a/core/src/state/state.rs b/core/src/state/state.rs index 52693c6..ff53fba 100644 --- a/core/src/state/state.rs +++ b/core/src/state/state.rs @@ -2,87 +2,133 @@ Appellation: state Contrib: FL03 */ -use crate::state::Halt; -use std::sync::Arc; -/// +use super::{Halt, RawState}; + +/// [State] is a generalized state implementation, representing the state of a system or +/// object. #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[repr(transparent)] -pub struct State(pub Q); +pub struct State(pub Q); impl State { - pub fn new(state: Q) -> Self { + pub fn from_value(state: Q) -> Self { Self(state) } - - /// Returns an immutable reference to the state. - pub const fn get(&self) -> &Q { - &self.0 + /// Returns a new instance of state with a raw pointer to the inner value. + pub fn as_ptr(&self) -> *const Q { + core::ptr::addr_of!(self.0) } - /// Returns a mutable reference to the state. - pub fn get_mut(&mut self) -> &mut Q { - &mut self.0 + /// Returns a new instance of state with a mutable raw pointer to the inner value. + pub fn as_mut_ptr(&mut self) -> *mut Q { + core::ptr::addr_of_mut!(self.0) } + /// Casts the state to a new type, returning a new instance of [State]. + /// + /// # Saftey + /// + /// This method is unsafe because it is up to the caller to ensure that the cast is valid. + pub unsafe fn cast(self) -> State { + State(core::ptr::read(&self.0 as *const Q as *const R)) + } + #[inline] /// Consumes and returns the inner value of the state. - pub fn into_inner(self) -> Q { + pub fn get(self) -> Q { self.0 } + /// Returns an immutable reference to the inner value of the state. + pub const fn get_ref(&self) -> &Q { + &self.0 + } + /// Returns a mutable reference to the inner value of the state. + pub fn get_mut(&mut self) -> &mut Q { + self.as_mut() + } + /// [State::map] applies the given function onto the inner value of the state, returning a + /// new state with the result. Essentially, the method describes the mechanism for + /// transfroming the state. + pub fn map(self, f: F) -> State + where + F: FnOnce(Q) -> R, + { + State(f(self.get())) + } + /// Replaces the state with a new value, returning the old value. + pub fn replace(&mut self, state: Q) -> Q { + core::mem::replace(&mut self.0, state) + } + /// Clears the state, setting it to its default value. + pub fn reset(&mut self) + where + Q: Default, + { + self.set(Default::default()); + } /// Sets the state to a new value. pub fn set(&mut self, state: Q) { self.0 = state; } + /// Swaps the inner value of the state with that of the given state. + pub fn swap(&mut self, other: &mut S) + where + S: RawState, + { + core::mem::swap(&mut self.0, other.get_mut()); + } + /// Takes the inner value of the state, replacing it with the default value and returning + /// the previous value. + pub fn take(&mut self) -> Q + where + Q: Default, + { + core::mem::take(&mut self.0) + } /// Returns a halted state with an immutable reference to the state. pub fn as_halt(&self) -> State> { State(Halt(self)) } /// Consumes the state and returns a halted state. pub fn into_halt(self) -> State> { - State(Halt(self.into_inner())) + State(Halt(self.get())) } /// Returns a new state with a boxed inner value. pub fn boxed(self) -> State> { - State(Box::new(self.0)) - } - /// [State::map] applies a [`Fn`] closure to the state, returing a new state in the process. - /// Essentially, the method sufficiently describes the transformation of the state. - pub fn map(self, f: F) -> State - where - F: Fn(&Q) -> R, - { - State(f(self.get())) + self.map(Box::new) } - /// [State::map_mut] applies a [`FnMut`] closure to the state, returing the transformed state. - pub fn map_mut(mut self, f: &mut F) -> State + /// Converts the inner type into a boxed "any" state, returning a new instance of state + pub fn as_any(&self) -> State> where - F: FnMut(&mut Q) -> R, + Q: Clone + 'static, { - State(f(self.get_mut())) + State(Box::new(self.get_ref().clone())) } - /// Maps the state to a new state using a closure that takes the state by value. - pub fn map_once(self, f: F) -> State + /// Converts the inner type into a boxed "any" state, returning a new instance of state + pub fn into_any(self) -> State> where - F: FnOnce(State) -> R, + Q: 'static, { - State(f(self)) + State(Box::new(self.get())) } + #[cfg(feature = "std")] /// Wraps the inner value of the state with an [`Arc`] and returns a new instance of [State] - pub fn shared(self) -> State> { - State(Arc::new(self.0)) + pub fn shared(self) -> State> { + self.map(std::sync::Arc::new) } + #[cfg(feature = "std")] /// Returns a shared reference to the state. - pub fn to_shared(&self) -> State> + pub fn to_shared(&self) -> State> where Q: Clone, { - State(Arc::new(self.0.clone())) + self.clone().shared() } /// Returns a state with an owned inner value. pub fn to_ref(&self) -> State<&Q> { - State(&self.0) + State(self.get_ref()) } /// Returns a state with a mutable reference to the inner value. pub fn to_mut(&mut self) -> State<&mut Q> { - State(&mut self.0) + State(self.get_mut()) } /// Returns the `name` of the generic inner type, `Q`. pub fn get_inner_type_name(&self) -> &'static str { @@ -107,196 +153,3 @@ impl State { core::any::TypeId::of::() == core::any::TypeId::of::>>() } } - -impl State> { - /// Creates a new instance of [State] from a [Halt] state. - pub fn halted(Halt(inner): Halt) -> Self { - Self(Halt(inner)) - } - - pub fn unhalt(self) -> State { - State(self.0.into_inner()) - } -} - -impl<'a, Q> State<&'a Q> { - /// Clones the internal state and returning a new instance of [State] - pub fn cloned(&self) -> State - where - Q: Clone, - { - State(self.0.clone()) - } - /// Copies the internal state and returning a new instance of [State] - pub fn copied(&self) -> State - where - Q: Copy, - { - State(*self.0) - } -} - -impl<'a, Q> State<&'a mut Q> { - /// Clones the internal state and returning a new instance of [State] - pub fn cloned(&self) -> State - where - Q: Clone, - { - State(self.0.clone()) - } - /// Copies the internal state and returning a new instance of [State] - pub fn copied(&self) -> State - where - Q: Copy, - { - State(*self.0) - } -} - -impl AsRef for State { - fn as_ref(&self) -> &Q { - &self.0 - } -} - -impl AsMut for State { - fn as_mut(&mut self) -> &mut Q { - &mut self.0 - } -} - -impl core::borrow::Borrow for State { - fn borrow(&self) -> &Q { - &self.0 - } -} - -impl core::borrow::BorrowMut for State { - fn borrow_mut(&mut self) -> &mut Q { - &mut self.0 - } -} - -impl core::ops::Deref for State { - type Target = Q; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl core::ops::DerefMut for State { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -macro_rules! impl_fmt { - ($($trait:ident),*) => { - $( - impl core::fmt::$trait for State - where - Q: core::fmt::$trait, - { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - core::fmt::$trait::fmt(&self.0, f) - } - } - )* - }; -} - -impl_fmt!(Binary, Display, LowerExp, LowerHex, Octal, UpperExp, UpperHex); - -unsafe impl core::marker::Send for State where Q: core::marker::Send {} - -unsafe impl core::marker::Sync for State where Q: core::marker::Sync {} - -impl PartialEq for State -where - Q: PartialEq, -{ - fn eq(&self, other: &Q) -> bool { - self.get().eq(other) - } -} - -impl PartialOrd for State -where - Q: PartialOrd, -{ - fn partial_cmp(&self, other: &Q) -> Option { - self.get().partial_cmp(other) - } -} - -impl From for State { - fn from(state: Q) -> Self { - State(state) - } -} - -macro_rules! impl_ops { - (@impl $trait:ident.$call:ident) => { - impl ::core::ops::$trait for State - where - Q: ::core::ops::$trait, - { - type Output = State; - - fn $call(self, rhs: State) -> Self::Output { - State(::core::ops::$trait::$call(self.0, rhs.0)) - } - } - - impl ::core::ops::$trait for State - where - Q: core::ops::$trait, - { - type Output = State; - - fn $call(self, rhs: Q) -> Self::Output { - State(::core::ops::$trait::$call(self.0, rhs)) - } - } - - paste::paste! { - impl ::core::ops::[<$trait Assign>]> for State - where - Q: ::core::ops::[<$trait Assign>], - { - - fn [<$call _assign>](&mut self, rhs: State) { - ::core::ops::[<$trait Assign>]::[<$call _assign>](self.get_mut(), rhs.0) - } - } - impl ::core::ops::[<$trait Assign>] for State - where - Q: ::core::ops::[<$trait Assign>], - { - - fn [<$call _assign>](&mut self, rhs: Q) { - ::core::ops::[<$trait Assign>]::[<$call _assign>](self.get_mut(), rhs) - } - } - } - }; - ($($trait:ident.$call:ident),* $(,)?) => { - $( - impl_ops!(@impl $trait.$call); - )* - }; -} - -impl_ops! { - Add.add, - BitAnd.bitand, - BitOr.bitor, - BitXor.bitxor, - Div.div, - Mul.mul, - Rem.rem, - Shl.shl, - Shr.shr, - Sub.sub, -} diff --git a/core/src/state/states/binary.rs b/core/src/state/states/binary.rs deleted file mode 100644 index 939ced3..0000000 --- a/core/src/state/states/binary.rs +++ /dev/null @@ -1,154 +0,0 @@ -/* - Appellation: state - Contrib: FL03 -*/ -use crate::State; - -#[derive( - Clone, - Copy, - Debug, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - strum::EnumDiscriminants, - strum::EnumIs, - strum::VariantNames, -)] -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize, serde::Serialize), - serde(rename_all = "lowercase"), - strum_discriminants( - derive(serde::Deserialize, serde::Serialize), - serde(rename_all = "lowercase") - ) -)] -#[repr(C)] -#[strum(serialize_all = "lowercase")] -#[strum_discriminants( - name(BinState), - derive( - Hash, - Ord, - PartialOrd, - strum::AsRefStr, - strum::Display, - strum::EnumCount, - strum::EnumIs, - strum::EnumIter, - strum::EnumString, - strum::VariantNames - ) -)] -pub enum BinaryState { - Invalid(I), - Valid(V), -} - -impl BinaryState { - pub fn invalid(state: I) -> Self { - Self::Invalid(state) - } - - pub fn valid(state: V) -> Self { - Self::Valid(state) - } - - pub fn invalidate(self, state: Q) -> BinaryState { - match self { - Self::Invalid(_) => BinaryState::Invalid(state), - Self::Valid(_) => BinaryState::Invalid(state), - } - } - - pub fn kind(&self) -> BinState { - match self { - Self::Invalid(_) => BinState::Invalid, - Self::Valid(_) => BinState::Valid, - } - } -} - -impl BinaryState { - pub fn into_inner(self) -> State { - match self { - Self::Invalid(q) => State(q), - Self::Valid(q) => State(q), - } - } - - pub fn state(&self) -> (BinState, &Q) { - (self.kind(), self.as_ref()) - } -} - -impl AsRef for BinaryState { - fn as_ref(&self) -> &Q { - match self { - Self::Invalid(q) => q, - Self::Valid(q) => q, - } - } -} - -impl AsMut for BinaryState { - fn as_mut(&mut self) -> &mut Q { - match self { - Self::Invalid(q) => q, - Self::Valid(q) => q, - } - } -} - -impl core::borrow::Borrow for BinaryState { - fn borrow(&self) -> &Q { - self.as_ref() - } -} - -impl core::borrow::BorrowMut for BinaryState { - fn borrow_mut(&mut self) -> &mut Q { - self.as_mut() - } -} - -impl Default for BinState { - fn default() -> Self { - Self::Invalid - } -} - -impl Default for BinaryState -where - I: Default, -{ - fn default() -> Self { - Self::invalid(::default()) - } -} - -impl core::ops::Deref for BinaryState { - type Target = Q; - - fn deref(&self) -> &Self::Target { - self.as_ref() - } -} - -impl core::ops::DerefMut for BinaryState { - fn deref_mut(&mut self) -> &mut Self::Target { - self.as_mut() - } -} - -impl core::fmt::Display for BinaryState -where - Q: core::fmt::Display, -{ - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - write!(f, "{}", *self) - } -} diff --git a/core/src/tape/hash_tape.rs b/core/src/tape/hash_tape.rs deleted file mode 100644 index 47d302d..0000000 --- a/core/src/tape/hash_tape.rs +++ /dev/null @@ -1,104 +0,0 @@ -/* - Appellation: hash_tape - Contrib: FL03 -*/ -// #![cfg(feature = "std")] -use crate::shift::Direction; -use std::collections::hash_map::{self, HashMap}; - -pub trait HashIndex: Eq + core::hash::Hash + core::ops::Neg {} - -pub type Hdx = isize; - -#[derive(Clone, Debug, Default)] -pub struct HashTape { - cursor: Hdx, - store: HashMap, - ticks: usize, -} - -impl HashTape { - pub fn new() -> HashTape { - HashTape { - cursor: 0, - store: HashMap::new(), - ticks: 0, - } - } - - pub fn reset(&mut self) { - self.cursor = 0; - self.store.clear(); - self.ticks = 0; - } - - pub fn cursor(&self) -> Hdx { - self.cursor - } - - pub fn ticks(&self) -> usize { - self.ticks - } - - pub fn entry(&mut self, index: Hdx) -> hash_map::Entry { - self.store.entry(index) - } - - pub fn get(&self, index: Hdx) -> Option<&V> { - self.store.get(&index) - } - /// Returns a mutable reference to the value at the given index. - pub fn get_mut(&mut self, index: Hdx) -> Option<&mut V> { - self.store.get_mut(&index) - } - /// Inserts a value at the given index. - pub fn insert(&mut self, index: Hdx, value: V) { - self.store.insert(index, value); - } - /// Returns true if the tape is empty. - pub fn is_empty(&self) -> bool { - self.store.is_empty() - } - /// Returns the number of elements in the tape. - pub fn len(&self) -> usize { - self.store.len() - } - /// Removes the value at the given index. - pub fn remove(&mut self, index: Hdx) -> Option { - self.store.remove(&index) - } - /// Shifts the cursor in the given direction. - pub fn shift(&mut self, direction: Direction) { - self.cursor += direction; - self.ticks += 1; - } - /// Returns a mutable reference to the value of the head at the current position; on empty, - /// the given value is inserted and returned. - pub fn or_insert(&mut self, default: V) -> &mut V { - self.store.entry(self.cursor).or_insert(default) - } - /// Returns a mutable reference to the value of the head at the current position; on empty, - /// the function is evaluated and the result is inserted and returned. - pub fn or_insert_with(&mut self, default: F) -> &mut V - where - F: FnOnce() -> V, - { - self.store.entry(self.cursor).or_insert_with(default) - } - /// Returns a mutable reference to the value of the head at the current position; if the - /// value is not present, the default value is inserted and returned. - pub fn or_default(&mut self) -> &mut V - where - V: Default, - { - self.store.entry(self.cursor).or_default() - } - /// Returns a reference to the value at the current cursor position. - pub fn read(&self) -> Option<&V> { - self.store.get(&self.cursor) - } - - pub fn write(&mut self, value: V) { - let _ = self.store.insert(self.cursor, value); - } -} diff --git a/core/src/tape/mod.rs b/core/src/tape/mod.rs deleted file mode 100644 index 3f52730..0000000 --- a/core/src/tape/mod.rs +++ /dev/null @@ -1,181 +0,0 @@ -/* - Appellation: tape - Contrib: FL03 -*/ -//! # Tape -//! -//! Idealized Turing machines consider a tape, or memory, that is infinite in both directions. -//! This tape is a one-dimensional array of symbols manipulated by the tape head according to -//! some set of pre-defined rules. -pub use self::tape::StdTape; - -pub(crate) mod tape; - -#[doc(hidden)] -pub mod hash_tape; - -pub(crate) mod prelude { - pub use super::tape::StdTape; -} - -#[doc(hidden)] -pub trait Mem { - type Key; - type Value; - - fn clear(&mut self); - - fn get(&self, key: &Self::Key) -> Option<&Self::Value>; - - fn get_mut(&mut self, key: &Self::Key) -> Option<&mut Self::Value>; - - fn insert(&mut self, key: Self::Key, value: Self::Value); - - fn is_empty(&self) -> bool; - - fn len(&self) -> usize; -} - -#[doc(hidden)] -/// [RawTape] defines the basic interface used for tape-like structures; i.e., a contiguous, -/// sequential array of elements. -pub trait RawTape { - type Elem; - - private!(); - - fn as_slice(&self) -> &[Self::Elem]; - - fn is_empty(&self) -> bool { - self.len() == 0 - } - - fn len(&self) -> usize { - self.as_slice().len() - } -} - -#[doc(hidden)] -/// [Tape] is a -pub trait Tape: RawTape { - type Idx; - - fn clear(&mut self); - - fn get(&self, idx: &Self::Idx) -> Option<&Self::Elem>; - - fn get_mut(&mut self, idx: &Self::Idx) -> Option<&mut Self::Elem>; - - fn insert(&mut self, idx: Self::Idx, elem: Self::Elem); -} - -/* - ************* Implementations ************* -*/ -#[cfg(feature = "alloc")] -use alloc::vec::Vec; -use std::collections::HashMap; - -impl RawTape for [T] { - type Elem = T; - - seal!(); - - fn as_slice(&self) -> &[Self::Elem] { - &self - } - - fn is_empty(&self) -> bool { - <[T]>::is_empty(self) - } - - fn len(&self) -> usize { - <[T]>::len(self) - } -} - -impl RawTape for Vec { - type Elem = T; - - seal!(); - - fn as_slice(&self) -> &[Self::Elem] { - Vec::as_slice(self) - } - - fn is_empty(&self) -> bool { - Vec::is_empty(self) - } - - fn len(&self) -> usize { - Vec::len(self) - } -} - -impl Mem for Vec { - type Key = usize; - type Value = V; - - fn clear(&mut self) { - Vec::clear(self); - } - - fn get(&self, key: &Self::Key) -> Option<&Self::Value> { - match key { - key if *key < self.len() => Some(&self[*key]), - _ => None, - } - } - - fn get_mut(&mut self, key: &Self::Key) -> Option<&mut Self::Value> { - match key { - key if *key < self.len() => Some(&mut self[*key]), - _ => None, - } - } - - fn insert(&mut self, key: Self::Key, value: Self::Value) { - Vec::insert(self, key, value); - } - - fn is_empty(&self) -> bool { - Vec::is_empty(self) - } - - fn len(&self) -> usize { - Vec::len(self) - } -} - -impl Mem for HashMap -where - K: Eq + std::hash::Hash, - V: Eq + std::hash::Hash, -{ - type Key = K; - type Value = V; - - fn clear(&mut self) { - HashMap::clear(self); - } - - fn get(&self, key: &Self::Key) -> Option<&Self::Value> { - HashMap::get(self, key) - } - - fn get_mut(&mut self, key: &Self::Key) -> Option<&mut Self::Value> { - HashMap::get_mut(self, &key) - } - - fn insert(&mut self, key: Self::Key, value: Self::Value) { - HashMap::insert(self, key, value); - } - - fn is_empty(&self) -> bool { - HashMap::is_empty(self) - } - - fn len(&self) -> usize { - HashMap::len(self) - } -} diff --git a/core/src/traits/container.rs b/core/src/traits/container.rs new file mode 100644 index 0000000..cc36f36 --- /dev/null +++ b/core/src/traits/container.rs @@ -0,0 +1,32 @@ +/* + Appellation: container + Contrib: FL03 +*/ + +pub trait RawContainer { + type Elem; +} + +/* + ************* Implementations ************* +*/ + +impl RawContainer for Vec { + type Elem = T; +} + +impl RawContainer for Box<[T]> { + type Elem = T; +} + +impl RawContainer for [T] { + type Elem = T; +} + +impl RawContainer for &mut [T] { + type Elem = T; +} + +impl RawContainer for &mut [T; N] { + type Elem = T; +} diff --git a/core/src/shift/mod.rs b/core/src/traits/convert.rs similarity index 68% rename from core/src/shift/mod.rs rename to core/src/traits/convert.rs index 7d7903b..46953b8 100644 --- a/core/src/shift/mod.rs +++ b/core/src/traits/convert.rs @@ -1,19 +1,9 @@ /* - Appellation: shift + Appellation: convert Contrib: FL03 */ -#[doc(inline)] -pub use self::direction::Direction; +use crate::Direction; -pub(crate) mod direction; - -pub(crate) mod prelude { - pub use super::direction::Direction; -} - -pub trait Directional { - fn direction(&self) -> Direction; -} /// The [AsDirection] trait provides a convience method for converting a type into a [Direction]. pub trait AsDirection { fn as_direction(&self) -> Direction; @@ -28,10 +18,10 @@ pub trait IntoDirection { */ impl AsDirection for T where - T: Clone + Into, + T: Clone + IntoDirection, { fn as_direction(&self) -> Direction { - self.clone().into() + self.clone().into_direction() } } diff --git a/core/src/traits/cspace.rs b/core/src/traits/cspace.rs deleted file mode 100644 index c78b063..0000000 --- a/core/src/traits/cspace.rs +++ /dev/null @@ -1,20 +0,0 @@ -/* - Appellation: fsm - Contrib: FL03 -*/ - -pub trait Point { - type Elem; -} - -pub trait RawSpace { - type Elem; - - private!(); -} - -pub trait Space: RawSpace {} - -pub trait ConfigSpace { - type Space; -} diff --git a/core/src/traits/execute.rs b/core/src/traits/execute.rs deleted file mode 100644 index 7a9e427..0000000 --- a/core/src/traits/execute.rs +++ /dev/null @@ -1,10 +0,0 @@ -/* - Appellation: execute - Contrib: FL03 -*/ - -pub trait Execute { - type Output; - - fn execute(&self) -> Result; -} diff --git a/core/src/traits/indexing.rs b/core/src/traits/indexing.rs new file mode 100644 index 0000000..78b604a --- /dev/null +++ b/core/src/traits/indexing.rs @@ -0,0 +1,50 @@ +/* + Appellation: indexing + Contrib: FL03 +*/ + +#[doc(hidden)] +pub trait RawIndex { + private!(); +} + +pub trait Index: RawIndex { + fn increment(self) -> Self; + + fn decrement(self) -> Self; +} + +pub trait HashIndex: Index + core::cmp::Eq + core::hash::Hash {} + +/* + ************* Implementations ************* +*/ +impl Index for T +where + T: RawIndex + core::ops::Add + core::ops::Sub + num::One, +{ + fn increment(self) -> Self { + self + T::one() + } + + fn decrement(self) -> Self { + self - T::one() + } +} + +impl HashIndex for T where T: Index + core::cmp::Eq + core::hash::Hash {} + +macro_rules! impl_index { + (@impl $T:ty) => { + impl RawIndex for $T { + seal!(); + } + }; + ($($T:ty),* $(,)?) => { + $( + impl_index!(@impl $T); + )* + }; +} + +impl_index!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize); diff --git a/core/src/traits/mod.rs b/core/src/traits/mod.rs index b8a0271..568c721 100644 --- a/core/src/traits/mod.rs +++ b/core/src/traits/mod.rs @@ -3,21 +3,23 @@ Contrib: FL03 */ #[doc(inline)] -pub use self::{execute::*, increment::*, symbolic::*, transform::*}; +pub use self::{convert::*, increment::*, indexing::*, symbols::*}; -pub(crate) mod execute; -pub(crate) mod increment; -pub(crate) mod symbolic; -pub(crate) mod transform; +mod convert; +mod increment; +mod indexing; +mod symbols; #[doc(hidden)] -pub mod cspace; +pub mod container; #[doc(hidden)] pub mod io; +#[doc(hidden)] +pub mod transform; pub(crate) mod prelude { - pub use super::execute::*; + pub use super::convert::*; pub use super::increment::*; - pub use super::symbolic::*; - pub use super::transform::*; + pub use super::indexing::*; + pub use super::symbols::*; } diff --git a/core/src/traits/symbolic.rs b/core/src/traits/symbols.rs similarity index 98% rename from core/src/traits/symbolic.rs rename to core/src/traits/symbols.rs index 57766d2..33c3d28 100644 --- a/core/src/traits/symbolic.rs +++ b/core/src/traits/symbols.rs @@ -79,6 +79,7 @@ impl Alphabet for [S] { } } +#[cfg(feature = "alloc")] impl Alphabet for Vec { type Elem = S; diff --git a/core/src/traits/transform.rs b/core/src/traits/transform.rs index faabec5..42ddf7a 100644 --- a/core/src/traits/transform.rs +++ b/core/src/traits/transform.rs @@ -2,21 +2,57 @@ Appellation: transform Contrib: FL03 */ +use crate::{Direction, Head, State}; -/// [`Shift`] describes a generalized shift operation; -/// w.r.t. Turing machines, Moore (1990) describes a shift operation as a _movement_ of the -/// tape head -pub trait Shift { +/// [Handle] is a generic trait describing objects capable of handling some input and producing +/// some output. +pub trait Handle { type Output; - fn shift(&self, tape: &T) -> Self::Output; + fn handle(&mut self, args: T) -> Self::Output; } -/// [`Transform`] is describes a binary operation capable of applying some transformation. -/// More commonly, the typical "rustic" manner of which an object is transformed is through -/// the [`map`] method, which applies a function to a value and returns a new value. -pub trait Transform { + +/// [TM] +pub trait TM { + type Idx: Copy + core::ops::Add; + + fn process(&mut self, direction: Direction, state: State, symbol: A) -> Head { + let pos = self.position(); + self.write(symbol); + self.scope_mut().replace(state, pos + direction) + } + + fn read(&self) -> Option; + + fn scope(&self) -> &Head; + + fn scope_mut(&mut self) -> &mut Head; + + fn write(&mut self, symbol: A) -> Option; + + fn position(&self) -> Self::Idx { + self.scope().symbol + } +} + +pub trait Driver { + type Head; + + fn read(&self) -> Option; + + fn scope(&self) -> Head<&'_ Q, &'_ A>; + + fn write(&mut self, symbol: A) -> Option; +} + +pub trait Read { type Output; - /// [`Transform::transform`] is a method that takes a reference to `self` and a value of type - /// `T` and returns a value of type [`Transform::Output`]. - fn transform(&self, delta: T) -> Self::Output; + + fn read(&self) -> Self::Output; +} + +pub trait Write { + type Output; + + fn write(&self) -> Self::Output; } diff --git a/core/src/types/cell.rs b/core/src/types/cell.rs new file mode 100644 index 0000000..66a225f --- /dev/null +++ b/core/src/types/cell.rs @@ -0,0 +1,22 @@ +/* + Appellation: cell + Contrib: FL03 +*/ +use crate::State; + +/// [Cell] is a struct +pub struct Cell { + pub index: usize, + pub state: State, + pub symbol: A, +} + +impl Cell { + pub fn state(&self) -> State<&Q> { + self.state.to_ref() + } + + pub fn symbol(&self) -> &A { + &self.symbol + } +} diff --git a/core/src/types/cursor.rs b/core/src/types/cursor.rs deleted file mode 100644 index de47332..0000000 --- a/core/src/types/cursor.rs +++ /dev/null @@ -1,193 +0,0 @@ -/* - Appellation: cursor - Contrib: FL03 -*/ -use crate::Direction; - -/// Here, [Cursor] describes a type capable of tracking both the position and number of steps -/// taken by an actor. -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Cursor { - /// The current position of the cursor. - pub(crate) position: Idx, - /// The number of steps taken by the cursor. - pub(crate) ticks: usize, -} - -impl Cursor { - pub fn new() -> Cursor - where - Idx: Default, - { - Cursor { - position: Idx::default(), - ticks: 0, - } - } - /// Resets the cursor to its initial state. - pub fn reset(&mut self) - where - Idx: Default, - { - self.position = Idx::default(); - self.ticks = 0; - } - /// Returns the current position of the cursor. - pub const fn position(&self) -> &Idx { - &self.position - } - - pub fn ticks(&self) -> usize { - self.ticks - } - - pub fn set_position(&mut self, position: Idx) { - self.position = position; - self.on_update() - } - /// Shifts the cursor in the given direction. - pub fn shift(&mut self, direction: Direction) - where - Idx: core::ops::AddAssign, - { - self.position += direction; - self.on_update() - } - - pub fn shift_left(&mut self) - where - Idx: core::ops::AddAssign, - { - self.shift(Direction::Left) - } - - pub fn shift_right(&mut self) - where - Idx: core::ops::AddAssign, - { - self.shift(Direction::Right) - } - - pub fn stay(&mut self) { - self.on_update() - } - - pub(crate) fn on_update(&mut self) { - self.ticks += 1; - } -} - -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Ticker { - pub(crate) ticks: T, -} - -impl Ticker { - pub fn new() -> Self - where - T: Default, - { - Self { - ticks: T::default(), - } - } - - pub const fn get(&self) -> &T { - &self.ticks - } - - pub fn reset(&mut self) - where - T: Default, - { - self.ticks = T::default(); - } - - pub fn set(&mut self, ticks: T) { - self.ticks = ticks; - } - - pub fn tick(&mut self) - where - T: core::ops::AddAssign + num::One, - { - self.ticks += T::one(); - } -} - -impl core::convert::AsRef for Ticker { - fn as_ref(&self) -> &T { - &self.ticks - } -} - -impl core::convert::AsMut for Ticker { - fn as_mut(&mut self) -> &mut T { - &mut self.ticks - } -} - -impl core::ops::Deref for Ticker { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.ticks - } -} - -impl core::ops::DerefMut for Ticker { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.ticks - } -} - -impl core::iter::Iterator for Ticker -where - T: Copy + core::ops::AddAssign + num::One, -{ - type Item = T; - - fn next(&mut self) -> Option { - self.tick(); - Some(self.ticks) - } -} - -impl core::cmp::PartialEq for Ticker -where - T: PartialEq, -{ - fn eq(&self, other: &T) -> bool { - &self.ticks == other - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_cursor() { - let mut cursor = Cursor::::new(); - assert_eq!(*cursor.position(), 0); - assert_eq!(cursor.ticks(), 0); - - cursor.shift(Direction::Right); - assert_eq!(*cursor.position(), 1); - assert_eq!(cursor.ticks(), 1); - - cursor.shift(Direction::Left); - assert_eq!(*cursor.position(), 0); - assert_eq!(cursor.ticks(), 2); - - cursor.shift(Direction::Stay); - assert_eq!(*cursor.position(), 0); - assert_eq!(cursor.ticks(), 3); - - cursor.set_position(10); - assert_eq!(*cursor.position(), 10); - assert_eq!(cursor.ticks(), 4); - } -} diff --git a/core/src/shift/direction.rs b/core/src/types/direction.rs similarity index 76% rename from core/src/shift/direction.rs rename to core/src/types/direction.rs index 60ace26..a5d082d 100644 --- a/core/src/shift/direction.rs +++ b/core/src/types/direction.rs @@ -2,14 +2,13 @@ Appellation: direction Contrib: FL03 */ - /// [Direction] enumerates the various directions a head can move, namely: left, right, and stay. +/// /// The included methods and implementations aim to streamline the conversion between [Direction] and other types. #[derive( Clone, Copy, Debug, - Default, Eq, Hash, Ord, @@ -18,7 +17,9 @@ strum::AsRefStr, strum::Display, strum::EnumCount, + strum::EnumIs, strum::EnumIter, + strum::VariantArray, strum::VariantNames, )] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -48,7 +49,6 @@ pub enum Direction { )] /// Represents a single right shift Right = 1, - #[default] #[cfg_attr( feature = "serde", serde( @@ -139,52 +139,87 @@ impl Direction { } } +impl Default for Direction { + fn default() -> Self { + Self::Stay + } +} + impl core::ops::Add for Direction where - T: crate::Decrement + crate::Increment, + T: core::ops::Add + core::ops::Sub + num::One, { type Output = T; fn add(self, rhs: T) -> Self::Output { match self { - Self::Left => rhs.decrement(), - Self::Right => rhs.increment(), + Self::Left => rhs - T::one(), + Self::Right => rhs + T::one(), Self::Stay => rhs, } } } -impl core::ops::Add for isize { - type Output = isize; +macro_rules! impl_apply_direction { + (@unsigned $T:ty) => { + impl core::ops::Add for $T { + type Output = $T; - fn add(self, rhs: Direction) -> Self::Output { - self + rhs as isize - } -} + fn add(self, rhs: Direction) -> Self::Output { + match rhs { + Direction::Left => self.wrapping_sub(1), + Direction::Right => self.wrapping_add(1), + Direction::Stay => self, + } + } + } + + impl core::ops::AddAssign for $T { + fn add_assign(&mut self, rhs: Direction) { + *self = match rhs { + Direction::Left => self.wrapping_sub(1), + Direction::Right => self.wrapping_add(1), + Direction::Stay => 0, + }; + } + } -impl core::ops::Add for usize { - type Output = usize; + }; + (@signed $T:ty) => { + impl core::ops::Add for $T { + type Output = $T; - fn add(self, rhs: Direction) -> Self::Output { - self.wrapping_add_signed(rhs as isize) - } -} + fn add(self, rhs: Direction) -> Self::Output { + self + rhs as $T + } + } -impl core::ops::AddAssign for usize { - fn add_assign(&mut self, rhs: Direction) { - *self = core::ops::Add::add(*self, rhs); - } -} + impl core::ops::AddAssign for $T { + + fn add_assign(&mut self, rhs: Direction) { + *self += rhs as $T; + } + } + + }; + (signed: $($T:ty),* $(,)?) => { + $( + impl_apply_direction!(@signed $T); + )* + }; + (unsigned: $($T:ty),* $(,)?) => { + $( + impl_apply_direction!(@unsigned $T); + )* + }; -impl core::ops::AddAssign for isize { - fn add_assign(&mut self, rhs: Direction) { - *self = core::ops::Add::add(*self, rhs); - } } +impl_apply_direction!(signed: i8, i16, i32, i64, i128, isize,); +impl_apply_direction!(unsigned: u8, u16, u32, u64, u128, usize); mod impl_from { use super::*; - use crate::shift::IntoDirection; + use crate::IntoDirection; macro_rules! impl_from_direction { ($($T:ident),*) => { diff --git a/core/src/types/head.rs b/core/src/types/head.rs index 7c9be8c..3ffd75e 100644 --- a/core/src/types/head.rs +++ b/core/src/types/head.rs @@ -4,17 +4,14 @@ */ use crate::state::State; -/// The [Head] struct represent the state and symbol of an actor at a given moment in time. -/// With respect to a Turing machine, the head defines the current state and symbol of the -/// machine. When associated with a direction the head becomes a tail, instructing the machine -/// to move, write, and transition to a new state. +/// The [Head] is formally defined to be a 2-tuple consisting of a state / symbol pair. #[derive(Clone, Copy, Default, PartialEq, Eq, Hash, Ord, PartialOrd)] #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(rename_all = "lowercase") )] -pub struct Head { +pub struct Head { #[cfg_attr(feature = "serde", serde(alias = "current_state"))] pub state: State, #[cfg_attr(feature = "serde", serde(alias = "current_symbol"))] @@ -22,31 +19,22 @@ pub struct Head { } impl Head { - pub fn new(State(state): State, symbol: S) -> Self { - Self { - state: State(state), - symbol, - } + pub fn new(state: State, symbol: S) -> Self { + Self { state, symbol } } /// Create a new instance of the [Head] using the given state and default symbol. - pub fn from_state(State(state): State) -> Self + pub fn from_state(state: State) -> Self where S: Default, { - Self { - state: State(state), - symbol: Default::default(), - } + Self::new(state, S::default()) } /// Create a new instance of the [Head] using the given symbol and default state. pub fn from_symbol(symbol: S) -> Self where Q: Default, { - Self { - state: Default::default(), - symbol, - } + Self::new(State::default(), symbol) } /// Create a new instance from a 2-tuple: ([state](State), symbol) pub fn from_tuple((state, symbol): (State, S)) -> Self { @@ -63,6 +51,22 @@ impl Head { pub fn with_symbol(self, symbol: S) -> Self { Self { symbol, ..self } } + /// Returns a reference to the current state + pub fn state(&self) -> State<&Q> { + self.state.to_ref() + } + /// Returns a mutable reference to the current [State] + pub fn state_mut(&mut self) -> State<&mut Q> { + self.state.to_mut() + } + /// Returns a reference to the current symbol + pub const fn symbol(&self) -> &S { + &self.symbol + } + /// Returns a mutable reference to the current symbol + pub fn symbol_mut(&mut self) -> &mut S { + &mut self.symbol + } /// Returns a reference to the current state and symbol returing a 2-tuple pub fn as_tuple(&self) -> (&State, &S) { (&self.state, &self.symbol) @@ -83,21 +87,21 @@ impl Head { pub fn set_symbol(&mut self, symbol: S) { self.symbol = symbol; } - /// Returns a reference to the current state - pub fn state(&self) -> State<&Q> { - self.state.to_ref() + /// Replaces the current state and symbol with the given state and symbol; returns the + /// previous instance of the head. + pub fn replace(&mut self, state: State, symbol: S) -> Self { + Head { + state: core::mem::replace(&mut self.state, state), + symbol: core::mem::replace(&mut self.symbol, symbol), + } } - /// Returns a mutable reference to the current [State] - pub fn state_mut(&mut self) -> State<&mut Q> { - self.state.to_mut() + /// Replaces the current state with the given state, returing the previous state + pub fn replace_state(&mut self, state: State) -> State { + core::mem::replace(&mut self.state, state) } - /// Returns a reference to the current symbol - pub const fn symbol(&self) -> &S { - &self.symbol - } - /// Returns a mutable reference to the current symbol - pub fn symbol_mut(&mut self) -> &mut S { - &mut self.symbol + /// Replaces the current symbol with the given symbol, returning the previous symbol + pub fn replace_symbol(&mut self, symbol: S) -> S { + core::mem::replace(&mut self.symbol, symbol) } /// Updates the current [State] and symbol pub fn update(&mut self, state: Option>, symbol: Option) { @@ -215,6 +219,73 @@ where } } +impl PartialEq> for Head +where + Q: PartialEq, +{ + fn eq(&self, state: &State) -> bool { + &self.state == state + } +} + +impl PartialEq> for State +where + Q: PartialEq, +{ + fn eq(&self, head: &Head) -> bool { + *self == *head.state + } +} + +impl<'a, Q, S> PartialEq> for State<&'a Q> +where + Q: PartialEq, +{ + fn eq(&self, head: &Head) -> bool { + *self == head.state() + } +} + +impl<'a, Q, S> PartialEq> for Head +where + Q: PartialEq, +{ + fn eq(&self, state: &State<&'a Q>) -> bool { + self.state() == *state + } +} + +impl PartialEq<(State, S)> for Head +where + Q: PartialEq, + S: PartialEq, +{ + fn eq(&self, (state, symbol): &(State, S)) -> bool { + &self.state == state && &self.symbol == symbol + } +} + +impl PartialEq<(Q, S)> for Head +where + State: PartialEq, + Q: PartialEq, + S: PartialEq, +{ + fn eq(&self, (state, symbol): &(Q, S)) -> bool { + &self.state == state && &self.symbol == symbol + } +} + +impl PartialEq> for (State, S) +where + Q: PartialEq, + S: PartialEq, +{ + fn eq(&self, head: &Head) -> bool { + head.state == self.0 && head.symbol == self.1 + } +} + impl From<(Q, S)> for Head { fn from((state, symbol): (Q, S)) -> Self { Self::new(State(state), symbol) diff --git a/core/src/types/mod.rs b/core/src/types/mod.rs index 8ebfe12..af66877 100644 --- a/core/src/types/mod.rs +++ b/core/src/types/mod.rs @@ -3,24 +3,24 @@ Contrib: FL03 */ #[doc(inline)] -pub use self::{head::Head, tail::Tail}; +pub use self::{direction::Direction, head::Head, tail::Tail}; +pub(crate) mod direction; pub(crate) mod head; pub(crate) mod tail; #[doc(hidden)] -pub mod cursor; +pub mod cell; +#[doc(hidden)] +pub mod snapshot; +#[doc(hidden)] +pub mod transition; pub(crate) mod prelude { + pub use super::direction::*; pub use super::head::Head; pub use super::tail::Tail; - pub use super::IndexedHead; - - #[allow(unused)] - pub(crate) use super::Idx; } -/// A type alias generally used to represent the position of a value within a collection. -pub(crate) type Idx = usize; -/// A type alias for a head which store an index as its symbol -pub type IndexedHead = Head; +/// A type alias for a [Result] with our custom error type: [`Error`](crate::Error) +pub type Result = core::result::Result; diff --git a/core/src/types/snapshot.rs b/core/src/types/snapshot.rs new file mode 100644 index 0000000..4e5fb90 --- /dev/null +++ b/core/src/types/snapshot.rs @@ -0,0 +1,25 @@ +/* + Appellation: snapshot + Contrib: FL03 +*/ +use crate::mem::RawMemory; +use crate::State; + +pub struct Snapshot<'a, Q, M> +where + M: RawMemory, +{ + pub state: State<&'a Q>, + pub symbol: *mut M::Elem, + pub tape: M, +} + +impl<'a, Q, A, M> Snapshot<'a, Q, M> +where + A: 'a, + M: RawMemory, +{ + pub fn state(&self) -> State<&'a Q> { + self.state + } +} diff --git a/core/src/types/tail.rs b/core/src/types/tail.rs index 308712a..8bbef67 100644 --- a/core/src/types/tail.rs +++ b/core/src/types/tail.rs @@ -14,7 +14,7 @@ use crate::{Direction, Head, State}; derive(serde::Deserialize, serde::Serialize), serde(rename_all = "lowercase") )] -pub struct Tail { +pub struct Tail { pub direction: Direction, #[cfg_attr(feature = "serde", serde(alias = "next_state"))] pub state: State, @@ -33,9 +33,25 @@ impl Tail { symbol, } } - - pub fn create() -> TailBuilder { - TailBuilder::new(Direction::Right) + /// Returns an instance of [TailBuilder] allowing developers to construct a [Tail] with + /// a fluent API + pub fn init() -> TailBuilder { + TailBuilder::new() + } + /// Configures the tail with a new direction + pub fn with_direction(self, direction: Direction) -> Self { + Self { direction, ..self } + } + /// Configures the tail with a new state + pub fn with_state(self, State(state): State) -> Self { + Self { + state: State(state), + ..self + } + } + /// Configures the tail with a new symbol + pub fn with_symbol(self, symbol: S) -> Self { + Self { symbol, ..self } } /// Returns the direction, state, and symbol as a 3-tuple pub fn as_tuple(&self) -> (Direction, &State, &S) { @@ -63,12 +79,18 @@ impl Tail { } /// Consumes the tail and returns a new instance of the [Head] pub fn into_head(self) -> Head { - super::Head::new(self.state, self.symbol) + Head { + state: self.state, + symbol: self.symbol, + } } /// Returns an instance of the [head](Head) where each element within /// the created instance is an immutable reference - pub fn to_head_ref<'a>(&'a self) -> Head<&'a Q, &'a S> { - super::Head::new(self.state.to_ref(), &self.symbol) + pub fn as_head(&self) -> Head<&Q, &S> { + Head { + state: self.state.to_ref(), + symbol: &self.symbol, + } } pub fn to_ref(&self) -> Tail<&'_ Q, &'_ S> { @@ -197,8 +219,22 @@ mod builder { symbol: Option, } + impl Default for TailBuilder { + fn default() -> Self { + Self::new() + } + } + impl TailBuilder { - pub fn new(direction: Direction) -> Self { + pub fn new() -> Self { + Self { + direction: Direction::Right, + state: None, + symbol: None, + } + } + + pub fn from_direction(direction: Direction) -> Self { Self { direction, state: None, diff --git a/core/src/types/transition.rs b/core/src/types/transition.rs new file mode 100644 index 0000000..ac682b3 --- /dev/null +++ b/core/src/types/transition.rs @@ -0,0 +1,167 @@ +/* + Appellation: transition + Contrib: FL03 +*/ +use super::{Direction, Head}; +use crate::State; + +/// [Transition] is a type representing the tail of a Turing machine; +/// +/// Formally, a tail is defined to be a 3-tuple (direction, state, symbol). Considering the +/// head of a machine is a 2-tuple (state, symbol), the tail simply extends the head with a +/// direction. +#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, Ord, PartialOrd)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(rename_all = "lowercase") +)] +pub struct Transition { + pub direction: Direction, + pub head: Head, +} + +impl Transition { + pub fn new(direction: Direction, state: State, symbol: S) -> Self { + Self { + direction, + head: Head::new(state, symbol), + } + } + + pub fn with_direction(self, direction: Direction) -> Self { + Self { direction, ..self } + } + + pub fn with_state(self, state: State) -> Self { + Self { + head: self.head.with_state(state), + ..self + } + } + + pub fn with_symbol(self, symbol: S) -> Self { + Self { + head: self.head.with_symbol(symbol), + ..self + } + } + + pub fn as_head_ref(&self) -> Head<&Q, &S> { + self.head.to_ref() + } + + pub fn as_mut_head(&mut self) -> Head<&mut Q, &mut S> { + self.head.to_mut() + } + + pub const fn head(&self) -> &Head { + &self.head + } + + pub fn head_mut(&mut self) -> &mut Head { + &mut self.head + } + + pub fn state(&self) -> State<&'_ Q> { + self.head.state() + } + + pub fn state_mut(&mut self) -> State<&'_ mut Q> { + self.head.state_mut() + } + + pub fn symbol(&self) -> &S { + self.head.symbol() + } + + pub fn symbol_mut(&mut self) -> &mut S { + self.head.symbol_mut() + } + + pub fn to_ref(&self) -> Transition<&'_ Q, &'_ S> { + Transition { + direction: self.direction, + head: self.head.to_ref(), + } + } + + pub fn to_mut(&mut self) -> Transition<&'_ mut Q, &'_ mut S> { + Transition { + direction: self.direction, + head: self.head.to_mut(), + } + } +} + +impl<'a, Q, S> Transition<&'a Q, &'a S> { + pub fn cloned(&self) -> Transition + where + Q: Clone, + S: Clone, + { + Transition { + direction: self.direction, + head: self.head.cloned(), + } + } + + pub fn copied(&self) -> Transition + where + Q: Copy, + S: Copy, + { + Transition { + direction: self.direction, + head: self.head.copied(), + } + } +} + +impl<'a, Q, S> Transition<&'a mut Q, &'a mut S> { + pub fn cloned(&self) -> Transition + where + Q: Clone, + S: Clone, + { + Transition { + direction: self.direction, + head: self.head.cloned(), + } + } + + pub fn copied(&self) -> Transition + where + Q: Copy, + S: Copy, + { + Transition { + direction: self.direction, + head: self.head.copied(), + } + } +} + +impl core::fmt::Debug for Transition +where + Q: core::fmt::Debug, + S: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("Transition") + .field(&self.direction) + .field(&self.head.state) + .field(&self.head.symbol) + .finish() + } +} + +impl core::fmt::Display for Transition +where + Q: core::fmt::Display, + S: core::fmt::Display, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{dir}({head})", dir = self.direction, head = self.head) + } +} diff --git a/core/tests/actor.rs b/core/tests/actor.rs index 8742ef4..79047b0 100644 --- a/core/tests/actor.rs +++ b/core/tests/actor.rs @@ -5,11 +5,9 @@ extern crate rstm_core as rstm; use rstm::actors::Actor; -use rstm::rules::{Program, Rule}; +use rstm::rules::{Rule, Ruleset}; use rstm::{ruleset, State}; -const INITIAL_STATE: State = State(0); - lazy_static::lazy_static! { static ref RULES: [Rule; 6] = ruleset![ (0, 0) -> Right(1, 1), @@ -20,19 +18,16 @@ lazy_static::lazy_static! { (-1, 1) -> Left(0, 1), ]; - static ref PROGRAM: Program = Program::from_iter(RULES.clone()).with_initial_state(INITIAL_STATE); } #[test] fn busy_beaver() { + let initial_state = State(0_isize); let input = [0_usize; 10]; - let program = Program::new() - .rules(RULES.clone()) - .initial_state(INITIAL_STATE) - .build(); + let program = Ruleset::from_iter(*RULES); - let actor = Actor::from_state(State(0)).with_tape(input); + let actor = Actor::from_state(initial_state).with_alpha(input); let mut rt = actor.execute(program); for _ in 0..10 { assert!(rt.next().is_some()); diff --git a/core/tests/rules.rs b/core/tests/rules.rs new file mode 100644 index 0000000..4fa7fe6 --- /dev/null +++ b/core/tests/rules.rs @@ -0,0 +1,33 @@ +/* + Appellation: rules + Contrib: FL03 +*/ +extern crate rstm_core as rstm; + +use rstm::rules::Ruleset; +use rstm::{ruleset, Direction, Head, State, Tail}; + +#[test] +fn ruleset() { + let rules = ruleset![ + (0, 0) -> Right(1, 1), + (0, 1) -> Left(-1, 0), + (1, 0) -> Right(1, 1), + (1, 1) -> Left(-1, 1), + (-1, 0) -> Right(0, 0), + (-1, 1) -> Left(0, 1), + ]; + // validate the number of rules within the ruleset + assert_eq!(rules.len(), 6); + // create a new program using the ruleset + let program = Ruleset::from_iter(rules); + // validate the number of rules within the program + assert_eq!(rules.len(), program.len()); + // create a new head for a rule within the program + let head = Head::new(State(0), 0); + // retrieve and validate the tail for the given head + assert_eq!( + program.get_by_head(&head), + Some(&Tail::new(Direction::Right, State(1), 1)) + ) +} diff --git a/core/tests/state.rs b/core/tests/state.rs new file mode 100644 index 0000000..0e69d8f --- /dev/null +++ b/core/tests/state.rs @@ -0,0 +1,94 @@ +/* + Appellation: state + Contrib: FL03 +*/ +extern crate rstm_core as rstm; + +use rstm::state::{Halt, State}; + +#[test] +fn state() { + // create a new instance of state + let state = State::from_value(0); + // validate the functional accessors; get and into_inner + assert_eq!(state.get_ref(), &0); + assert_eq!(state.get(), 0); + // create a new mutable instance of state + let mut state = State::from_value(0); + // replace the inner value with 1 + assert_eq!(state.replace(1), 0); + // verify the replacement + assert_eq!(*state.get_ref(), 1); + // set the inner value to 2 + state.set(2); + // verify the update + assert_eq!(*state.get_ref(), 2); + // reset the state to its default value + state.reset(); + // verify the reset + assert_eq!(*state.get_ref(), 0); + // swap + state.swap(&mut State::from_value(10)); + // verify the swap + assert_eq!(*state.get_ref(), 10); +} + +#[test] +fn halting() { + // create a new instance of state + let state = Halt::new(0); + // validate the functional accessors; get and into_inner + assert_eq!(state.get_ref(), &0); + assert_eq!(state.get(), 0); + // create a new mutable instance of state + let mut state = Halt::new(0); + // replace the inner value with 1 + assert_eq!(state.replace(1), 0); + // verify the replacement + assert_eq!(*state.get_ref(), 1); + // set the inner value to 2 + state.set(2); + // verify the update + assert_eq!(*state.get_ref(), 2); + // reset the state to its default value + state.reset(); + // verify the reset + assert_eq!(*state.get_ref(), 0); + // swap + state.swap(&mut Halt::new(10)); + // verify the swap + assert_eq!(*state.get_ref(), 10); +} + +#[test] +fn cast_state() { + // create a new instance of state with a value of 0_isize + let q = State(0_isize); + // cast the state to a usize + let r = unsafe { q.cast::() }; + // verify the casted state has an inner value of 0 + assert_eq!(r, 0_usize); +} + +#[test] +fn convert_state() { + // create a new instance of state with a value of 0 + let q = State(0_isize); + // convert the state into an Any + let r = q.into_any(); + // verify the converted state has an inner value of 0 + assert_eq!(*r.get_ref().downcast_ref::().unwrap(), 0_isize); +} + +#[test] +fn numstate() { + use num::{One, Zero}; + // create a new instance of state with a value of 1 + let one = State::::one(); + // create a new instance of state with a value of 0_f64 + let zero = State::::zero(); + // verify the state has an inner value of 1 + assert_eq!(one, 1); + // verify the state has an inner value of 0 + assert_eq!(zero, 0.0); +} diff --git a/core/tests/tape.rs b/core/tests/tape.rs new file mode 100644 index 0000000..c15293a --- /dev/null +++ b/core/tests/tape.rs @@ -0,0 +1,16 @@ +/* + Appellation: tape + Contrib: FL03 +*/ +extern crate rstm_core as rstm; + +use rstm::mem::tape::StdTape; + +#[test] +fn stdtape() { + let mut tape = StdTape::::new(); + tape.write(0); + tape.write(1); + + assert_eq!(tape.read(), Ok(&1)); +} diff --git a/rstm/Cargo.toml b/rstm/Cargo.toml index 18c8ec2..07be993 100644 --- a/rstm/Cargo.toml +++ b/rstm/Cargo.toml @@ -14,7 +14,7 @@ version.workspace = true [features] default = [ - "alloc", + "std", ] full = [ @@ -25,6 +25,7 @@ full = [ # ********* [FF] Dependencies ********* alloc = [ + "rstm-core/alloc", "serde?/alloc", ] @@ -39,11 +40,12 @@ tracing = [ ] # ********* [FF] Environments ********* -# std = [ -# "alloc", -# "serde?/std", -# "strum/std", -# ] +std = [ + "alloc", + "rstm-core/std", + "serde?/std", + "strum/std", +] [lib] bench = true @@ -53,28 +55,25 @@ test = true [[example]] name = "actor" -required-features = ["tracing"] - -[[example]] -name = "basic" -required-features = ["tracing"] +required-features = ["alloc", "tracing"] # ****************** Dependencies ****************** [dependencies] thiserror.workspace = true [dependencies.rstm-core] +default-features = false path = "../core" -version = "0.0.3" +version = "0.0.4" [dependencies.serde] -# default-features = false +default-features = false features = ["derive"] optional = true version = "1" [dependencies.strum] -# default-features = false +default-features = false features = ["derive"] version = "0.26" diff --git a/rstm/benches/default.rs b/rstm/benches/default.rs new file mode 100644 index 0000000..2169b51 --- /dev/null +++ b/rstm/benches/default.rs @@ -0,0 +1,81 @@ +/* + Appellation: default + Contrib: FL03 +*/ +#![feature(test)] +extern crate test; + +use test::Bencher; + +// bench: find the `BENCH_SIZE` first terms of the fibonacci sequence +static BENCH_SIZE: usize = 20; + +// function to benchmark must be annotated with `#[bench]` +#[bench] +fn recursive_fibonacci(b: &mut Bencher) { + // exact code to benchmark must be passed as a closure to the iter + // method of Bencher + b.iter(|| (0..BENCH_SIZE).map(fib::fibonacci).collect::>()) +} + +#[bench] +fn iterative_fibonacci(b: &mut Bencher) { + b.iter(|| fib::Fibonacci::seq().take(BENCH_SIZE).collect::>()) +} + +pub mod fib { + + /// [fibonacci] calculates the nth term of the fibonacci sequence using recursion; this + /// implementation is not recommended for large values of n and is simply used as a + /// benchmark. + pub fn fibonacci(n: usize) -> u32 { + if n < 2 { + 1 + } else { + fibonacci(n - 1) + fibonacci(n - 2) + } + } + + /// [Fibonacci] implements the fibonacci sequence as an [Iterator] + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct Fibonacci { + pub curr: u32, + next: u32, + } + + impl Fibonacci { + pub fn new(curr: u32, next: u32) -> Self { + Self { curr, next } + } + + pub fn seq() -> Self { + Self::new(1, 1) + } + + pub fn get(&self) -> u32 { + self.curr + } + + pub fn get_next(&self) -> u32 { + self.next + } + } + + impl Default for Fibonacci { + fn default() -> Self { + Self { curr: 1, next: 1 } + } + } + + impl Iterator for Fibonacci { + type Item = u32; + + fn next(&mut self) -> Option { + use core::mem::replace; + let next = self.get() + self.next; + let curr = replace(&mut self.next, next); + + Some(replace(&mut self.curr, curr)) + } + } +} diff --git a/rstm/examples/actor.rs b/rstm/examples/actor.rs index 5e53b2e..7acfc33 100644 --- a/rstm/examples/actor.rs +++ b/rstm/examples/actor.rs @@ -4,10 +4,10 @@ */ extern crate rstm; -use rstm::{ruleset, Actor, Program, State}; +use rstm::{ruleset, Actor, Ruleset, State}; fn main() -> Result<(), Box> { - _tracing(); + _tracing("debug"); // initialize the tape data let alpha = vec![0u8; 10]; // initialize the state of the machine @@ -21,32 +21,27 @@ fn main() -> Result<(), Box> { (-1, 0) -> Left(0, 0), (-1, 1) -> Left(1, 1), ]; - - let program = Program::new() - .initial_state(initial_state) - .rules(rules) - .build(); - + // create a new program from the ruleset + let program = Ruleset::from_iter(rules); // create a new instance of the machine - let tm = dbg!(Actor::from_state(initial_state).with_tape(alpha)); + let tm = dbg!(Actor::new(alpha, initial_state, 0)); tm.execute(program).run()?; Ok(()) } -fn _tracing() { +fn _tracing(level: &str) { + let level = match level { + "debug" => tracing::Level::DEBUG, + "error" => tracing::Level::ERROR, + "trace" => tracing::Level::TRACE, + "warn" => tracing::Level::WARN, + _ => tracing::Level::INFO, + }; let timer = tracing_subscriber::fmt::time::uptime(); tracing_subscriber::fmt() - .with_max_level(tracing::Level::INFO) + .with_max_level(level) .with_target(false) .with_timer(timer) .init(); tracing::info!("Welcome to rstm!"); } - -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub enum S3 { - A = 0, - B = 1, - C = -1, -} diff --git a/rstm/examples/basic.rs b/rstm/examples/basic.rs deleted file mode 100644 index 0872b87..0000000 --- a/rstm/examples/basic.rs +++ /dev/null @@ -1,48 +0,0 @@ -/* - Appellation: default - Contrib: FL03 -*/ -extern crate rstm; - -use rstm::{ruleset, Program, State, StdTape, Turm}; - -fn main() -> Result<(), Box> { - _tracing("debug"); - - // initialize the tape data - let alpha = [0_usize; 10]; - // define the rules for the machine - let rules = ruleset![ - (0, 0) -> Right(1, 0), - (0, 1) -> Right(-1, 1), - (1, 0) -> Right(0, 1), - (1, 1) -> Right(-1, 0), - (-1, 0) -> Left(0, 0), - (-1, 1) -> Left(1, 1), - ]; - // create a new program with the rules - let program = Program::new().initial_state(State(0)).rules(rules).build(); - // create a new tape with the data - let tape = StdTape::from_iter(alpha); - // create a new instance of the machine - let tm = Turm::new(program, tape); - tm.execute()?; - Ok(()) -} - -fn _tracing(level: &str) { - let level = match level { - "debug" => tracing::Level::DEBUG, - "error" => tracing::Level::ERROR, - "trace" => tracing::Level::TRACE, - "warn" => tracing::Level::WARN, - _ => tracing::Level::INFO, - }; - let timer = tracing_subscriber::fmt::time::uptime(); - tracing_subscriber::fmt() - .with_max_level(level) - .with_target(false) - .with_timer(timer) - .init(); - tracing::info!("Welcome to rstm!"); -} diff --git a/rstm/src/exp/mod.rs b/rstm/src/exp/mod.rs new file mode 100644 index 0000000..1c6e687 --- /dev/null +++ b/rstm/src/exp/mod.rs @@ -0,0 +1,13 @@ +/* + Appellation: exp + Contrib: FL03 +*/ +//! # Experimental (exp) +//! +//! +#![allow(unused)] + +#[doc(hidden)] +pub mod model; +#[doc(hidden)] +pub mod shift; diff --git a/rstm/src/turing.rs b/rstm/src/exp/model/base.rs similarity index 54% rename from rstm/src/turing.rs rename to rstm/src/exp/model/base.rs index 49deae4..4b11a43 100644 --- a/rstm/src/turing.rs +++ b/rstm/src/exp/model/base.rs @@ -2,65 +2,55 @@ Appellation: tm Contrib: FL03 */ +use crate::prelude::{Direction, Error, Head, Rule, StdTape, Symbolic, Tail}; +use crate::rules::Ruleset; +use crate::state::{halt::HaltState, RawState, State}; - -use crate::prelude::{Error, HaltState, Head, StdTape, Symbolic, Tail}; -use crate::rules::Program; -use crate::state::State; - -/// # Turing Machine ([Turm]) +/// # Turing Machine ([StdTm]) /// /// The Turing Machine is a mathematical model of computation that uses a set of rules to determine /// how a machine should manipulate a tape. The machine can read, write, and move linearly across the tape. /// Each pre-defined rule maps a head, consisting of a state and symbol, to a new state and symbol along with a direction. #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Turm { - pub(crate) program: Program, - pub(crate) state: HaltState, - pub(crate) tape: StdTape, +pub struct StdTM { + pub(crate) program: Ruleset, + pub(crate) state: State>, + pub(crate) tape: StdTape, } -impl Turm { - pub fn new(program: Program, tape: StdTape) -> Self +impl StdTM { + pub fn new(rules: impl IntoIterator>, tape: StdTape) -> Self where - Q: Clone + Default, - S: Default, + A: Default, { - let state = program.initial_state().cloned(); - Turm { - program, - state: HaltState::state(state), + StdTM { + program: Ruleset::from_iter(rules), + state: State::none(), tape, } } /// Returns an immutable reference to the [program](Program) - pub const fn program(&self) -> &Program { + pub const fn program(&self) -> &Ruleset { &self.program } /// Creates a new instance of a [head](Head) from references to the current state and symbol; - pub fn read(&self) -> Option> { + pub fn read(&self) -> Option, &'_ A>> { self.tape() .read() .ok() .map(|symbol| Head::new(self.state(), symbol)) } - /// Returns an instance of the [state](State) with an immutable - /// reference to the internal data - pub fn state(&self) -> State<&'_ Q> { - self.state.as_state() - } - /// Returns an instance of the [state](State) with a mutable - /// reference to the internal data - pub fn state_mut(&mut self) -> State<&'_ mut Q> { - self.state.as_mut_state() + + pub fn state(&self) -> State<&Option> { + self.state.to_ref() } /// Returns an immutable reference to the [tape](StdTape) - pub const fn tape(&self) -> &StdTape { + pub const fn tape(&self) -> &StdTape { &self.tape } /// Returns a mutable reference to the [tape](StdTape) - pub fn tape_mut(&mut self) -> &mut StdTape { + pub fn tape_mut(&mut self) -> &mut StdTape { &mut self.tape } /// Runs the program until the @@ -73,7 +63,7 @@ impl Turm { pub fn execute(mut self) -> Result<(), Error> where Q: Clone + PartialEq + 'static, - S: Symbolic, + A: Symbolic, { #[cfg(feature = "tracing")] tracing::trace!("Executing the program..."); @@ -95,42 +85,41 @@ impl Turm { } } + fn handle(&mut self, direction: Direction, state: State>, symbol: A) { + self.tape.update(direction, symbol); + self.state = state.into(); + } + #[cfg_attr( feature = "tracing", tracing::instrument(skip_all, name = "process", target = "turing") )] - fn process(&mut self) -> Option> + fn process(&mut self) -> Option> where Q: Clone + PartialEq, - S: Clone + PartialEq, + A: Clone + PartialEq, { #[cfg(feature = "tracing")] tracing::trace!("Processing the current instruction..."); - // Get the first instruction for the current head - if let Some(Tail { - direction, - state, - symbol, - }) = self.program.get_ref(self.read()?) - { - - // - self.tape.update(direction, symbol.clone()); - self.state = state.cloned().into(); - return Some(Head::new(state, symbol)); + let csymbol = self.tape.read().ok()?; + if let Some(cstate) = self.state().get() { + if let Some(Tail { + direction, + state, + symbol, + }) = self.program.get(State(cstate), csymbol) + { + // + self.tape.update(*direction, symbol.clone()); + self.state = state.clone().map(Some); + return Some(Head::new(State(state), symbol)); + } + unreachable!("No instruction found for the current head") + } else { + #[cfg(feature = "tracing")] + tracing::trace!("Head is halted, terminating execution..."); + return None; } - unreachable!("No instruction found for the current head") - } -} - -impl core::iter::Iterator for Turm -where - Q: Clone + PartialEq + 'static, - S: Clone + PartialEq, -{ - type Item = Head; - - fn next(&mut self) -> Option { - self.process().map(|i| i.cloned()) + } } diff --git a/rstm/src/exp/model/mod.rs b/rstm/src/exp/model/mod.rs new file mode 100644 index 0000000..8137aea --- /dev/null +++ b/rstm/src/exp/model/mod.rs @@ -0,0 +1,14 @@ +/* + Appellation: models + Contrib: FL03 +*/ +#[doc(inline)] +pub use self::{base::StdTM, tmh::TMH}; + +pub mod base; +pub mod tmh; + +pub(crate) mod prelude { + pub use super::base::StdTM; + pub use super::tmh::TMH; +} diff --git a/rstm/src/exp/model/tmh.rs b/rstm/src/exp/model/tmh.rs new file mode 100644 index 0000000..cb76610 --- /dev/null +++ b/rstm/src/exp/model/tmh.rs @@ -0,0 +1,106 @@ +/* + Appellation: engine + Contrib: FL03 +*/ +#![cfg(feature = "std")] +use crate::rules::Rule; +use crate::state::RawState; +use crate::{Direction, Error, Head, Ruleset, State}; + +use std::collections::hash_map::{self, HashMap}; + +pub struct TMH +where + Q: RawState, +{ + pub(crate) program: Ruleset, + pub(crate) scope: Head, // Head, + pub(crate) tape: HashMap, +} + +impl TMH +where + Q: RawState, +{ + pub fn new(initial_state: State) -> Self { + Self { + program: Ruleset::new(), + scope: Head { + state: initial_state, + symbol: 0, + }, + tape: HashMap::new(), + } + } + pub fn with_input(self, input: I) -> Self + where + I: IntoIterator, + { + Self { + tape: HashMap::from_iter(input.into_iter().enumerate().map(|(i, a)| (i as isize, a))), + ..self + } + } + pub fn with_program(self, program: Ruleset) -> Self { + Self { program, ..self } + } + + pub fn load(&mut self, rules: I) + where + I: IntoIterator>, + { + self.program.extend(rules); + } + + pub fn current_state(&self) -> State<&'_ Q> { + self.scope.state() + } + + pub fn position(&self) -> isize { + self.scope.symbol + } + + pub fn cell(&self) -> &Head { + &self.scope + } + + pub fn entry(&mut self) -> hash_map::Entry<'_, isize, A> { + self.tape.entry(self.position()) + } + + pub fn read(&self) -> Option> { + self.tape.get(&self.scope.symbol).map(|symbol| Head { + state: self.scope.state(), + symbol, + }) + } + + pub fn write(&mut self, data: A) -> Option { + self.tape.insert(self.position(), data) + } + /// Handle the given rule; writes the symbol onto the tape before updating the state of the + /// head and applying a single step in the direction specified. Returns the previous head. + fn handle(&mut self, direction: Direction, state: State, symbol: A) -> Head { + // write the symbol onto the tape + let _prev = self.write(symbol); + // replace the current head with the new head + self.scope.replace(state, self.position() + direction) + } + + pub fn process(&mut self) -> Result<(), Error> + where + A: crate::Symbolic, + Q: Clone + Eq + std::hash::Hash, + { + let Head { state, symbol } = match self.read() { + Some(head) => head, + None => return Err(Error::runtime_error("Engine::process")), + }; + + if let Some(rule) = self.program.get(state, symbol) { + let _prev = self.handle(rule.direction, rule.state.clone(), rule.symbol); + } + + Ok(()) + } +} diff --git a/rstm/src/exp/shift/direction.rs b/rstm/src/exp/shift/direction.rs new file mode 100644 index 0000000..8f5192c --- /dev/null +++ b/rstm/src/exp/shift/direction.rs @@ -0,0 +1,10 @@ +/* + Appellation: direction + Contrib: FL03 +*/ +use crate::prelude::Direction; + +pub struct LinearShift { + pub(crate) direction: Direction, + pub(crate) value: T, +} \ No newline at end of file diff --git a/rstm/src/exp/shift/mod.rs b/rstm/src/exp/shift/mod.rs new file mode 100644 index 0000000..0462f19 --- /dev/null +++ b/rstm/src/exp/shift/mod.rs @@ -0,0 +1,28 @@ +/* + Appellation: shift + Contrib: FL03 +*/ +#[doc(inline)] +pub use self::direction::*; + +pub(crate) mod direction; + +#[allow(unused_imports)] +pub(crate) mod prelude { + pub use super::direction::*; +} + +/// [`Shift`] describes a generalized shift operation; +/// w.r.t. Turing machines, Moore (1990) describes a shift operation as a _movement_ of the +/// tape head +pub trait Shift { + type Output; + + fn shift(&self, step: T) -> Self::Output; +} + +pub trait ApplyOnce where F: FnOnce(T) -> Self::Output { + type Output; + + fn apply(&self, f: F, args: T) -> F::Output; +} \ No newline at end of file diff --git a/rstm/src/lib.rs b/rstm/src/lib.rs index 04b9f08..80cee9c 100644 --- a/rstm/src/lib.rs +++ b/rstm/src/lib.rs @@ -7,27 +7,20 @@ //! `rstm` is a Rust library dedicated to the construction and execution of Turing Machines. //! The crate is designed to be flexible and easy to use while preserving the abstract nature //! of the models. -//! -//! Actors are one of the primary focuses of the library, designed to essentially mimic the -//! behavior of a smart-contract in reverse. Actors provide an _actionable_ or computable -//! surface for workloads to be executed on. -//! + +#![cfg_attr(not(feature = "std"), no_std)] #![crate_name = "rstm"] -// #![cfg_attr(not(feature = "std"), no_std)] +#![crate_type = "lib"] + #[cfg(feature = "alloc")] extern crate alloc; -pub use rstm_core::*; - #[doc(inline)] -pub use self::turing::Turm; - -#[macro_use] -pub(crate) mod macros {} +pub use rstm_core::*; -pub mod turing; +#[doc(hidden)] +pub mod exp; pub mod prelude { - pub use crate::turing::Turm; pub use rstm_core::prelude::*; }