diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7..c5b743a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,9 +1,10 @@ --- name: Bug report about: Create a report to help us improve -title: '' -labels: '' -assignees: '' +assignees: [ FL03 ] +labels: [ bug ] +projects: [ '@FL03/rstm:dev' ] +title: 'Bug: ' --- @@ -12,6 +13,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,15 +26,17 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] **Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7..43525e8 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,9 +1,10 @@ --- name: Feature request about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' +assignees: [ FL03 ] +labels: [ enhancement ] +projects: [ '@FL03/rstm:dev' ] +title: 'Feature Request: ' --- diff --git a/.github/ISSUE_TEMPLATE/issue.md b/.github/ISSUE_TEMPLATE/issue.md index 925cfed..66eb978 100644 --- a/.github/ISSUE_TEMPLATE/issue.md +++ b/.github/ISSUE_TEMPLATE/issue.md @@ -3,7 +3,7 @@ about: A generic issue template assignees: - FL03 labels: [] -projects: ['@FL03/concision:features'] +projects: ['@FL03/rstm:dev'] name: Generic Issue title: '' --- diff --git a/.github/ISSUE_TEMPLATE/proposal.md b/.github/ISSUE_TEMPLATE/proposal.md index 5b35200..37e764b 100644 --- a/.github/ISSUE_TEMPLATE/proposal.md +++ b/.github/ISSUE_TEMPLATE/proposal.md @@ -1,10 +1,9 @@ --- about: A formal proposal discussing any new features, changes, or improvements to the project. -assignees: - - FL03 -labels: ['proposal'] +assignees: [ FL03 ] +labels: [ 'proposal' ] name: Improvement Proposal -title: 'CNC-0000:' +title: 'Proposal: ' --- diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 83ba4e2..39d0006 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -1,9 +1,14 @@ name: clippy +concurrency: + cancel-in-progress: false + group: ${{ github.workflow }}-${{ github.ref }} + on: pull_request: branches: [ main ] release: + types: [ created ] repository_dispatch: types: [ clippy ] schedule: diff --git a/.github/workflows/crates.yml b/.github/workflows/crates.yml index 3673993..e9c761d 100644 --- a/.github/workflows/crates.yml +++ b/.github/workflows/crates.yml @@ -1,8 +1,8 @@ name: crates concurrency: - group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false + group: ${{ github.workflow }}-${{ github.ref }} env: BASENAME: ${{ github.event.repository.name }} @@ -16,7 +16,7 @@ on: workflow_dispatch: jobs: - independents: + base: env: CRATE_NAME: ${{ github.event.repository.name }}-${{ matrix.suffix }} name: Publish (${{ matrix.suffix }}) @@ -42,6 +42,7 @@ jobs: - run: cargo publish --all-features -v -p ${{ env.CRATE_NAME }} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} publish: name: Publish (${{ github.event.repository.name }}) + needs: base runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0ccfc7d..932548a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,8 +1,8 @@ name: rust -concurrency: +concurrency: cancel-in-progress: false - group: ${{ github.event.repository.name }}-rust + group: ${{ github.workflow }}-${{ github.ref }} env: CARGO_TERM_COLOR: always @@ -12,10 +12,9 @@ on: pull_request: branches: [ main ] push: - branches: [ main ] tags: [ v*.*.* ] release: - types: [ created ] + types: [ published ] repository_dispatch: types: [ rust ] schedule: @@ -53,9 +52,9 @@ jobs: cargo-${{ matrix.toolchain }}-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} cargo-${{ matrix.toolchain }}-${{ runner.os }}- cargo-${{ matrix.toolchain }}- - cargo- + - name: cargo test (workspace) --full + run: cargo test -v --workspace -F full - name: cargo (bench) if: matrix.toolchain == 'nightly' - run: cargo bench --features full -v --workspace - - name: cargo test (workspace)[full] - run: cargo test -v --workspace -F full + run: cargo bench -F full -v --workspace + diff --git a/Cargo.toml b/Cargo.toml index 868998c..40a9b7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,15 +23,15 @@ thiserror = "1" [workspace.package] authors = ["Joe McCain III ",] -categories = [ ] +categories = [ "mathematics", "science", "simulation" ] description = "This crate focuses on building concrete implementations for Turing Machines." edition = "2021" homepage = "https://github.com/FL03/rstm/wiki" -keywords = [ ] +keywords = [ "turing", "turing-machine", "utm" ] license = "Apache-2.0" readme = "README.md" repository = "https://github.com/FL03/rstm.git" -version = "0.0.2" +version = "0.0.3" [profile.dev] opt-level = 0 diff --git a/README.md b/README.md index 891d9ac..270d810 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,23 @@ # 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) -*** - -### _The library is currently in the early stages of development and is not yet ready for production use._ +[![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) -This library focuses on building concrete implementations for Turing Machines. - -## Features +*** +_**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. ## Getting Started -### Building from the source +### From the source Start by cloning the repository @@ -28,28 +27,90 @@ cd rstm ``` ```bash -cargo build --features full -r --workspace +cargo build --all-features --workspace +``` + +#### _Run an example_ + +```bash +cargo run -f F --example actor ``` ## Usage -### Example +### Creating a new ruleset + +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}.` + + +`ruleset!` is a macro that allows you to define a set of rules for the machine. The syntax is as follows: + +```rust + ruleset![ + (state, symbol) -> Direction(next_state, next_symbol), + ... + ] +``` + +The macro is hygenic, meaning developers will not need to import the `Direction` enum nor its variants in order to use the macro. + + + +#### Example: Building a ruleset for a three-state, two-symbol Turing machine + +```rust + use rstm::ruleset; + + // define the ruleset for the machine + let rules = ruleset![ + (0, 0) -> Right(1, 0), + (0, 1) -> Stay(-1, 1), + (1, 0) -> Left(0, 1), + (1, 1) -> Right(-1, 0), + (-1, 0) -> Right(0, 0), + (-1, 1) -> Right(1, 1), + ]; +``` + +### Examples ```rust extern crate rstm; - fn main() -> Result<(), Box> { + use rstm::{ruleset, Actor, Program, State}; + fn main() -> Result<(), Box> { + tracing_subscriber::fmt().with_target(false).init(); + + // initialize the tape data + let alpha = vec![0i8; 10]; + // initialize the state of the machine + let initial_state = State(0); + // define the ruleset 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), + ]; + + let program = Program::new() + .initial_state(initial_state) + .rules(rules) + .build(); + + // create a new instance of the machine + let tm = dbg!(Actor::from_state(initial_state).with_tape(alpha)); + tm.execute(program).run()?; Ok(()) } ``` ## Contributing -Pull requests are welcome. For major changes, please open an issue first -to discuss what you would like to change. - -Please make sure to update tests as appropriate. +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 diff --git a/SECURITY.md b/SECURITY.md index f26a990..1e786d2 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,6 +6,8 @@ Checkout the current and supported packages below | Package | Current | Supported | |---------|---------|-----------| -| rstm | 0.0.0 | <=0.0.0 | +| rstm | 0.0.3 | <=0.0.3 | ## Reporting a Vulnerability + +Any issues or vulnerabilities should be reported to the maintainer of the package via email or by creating an [issue](https://github.com/FL03/rstm/issues/new) on the repository. diff --git a/core/Cargo.toml b/core/Cargo.toml index 1cab80d..48eb54e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -13,21 +13,30 @@ repository.workspace = true version.workspace = true [features] -default = [] +default = [ + "alloc", +] full = [ "default", + "rand", "serde", "tracing", ] # ********* [FF] Dependencies ********* alloc = [ + "num/alloc", "serde?/alloc", ] +rand = [ + "num/rand", +] + serde = [ "dep:serde", + "num/serde", ] tracing = [ @@ -51,6 +60,10 @@ test = true thiserror.workspace = true paste.workspace = true +[dependencies.num] +# default-features = false +version = "0.4" + [dependencies.serde] # default-features = false features = ["derive"] diff --git a/core/src/actors/actor.rs b/core/src/actors/actor.rs new file mode 100644 index 0000000..e0f6cb9 --- /dev/null +++ b/core/src/actors/actor.rs @@ -0,0 +1,269 @@ +/* + 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}; + +/// An [Actor] describes a Turing machine with a moving head (TMH). +/// +/// [Actor]'s abstractly define actionable surfaces capable of executing a [Program]. +#[derive(Clone, Default, Eq, Hash, PartialEq, PartialOrd)] +pub struct Actor { + /// the input alphabet + pub(crate) alpha: Vec, + /// the head of the tape + pub(crate) head: Head, +} + +impl Actor { + pub fn new() -> ActorBuilder { + ActorBuilder::new() + } + /// 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 { + Self { + alpha: Vec::new(), + head: Head { + state: State(state), + symbol: 0, + }, + } + } + + pub fn with_tape(self, alpha: I) -> Self + where + I: IntoIterator, + { + Self { + alpha: Vec::from_iter(alpha), + ..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 head of the tape + pub const fn head(&self) -> &Head { + &self.head + } + /// Returns a mutable reference to the head of the tape + pub fn head_mut(&mut self) -> &mut Head { + &mut self.head + } + /// Returns an instance of the [Head] with an immutable reference to the state's inner + /// value + pub fn head_ref(&self) -> Head<&Q, usize> { + Head { + state: self.head.state.to_ref(), + symbol: 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() + } + /// 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() + } + /// 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 { + 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() + } + /// Checks if the tape is halted + pub fn is_halted(&self) -> bool + where + Q: 'static, + { + 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 + } + /// 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> { + #[cfg(feature = "tracing")] + tracing::trace!("Reading the tape..."); + self.alpha + .get(self.position()) + .map(|symbol| Head { + state: self.head.state(), + symbol, + }) + .ok_or(Error::index_out_of_bounds(self.position(), self.len())) + } + + /// Writes the given symbol to the tape + #[cfg_attr( + feature = "tracing", + tracing::instrument(skip_all, name = "write", target = "actor") + )] + pub fn write(&mut self, value: S) { + #[cfg(feature = "tracing")] + tracing::trace!("Writing to the tape..."); + let pos = self.position(); + + if pos == usize::MAX { + #[cfg(feature = "tracing")] + tracing::trace!("Prepending to the tape..."); + } else if pos >= self.len() { + #[cfg(feature = "tracing")] + tracing::trace!("Appending to the tape..."); + // append to the tape + self.alpha.push(value); + } else { + self.alpha[pos] = value; + } + } + /// Performs a single step of the Turing machine + #[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) { + #[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), + }; + } + + pub(crate) fn process(&mut self, rule: Tail) { + self.handle(rule.direction, rule.state, rule.symbol); + } +} + +impl core::fmt::Debug for Actor +where + S: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + for (i, c) in self.alpha.iter().enumerate() { + if i == self.position() { + write!(f, "[{c:?}]")?; + } else { + write!(f, "{c:?}")?; + } + } + Ok(()) + } +} + +impl core::fmt::Display for Actor +where + S: core::fmt::Display, +{ + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + for (i, c) in self.alpha.iter().enumerate() { + if i == self.position() { + write!(f, "[{c}]")?; + } else { + write!(f, "{c}")?; + } + } + 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 new file mode 100644 index 0000000..3207ed9 --- /dev/null +++ b/core/src/actors/exec.rs @@ -0,0 +1,111 @@ +/* + Appellation: exec + Contrib: FL03 +*/ +use super::Actor; +use crate::{Error, Head, Program, Symbolic}; + +/// +pub struct Executor { + pub(crate) actor: Actor, + pub(crate) program: Program, + /// the number of steps taken by the actor + pub(crate) steps: usize, +} + +impl Executor { + pub(crate) fn new(actor: Actor, program: Program) -> Self { + Self { + actor, + program, + steps: 0, + } + } + + pub fn from_actor(actor: Actor) -> Self + where + Q: Default, + { + Self { + actor, + program: Program { + initial_state: Default::default(), + rules: Vec::new(), + }, + steps: 0, + } + } + /// Load a program into the executor + pub fn load(self, program: Program) -> Self { + Executor { program, ..self } + } + + pub const fn actor(&self) -> &Actor { + &self.actor + } + /// Reads the current symbol at the head of the tape + pub fn read(&self) -> Result, Error> { + self.actor.read() + } + + #[cfg_attr( + feature = "tracing", + tracing::instrument(skip_all, name = "run", target = "actor") + )] + pub fn run(&mut self) -> Result<(), Error> + where + Q: Clone + PartialEq + 'static, + S: Symbolic, + { + #[cfg(feature = "tracing")] + tracing::info!("Running the program..."); + for _h in self { + continue; + } + Ok(()) + } +} + +impl Iterator for Executor +where + Q: Clone + PartialEq + 'static, + S: Symbolic, +{ + type Item = Head; + + #[cfg_attr( + feature = "tracing", + tracing::instrument(skip_all, name = "next", target = "actor") + )] + fn next(&mut self) -> Option { + // increment the number of steps taken + self.steps += 1; + #[cfg(feature = "tracing")] + tracing::info!("{tape:?}", tape = self.actor()); + // check if the actor is halted + if self.actor.is_halted() { + #[cfg(feature = "tracing")] + tracing::warn!("Detected a halted state; terminating the program..."); + return None; + } + // read the tape + let head = if let Ok(cur) = self.read() { + cur.cloned() + } else { + #[cfg(feature = "tracing")] + tracing::warn!("Unable to locate the value of the head..."); + Head::from_state(self.actor.state().cloned()) + }; + // execute the program + if let Some(tail) = self.program.get(&head).cloned() { + // process the instruction + self.actor.process(tail.clone()); + // return the head + return Some(tail.into_head()); + } else { + #[cfg(feature = "tracing")] + tracing::error!("No symbol found at {}", self.actor.position()); + panic!("No symbol found at {}", self.actor.position()); + } + } +} diff --git a/core/src/actors/mod.rs b/core/src/actors/mod.rs new file mode 100644 index 0000000..b8cff85 --- /dev/null +++ b/core/src/actors/mod.rs @@ -0,0 +1,25 @@ +/* + Appellation: actors + Contrib: FL03 +*/ +#[doc(inline)] +pub use self::{actor::Actor, exec::Executor}; + +pub(crate) mod actor; +pub(crate) mod exec; + +pub(crate) mod prelude { + pub use super::actor::Actor; + pub use super::exec::Executor; +} + +use crate::{rules::Program, Alphabet}; + +pub trait Model { + type Alpha: Alphabet; +} +pub trait Runtime { + fn load(&mut self, program: Program); + + fn run(&mut self); +} diff --git a/core/src/error.rs b/core/src/error.rs index 02c87a1..5a41b0d 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -2,35 +2,51 @@ Appellation: error Contrib: FL03 */ +#[derive( + Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, strum::VariantNames, thiserror::Error, +)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub enum StateError { + #[error("Invalid State: {0}")] + InvalidState(String), + #[error("State Not Found")] + StateNotFound, +} #[derive( Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, strum::VariantNames, thiserror::Error, )] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum Error { - #[error("[IndexError] Out of Bounds: {index} is out of bounds for a length of {len}")] + #[error("[Execution Error] {0}")] + ExecutionError(String), + #[error("[Index Error] Out of Bounds: {index} is out of bounds for a length of {len}")] IndexOutOfBounds { index: usize, len: usize }, - #[error("[StateError] Invalid State: {0}")] - InvalidState(String), - #[error("[StateError] State Not Found")] - StateNotFound, - #[error("Transformation error: {0}")] + #[error("[Runtime Error] {0}")] + RuntimeError(String), + #[error("[State Error] {0}")] + StateError(#[from] StateError), + #[error("[Transformation Error]: {0}")] TransformationError(String), - #[error("Unknown error: {0}")] + #[error("[Unknown Error] {0}")] Unknown(String), } impl Error { + pub fn execution_error(message: impl ToString) -> Self { + Error::ExecutionError(message.to_string()) + } + pub fn index_out_of_bounds(index: usize, len: usize) -> Self { Error::IndexOutOfBounds { index, len } } - pub fn invalid_state(err: impl ToString) -> Self { - Error::InvalidState(err.to_string()) + pub fn runtime_error(message: impl ToString) -> Self { + Error::RuntimeError(message.to_string()) } - pub fn state_not_found() -> Self { - Error::StateNotFound + pub fn state_error(err: StateError) -> Self { + Error::StateError(err) } pub fn transformation_error(message: impl ToString) -> Self { @@ -40,6 +56,14 @@ impl Error { pub fn unknown(message: impl ToString) -> Self { Error::Unknown(message.to_string()) } + + pub fn invalid_state(err: impl ToString) -> Self { + Error::StateError(StateError::InvalidState(err.to_string())) + } + + pub fn state_not_found() -> Self { + Error::StateError(StateError::StateNotFound) + } } impl From<&str> for Error { @@ -47,3 +71,9 @@ impl From<&str> for Error { Error::Unknown(err.to_string()) } } + +impl From for Error { + fn from(err: String) -> Self { + Error::Unknown(err) + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index b614f62..0051933 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -11,27 +11,44 @@ extern crate alloc; #[doc(inline)] pub use self::{ - error::Error, ops::prelude::*, rules::prelude::*, state::State, tape::StdTape, - traits::prelude::*, types::prelude::*, + actors::Actor, + error::Error, + rules::{Program, Rule}, + shift::Direction, + state::State, + tape::StdTape, + traits::*, + types::prelude::*, }; +#[allow(unused_macros)] #[macro_use] -pub(crate) mod macros; +pub(crate) mod macros { + #[macro_use] + pub mod fmt; + #[macro_use] + pub mod rules; + #[macro_use] + pub mod states; +} #[macro_use] pub(crate) mod seal; +#[doc(hidden)] +pub mod actors; pub mod error; -pub mod ops; 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::ops::prelude::*; 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::*; diff --git a/core/src/macros.rs b/core/src/macros.rs deleted file mode 100644 index 97f981e..0000000 --- a/core/src/macros.rs +++ /dev/null @@ -1,7 +0,0 @@ -/* - Appellation: macros - Contrib: FL03 -*/ - -#[macro_use] -pub mod states; diff --git a/core/src/macros/fmt.rs b/core/src/macros/fmt.rs new file mode 100644 index 0000000..a588e45 --- /dev/null +++ b/core/src/macros/fmt.rs @@ -0,0 +1,14 @@ +/* + Appellation: fmt + Contrib: FL03 +*/ + +macro_rules! unit_impl_fmt { + ($trait:ident::<$T:ty>($fmt:expr)) => { + impl ::core::fmt::$trait for $T { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, $e, $fmt(self)) + } + } + }; +} diff --git a/core/src/macros/rules.rs b/core/src/macros/rules.rs new file mode 100644 index 0000000..3e1a0d9 --- /dev/null +++ b/core/src/macros/rules.rs @@ -0,0 +1,49 @@ +/* + Appellation: rules + Contrib: FL03 +*/ + +/// [`ruleset!`] is a macro that simplifies the creation of a vector of [`Rules`](crate::Rule). +/// +/// ### Syntax +/// +/// ```ignore +/// ruleset![(state, symbol) -> direction(next_state, write_symbol), ...] +/// ``` +/// +/// ### Example +/// +/// The following example demonstrates the usage of the macro to create a ruleset using three +/// states `{-1, 0, 1}` and two symbols `{0, 1}`. +/// +/// ```rust +/// use rstm::ruleset; +/// +/// let rule = 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), +/// ]; +/// ``` +#[macro_export] +macro_rules! ruleset { + [@base ($state:expr, $symbol:literal) -> $direction:ident($next:expr, $write:literal)] => { + $crate::rules::Rule::new() + .state($crate::State($state)) + .symbol($symbol) + .write_symbol($write) + .direction($crate::Direction::$direction) + .next_state($crate::State($next)) + .build() + }; + [$( ($state:expr, $symbol:literal $(,)?) -> $direction:ident($next:expr, $write:literal $(,)?) ),* $(,)?] => { + [ + $( + $crate::ruleset![@base ($state, $symbol) -> $direction($next, $write)], + )* + ] + }; +} diff --git a/core/src/macros/states.rs b/core/src/macros/states.rs index fc539d9..ada53e4 100644 --- a/core/src/macros/states.rs +++ b/core/src/macros/states.rs @@ -2,3 +2,10 @@ Appellation: states Contrib: FL03 */ + +#[macro_export] +macro_rules! state { + ($state:expr) => { + $crate::State($state) + }; +} diff --git a/core/src/ops/mod.rs b/core/src/ops/mod.rs deleted file mode 100644 index d52e805..0000000 --- a/core/src/ops/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -/* - Appellation: ops - Contrib: FL03 -*/ -//! # Operations -//! -//! Overloadable operations used throughout the `rstm` crate. -#[doc(inline)] -pub use self::transform::*; - -#[doc(hidden)] -pub mod shift; -pub mod transform; - -pub(crate) mod prelude { - pub use super::transform::*; -} diff --git a/core/src/ops/shift.rs b/core/src/ops/shift.rs deleted file mode 100644 index e0e8f9e..0000000 --- a/core/src/ops/shift.rs +++ /dev/null @@ -1,10 +0,0 @@ -/* - Appellation: shift - Contrib: FL03 -*/ - -pub trait Shift { - type Output; - - fn shift(&self, tape: &T) -> Self::Output; -} diff --git a/core/src/ops/transform.rs b/core/src/ops/transform.rs deleted file mode 100644 index 4ec945c..0000000 --- a/core/src/ops/transform.rs +++ /dev/null @@ -1,16 +0,0 @@ -/* - Appellation: transform - Contrib: FL03 -*/ - -/// The [`Transform`] trait generically describes objects capable of applying some transformation. -/// From a "rustic" point of view, the trait simply provides an additional reference point for -/// the `map` method, which is a method that applies a function to a value and returns a new value. -/// -/// [`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`]. -pub trait Transform { - type Output; - - fn transform(&self, delta: T) -> Self::Output; -} diff --git a/core/src/rules/builders/instructions.rs b/core/src/rules/builders/instructions.rs new file mode 100644 index 0000000..a5182a9 --- /dev/null +++ b/core/src/rules/builders/instructions.rs @@ -0,0 +1,10 @@ +/* + 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 new file mode 100644 index 0000000..ecbfc07 --- /dev/null +++ b/core/src/rules/builders/program.rs @@ -0,0 +1,45 @@ +/* + 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 new file mode 100644 index 0000000..439fddc --- /dev/null +++ b/core/src/rules/builders/rule.rs @@ -0,0 +1,77 @@ +/* + Appellation: rule + Contrib: FL03 +*/ +use crate::rules::Rule; +use crate::{Direction, State}; + +#[derive(Default)] +pub struct RuleBuilder { + direction: Direction, + state: Option>, + symbol: Option, + next_state: Option>, + write_symbol: Option, +} + +impl RuleBuilder { + pub fn new() -> Self { + Self { + direction: Direction::Right, + state: None, + symbol: None, + next_state: None, + write_symbol: None, + } + } + + pub fn direction(self, direction: Direction) -> Self { + Self { direction, ..self } + } + + pub fn left(self) -> Self { + self.direction(Direction::Left) + } + + pub fn state(self, state: State) -> Self { + Self { + state: Some(state), + ..self + } + } + + pub fn symbol(self, symbol: S) -> Self { + Self { + symbol: Some(symbol), + ..self + } + } + + pub fn next_state(self, State(state): State) -> Self { + Self { + next_state: Some(State(state)), + ..self + } + } + + pub fn write_symbol(self, write_symbol: S) -> Self { + Self { + write_symbol: Some(write_symbol), + ..self + } + } + + pub fn build(self) -> Rule { + Rule { + head: crate::Head { + state: self.state.expect("state is required"), + symbol: self.symbol.expect("symbol is required"), + }, + tail: crate::Tail { + direction: self.direction, + state: self.next_state.expect("next_state is required"), + symbol: self.write_symbol.expect("write_symbol is required"), + }, + } + } +} diff --git a/core/src/rules/entry.rs b/core/src/rules/entry.rs deleted file mode 100644 index 92be88d..0000000 --- a/core/src/rules/entry.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* - Appellation: entry - Contrib: FL03 -*/ -pub use crate::rules::{Head, Tail}; - -pub struct Entry<'a, Q, S> { - key: &'a mut Head, - value: &'a mut Tail, -} - -impl<'a, Q, S> Entry<'a, Q, S> { - pub fn new(key: &'a mut Head, value: &'a mut Tail) -> Self { - Self { key, value } - } - - pub fn key(&self) -> &Head { - &self.key - } - - pub fn value(&self) -> &Tail { - &self.value - } -} diff --git a/core/src/rules/instruction.rs b/core/src/rules/instruction.rs deleted file mode 100644 index 6f0673c..0000000 --- a/core/src/rules/instruction.rs +++ /dev/null @@ -1,130 +0,0 @@ -/* - Appellation: instruction - Contrib: FL03 -*/ -pub use self::builder::InstructionBuilder; - -use crate::prelude::{Direction, Head, State, Tail}; - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Ord, PartialOrd)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Instruction { - pub head: Head, - pub tail: Tail, -} - -impl Instruction { - pub fn new() -> InstructionBuilder { - InstructionBuilder::new() - } - /// Returns a reference to the [head](Head) of the [instruction](Instruction) - pub const fn head(&self) -> &Head { - &self.head - } - /// Returns a mutable reference to the [head](Head) of the [instruction](Instruction) - pub fn head_mut(&mut self) -> &mut Head { - &mut self.head - } - /// Returns a reference to the [tail](Tail) of the [instruction](Instruction) - pub const fn tail(&self) -> &Tail { - &self.tail - } - /// Returns a mutable reference to the [tail](Tail) of the [instruction](Instruction) - pub fn tail_mut(&mut self) -> &mut Tail { - &mut self.tail - } - /// Returns the direction the [head](Head) is instructed to move - pub fn direction(&self) -> Direction { - self.tail().direction() - } - /// Returns the current [state](State) of the [head](Head) - pub fn state(&self) -> State<&'_ Q> { - self.head().get_state() - } - /// Returns the current symbol of the [head](Head) - pub const fn symbol(&self) -> &S { - self.head().symbol() - } - /// Returns the next [state](State) the agent is instructed to move to - pub fn next_state(&self) -> State<&'_ Q> { - self.tail().next_state() - } - /// Returns the symbol the [head](Head) is instructed to write - pub const fn write_symbol(&self) -> &S { - self.tail().write_symbol() - } -} - -mod builder { - use super::*; - - pub struct InstructionBuilder { - direction: Direction, - state: Option>, - symbol: Option, - next_state: Option>, - write_symbol: Option, - } - - impl InstructionBuilder { - pub fn new() -> Self { - Self { - direction: Direction::Right, - state: None, - symbol: None, - next_state: None, - write_symbol: None, - } - } - - pub fn direction(self, direction: Direction) -> Self { - Self { direction, ..self } - } - - pub fn left(self) -> Self { - self.direction(Direction::Left) - } - - pub fn state(self, state: State) -> Self { - Self { - state: Some(state), - ..self - } - } - - pub fn symbol(self, symbol: S) -> Self { - Self { - symbol: Some(symbol), - ..self - } - } - - pub fn next_state(self, State(state): State) -> Self { - Self { - next_state: Some(State(state)), - ..self - } - } - - pub fn write_symbol(self, write_symbol: S) -> Self { - Self { - write_symbol: Some(write_symbol), - ..self - } - } - - pub fn build(self) -> Instruction { - Instruction { - head: Head { - state: self.state.expect("state is required"), - symbol: self.symbol.expect("symbol is required"), - }, - tail: Tail { - direction: self.direction, - state: self.next_state.expect("next_state is required"), - symbol: self.write_symbol.expect("write_symbol is required"), - }, - } - } - } -} diff --git a/core/src/rules/mod.rs b/core/src/rules/mod.rs index 737ecbc..bdd22b8 100644 --- a/core/src/rules/mod.rs +++ b/core/src/rules/mod.rs @@ -4,41 +4,33 @@ */ #[doc(inline)] pub use self::{ - instruction::*, - parts::{Head, Tail}, - program::*, + builders::{ProgramBuilder, RuleBuilder}, + program::Program, + rule::Rule, }; -pub(crate) mod instruction; pub(crate) mod program; +pub(crate) mod rule; -#[doc(hidden)] -pub mod entry; +pub mod workload; -pub mod parts { - pub use self::{head::*, tail::*}; +#[doc(hidden)] +pub(crate) mod builders { + pub use self::{program::ProgramBuilder, rule::RuleBuilder}; - pub(crate) mod head; - pub(crate) mod tail; + mod program; + mod rule; } pub(crate) mod prelude { - pub use super::instruction::Instruction; - pub use super::parts::{Head, Tail}; pub use super::program::Program; + pub use super::rule::Rule; + pub use super::{Directive, Scope, Transition}; } -use crate::{Direction, State, Symbolic}; +use crate::{Direction, Head, State, Symbolic, Tail}; -pub trait Rule { - type Elem; - type State; -} - -pub trait Transition -where - S: Symbolic, -{ +pub trait Transition { fn direction(&self) -> Direction; fn current_state(&self) -> State<&'_ Q>; @@ -48,20 +40,45 @@ where fn symbol(&self) -> &S; fn write_symbol(&self) -> &S; + + fn head(&self) -> Head<&Q, &S> { + Head { + state: self.current_state(), + symbol: self.symbol(), + } + } + + fn tail(&self) -> Tail<&Q, &S> { + Tail { + direction: self.direction(), + state: self.next_state(), + symbol: self.write_symbol(), + } + } + + fn as_rule(&self) -> Rule<&Q, &S> { + Rule { + head: self.head(), + tail: self.tail(), + } + } } -pub trait Header { +/// The [`Scope`] trait is used to describe objects containing information or references to the +/// current state and symbol of a Turing machine. +pub trait Scope { fn current_state(&self) -> State<&'_ Q>; fn symbol(&self) -> &S; } +/// [`Directive`] is a trait describing the `tail` of a typical Turing machine; pub trait Directive { fn direction(&self) -> Direction; fn next_state(&self) -> State<&'_ Q>; - fn write_symbol(&self) -> &S; + fn value(&self) -> &S; } /* @@ -70,7 +87,7 @@ pub trait Directive { impl Transition for A where - A: Header + Directive, + A: Scope + Directive, S: Symbolic, { fn direction(&self) -> Direction { @@ -90,11 +107,11 @@ where } fn write_symbol(&self) -> &S { - self.write_symbol() + self.value() } } -impl Header for Instruction { +impl Scope for Rule { fn current_state(&self) -> State<&'_ Q> { self.head.state.to_ref() } @@ -104,23 +121,23 @@ impl Header for Instruction { } } -impl Directive for Instruction { +impl Directive for Rule { fn direction(&self) -> Direction { self.direction() } fn next_state(&self) -> State<&'_ Q> { - self.tail().next_state() + self.tail().state() } - fn write_symbol(&self) -> &S { + fn value(&self) -> &S { &self.write_symbol() } } -impl Header for crate::Head { +impl Scope for crate::Head { fn current_state(&self) -> State<&'_ Q> { - self.state().to_ref() + self.state() } fn symbol(&self) -> &S { @@ -134,15 +151,15 @@ impl Directive for crate::Tail { } fn next_state(&self) -> State<&'_ Q> { - self.next_state() + self.state() } - fn write_symbol(&self) -> &S { - &self.write_symbol() + fn value(&self) -> &S { + &self.symbol() } } -impl Header for (State, S) { +impl Scope for (State, S) { fn current_state(&self) -> State<&'_ Q> { self.0.to_ref() } @@ -161,7 +178,7 @@ impl Directive for (Direction, State, S) { self.1.to_ref() } - fn write_symbol(&self) -> &S { + fn value(&self) -> &S { &self.2 } } diff --git a/core/src/rules/parts/head.rs b/core/src/rules/parts/head.rs deleted file mode 100644 index 3fb272e..0000000 --- a/core/src/rules/parts/head.rs +++ /dev/null @@ -1,131 +0,0 @@ -/* - Appellation: head - Contrib: FL03 -*/ -use crate::state::State; - -/// The head of a turing machine generally speaks to the current state and symbol of the machine -/// w.r.t. the [tape](crate::Tape). -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Ord, PartialOrd)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Head { - #[cfg_attr( - feature = "serde", - serde( - flatten, - alias = "state", - alias = "current_state", - alias = "head_state" - ) - )] - pub state: State, - #[cfg_attr( - feature = "serde", - serde(flatten, alias = "symbol", alias = "current_symbol", alias = "value") - )] - pub symbol: S, -} - -impl Head { - pub fn new(State(state): State, symbol: S) -> Self { - Self { - state: State(state), - symbol, - } - } - /// Returns a reference to the current [state](State) and symbol returing a 2-tuple - pub fn as_tuple(&self) -> (&State, &S) { - (&self.state, &self.symbol) - } - /// Consumes the head and returns the current [state](State) and symbol as a 2-tuple - pub fn into_tuple(self) -> (State, S) { - (self.state, self.symbol) - } - /// Returns a mutable reference to the current [state](State) and symbol as a 2-tuple - pub fn as_mut_tuple(&mut self) -> (&mut State, &mut S) { - (&mut self.state, &mut self.symbol) - } - - /// Updates the current [state](State) - pub fn set_state(&mut self, state: State) { - self.state = state; - } - /// Updates the current symbol - pub fn set_symbol(&mut self, symbol: S) { - self.symbol = symbol; - } - /// Returns a reference to the current [state](State) - pub fn get_state(&self) -> State<&'_ Q> { - self.state.to_ref() - } - - pub const fn state(&self) -> &State { - &self.state - } - /// Returns a mutable reference to the current [state](State) - pub fn state_mut(&mut self) -> &mut State { - &mut self.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 - } - /// Updates the current [state](State) and symbol - pub fn update(&mut self, state: Option>, symbol: Option) { - if let Some(state) = state { - self.state = state; - } - if let Some(symbol) = symbol { - self.symbol = symbol; - } - } - - pub fn to_ref<'a>(&'a self) -> Head<&'a Q, &'a S> { - Head { - state: self.state.to_ref(), - symbol: &self.symbol, - } - } - - pub fn to_mut<'a>(&'a mut self) -> Head<&'a mut Q, &'a mut S> { - Head { - state: self.state.to_mut(), - symbol: &mut self.symbol, - } - } -} - -impl<'a, Q, S> Head<&'a Q, &'a S> { - pub fn cloned(&self) -> Head - where - Q: Clone, - S: Clone, - { - Head { - state: self.state.cloned(), - symbol: self.symbol.clone(), - } - } -} - -impl From<(Q, S)> for Head { - fn from((state, symbol): (Q, S)) -> Self { - Self::new(State(state), symbol) - } -} - -impl From<(State, S)> for Head { - fn from((state, symbol): (State, S)) -> Self { - Self::new(state, symbol) - } -} - -impl From> for (State, S) { - fn from(head: Head) -> Self { - head.into_tuple() - } -} diff --git a/core/src/rules/program.rs b/core/src/rules/program.rs index e3127e4..99fa028 100644 --- a/core/src/rules/program.rs +++ b/core/src/rules/program.rs @@ -2,34 +2,40 @@ Appellation: program Contrib: FL03 */ -use super::Instruction; +use super::{ProgramBuilder, Rule}; use crate::{Head, State, Tail}; use std::vec; -type RuleSet = Vec>; +type Ruleset = Vec>; + +// type Ruleset = std::collections::HashMap, Tail>; #[derive(Clone, Debug, Default)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Program { - pub initial_state: State, - pub(crate) ruleset: RuleSet, + pub(crate) initial_state: State, + pub(crate) rules: Ruleset, } impl Program { - pub fn new(State(initial_state): State) -> Self { - Self { - initial_state: State(initial_state), - ruleset: RuleSet::new(), - } + pub fn new() -> ProgramBuilder { + ProgramBuilder::new() } - pub fn from_iter(instructions: impl IntoIterator>) -> Self + pub fn from_iter(instructions: impl IntoIterator>) -> Self where Q: Default, { Self { initial_state: State::default(), - ruleset: RuleSet::from_iter(instructions), + rules: Ruleset::from_iter(instructions), + } + } + + pub fn from_state(State(initial_state): State) -> Self { + Self { + initial_state: State(initial_state), + rules: Ruleset::new(), } } /// @@ -40,12 +46,9 @@ impl Program { } } /// - pub fn with_instructions( - self, - instructions: impl IntoIterator>, - ) -> Self { + pub fn with_instructions(self, instructions: impl IntoIterator>) -> Self { Self { - ruleset: RuleSet::from_iter(instructions), + rules: Ruleset::from_iter(instructions), ..self } } @@ -54,76 +57,135 @@ impl Program { self.initial_state.to_ref() } /// Returns a reference to the instructions. - pub const fn instructions(&self) -> &RuleSet { - &self.ruleset + pub const fn instructions(&self) -> &Ruleset { + &self.rules } /// Returns a mutable reference to the instructions. - pub fn instructions_mut(&mut self) -> &mut RuleSet { - &mut self.ruleset + pub fn instructions_mut(&mut self) -> &mut Ruleset { + &mut self.rules } /// Returns an iterator over the elements. - pub fn iter(&self) -> core::slice::Iter> { - self.ruleset.iter() + pub fn iter(&self) -> core::slice::Iter> { + self.rules.iter() + } + /// Returns a mutable iterator over the elements. + pub fn iter_mut(&mut self) -> core::slice::IterMut> { + self.rules.iter_mut() } - /// Returns an owned reference to the element(s) specified by the index. - pub fn get(&self, idx: I) -> Option<&I::Output> + /// Returns a collection of tails for a given head. + pub fn get(&self, head: &Head) -> Option<&Tail> where - I: core::slice::SliceIndex<[Instruction]>, + Q: PartialEq, + S: PartialEq, { - self.ruleset.get(idx) + 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_head(&self, head: &Head) -> Vec> + /// Returns a mutable collection of tails for a given head. + pub fn get_mut(&mut self, head: &Head) -> Option<&mut Tail> where Q: PartialEq, S: PartialEq, { - self.iter() - .filter_map(|i| { - if i.head() == head { - Some(i.tail().to_ref()) - } else { - None - } - }) - .collect() - } - - pub fn find_head(&self, head: Head<&'_ Q, &'_ S>) -> Option<&Tail> + self.iter_mut().find_map(|i| { + if i.head() == head { + Some(i.tail_mut()) + } else { + None + } + }) + } + /// Returns a collection of tails for a given head. + pub fn get_ref(&self, head: Head<&'_ Q, &'_ S>) -> Option> where Q: PartialEq, S: PartialEq, { - self.iter().find(|i| i.head().to_ref() == head).map(|i| i.tail()) + self.iter().find_map(|i| { + if i.head_ref() == head { + Some(i.tail_ref()) + } else { + None + } + }) + } +} + +impl AsRef<[Rule]> for Program { + fn as_ref(&self) -> &[Rule] { + &self.rules } } -impl From> for Program { - fn from(instructions: RuleSet) -> Self { +impl AsMut<[Rule]> for Program { + fn as_mut(&mut self) -> &mut [Rule] { + &mut self.rules + } +} + +impl core::ops::Deref for Program { + type Target = [Rule]; + + fn deref(&self) -> &Self::Target { + &self.rules + } +} + +impl core::ops::DerefMut for Program { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.rules + } +} + +impl core::ops::Index> for Program +where + Q: PartialEq, + S: PartialEq, +{ + type Output = Tail; + + fn index(&self, index: Head) -> &Self::Output { + self.get(&index).unwrap() + } +} + +impl From> for Program { + fn from(instructions: Ruleset) -> Self { Self { initial_state: State::default(), - ruleset: instructions, + rules: instructions, } } } -impl FromIterator> for Program +impl Extend> for Program { + fn extend>>(&mut self, iter: I) { + self.rules.extend(iter) + } +} + +impl FromIterator> for Program where Q: Default, { - fn from_iter>>(iter: I) -> Self { + fn from_iter>>(iter: I) -> Self { Self { initial_state: State::default(), - ruleset: RuleSet::from_iter(iter), + rules: Ruleset::from_iter(iter), } } } impl IntoIterator for Program { - type Item = Instruction; + type Item = Rule; type IntoIter = vec::IntoIter; fn into_iter(self) -> Self::IntoIter { - self.ruleset.into_iter() + self.rules.into_iter() } } diff --git a/core/src/rules/rule.rs b/core/src/rules/rule.rs new file mode 100644 index 0000000..289a39b --- /dev/null +++ b/core/src/rules/rule.rs @@ -0,0 +1,166 @@ +/* + Appellation: instruction + Contrib: FL03 +*/ +use super::RuleBuilder; + +use crate::prelude::{Direction, 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, +} + +impl Rule { + pub fn new() -> RuleBuilder { + RuleBuilder::new() + } + /// Returns an immutable reference to the [Head] + pub const fn head(&self) -> &Head { + &self.head + } + /// Returns a mutable reference to the [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> { + self.head().to_ref() + } + /// Returns an immutable reference to the [Tail] of the [Instruction] + 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 { + &mut self.tail + } + /// Returns an instance of the [Tail] whose elements are immutable references + pub fn tail_ref(&self) -> Tail<&'_ Q, &'_ S> { + self.tail().to_ref() + } + /// Returns the direction of the shift + pub fn direction(&self) -> Direction { + self.tail().direction() + } + /// Returns the current [State] of the system + pub fn state(&self) -> State<&'_ Q> { + self.head().state() + } + /// Returns the symbol of the [Head] + pub const fn symbol(&self) -> &S { + self.head().symbol() + } + /// Returns the next [Head] of the system + pub fn next_head(&self) -> Head<&'_ Q, &'_ S> { + self.tail().to_head_ref() + } + /// Consumes the current object and returns the next [Head] of the system + pub fn into_next_head(self) -> Head { + self.tail.into_head() + } + /// Returns the next [State] of the system + pub fn next_state(&self) -> State<&'_ Q> { + self.tail().state() + } + /// Returns the value which for which the current object will be replaced with + pub const fn write_symbol(&self) -> &S { + 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) { + (self.head, self.tail) + } +} + +impl<'a, Q, S> Rule<&'a Q, &'a S> { + pub fn cloned(&self) -> Rule + where + Q: Clone, + S: Clone, + { + Rule { + head: self.head.cloned(), + tail: self.tail.cloned(), + } + } + + pub fn copied(&self) -> Rule + where + Q: Copy, + S: Copy, + { + Rule { + head: self.head.copied(), + tail: self.tail.copied(), + } + } +} + +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::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::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::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 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) + } +} + +impl From> for Rule { + fn from(builder: RuleBuilder) -> Self { + builder.build() + } +} diff --git a/core/src/rules/workload.rs b/core/src/rules/workload.rs new file mode 100644 index 0000000..a66d7d6 --- /dev/null +++ b/core/src/rules/workload.rs @@ -0,0 +1,270 @@ +/* + Appellation: workload + Contrib: FL03 +*/ +// #![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, + 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, + { + Self { + initial_state: State::default(), + rules: HashMap::new(), + } + } + + pub fn from_state(State(initial_state): State) -> Self { + Self { + initial_state: State(initial_state), + rules: HashMap::new(), + } + } + + pub fn from_iter(iter: I) -> Self + where + Q: Default, + I: IntoIterator, Tail)>, + { + Self { + initial_state: State::default(), + rules: HashMap::from_iter(iter), + } + } + + pub fn from_rules(iter: I) -> Self + where + Q: Default, + I: IntoIterator>, + { + let mut rules = HashMap::new(); + for rule in iter { + rules.insert(rule.head, rule.tail); + } + Self { + initial_state: State::default(), + rules, + } + } + + pub fn with_initial_state(self, State(state): State) -> Self { + Self { + initial_state: State(state), + ..self + } + } + + 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 { 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() + } + /// 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> { + 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> { + 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> { + 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()) + } + /// Inserts a new rule into the set of rules. + pub fn insert(&mut self, head: Head, tail: Tail) { + self.rules.insert(head, tail); + } + /// Inserts a new rule into the set of rules. + pub fn insert_rule(&mut self, rule: Rule) { + 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 { + 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 + 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, + { + self.or_insert(head, Tail::default()) + } + /// Removes a rule from the set of rules. + pub fn remove(&mut self, head: &Head) -> Option> { + self.rules.remove(head) + } +} + +impl core::iter::Extend<(Head, Tail)> for RuleSet +where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, +{ + fn extend(&mut self, iter: I) + where + I: IntoIterator, Tail)>, + { + for (head, tail) in iter { + self.insert(head, tail); + } + } +} + +impl core::iter::Extend> for RuleSet +where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, +{ + fn extend(&mut self, iter: I) + where + I: IntoIterator>, + { + for rule in iter { + self.insert(rule.head, rule.tail); + } + } +} + +impl core::ops::Index> for RuleSet +where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, +{ + type Output = Tail; + + fn index(&self, head: Head) -> &Self::Output { + &self.rules[&head] + } +} + +impl core::iter::FromIterator<(Head, Tail)> for RuleSet +where + Q: Default + Eq + core::hash::Hash, + S: Eq + core::hash::Hash, +{ + fn from_iter(iter: I) -> Self + where + I: IntoIterator, Tail)>, + { + Self::from_iter(iter) + } +} + +impl core::iter::FromIterator> for RuleSet +where + Q: Default + Eq + core::hash::Hash, + S: Eq + core::hash::Hash, +{ + fn from_iter(iter: I) -> Self + where + I: IntoIterator>, + { + Self::from_rules(iter) + } +} + +impl core::iter::IntoIterator for RuleSet +where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, +{ + type Item = (Head, Tail); + type IntoIter = std::collections::hash_map::IntoIter, Tail>; + + fn into_iter(self) -> Self::IntoIter { + self.rules.into_iter() + } +} + +impl<'a, Q, S> core::iter::IntoIterator for &'a RuleSet +where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, +{ + type Item = (&'a Head, &'a Tail); + type IntoIter = std::collections::hash_map::Iter<'a, Head, Tail>; + + fn into_iter(self) -> Self::IntoIter { + self.rules.iter() + } +} + +impl<'a, Q, S> core::iter::IntoIterator for &'a mut RuleSet +where + Q: Eq + core::hash::Hash, + S: Eq + core::hash::Hash, +{ + type Item = (&'a Head, &'a mut Tail); + type IntoIter = std::collections::hash_map::IterMut<'a, Head, Tail>; + + fn into_iter(self) -> Self::IntoIter { + self.rules.iter_mut() + } +} diff --git a/core/src/types/direction.rs b/core/src/shift/direction.rs similarity index 68% rename from core/src/types/direction.rs rename to core/src/shift/direction.rs index 03ca96e..60ace26 100644 --- a/core/src/types/direction.rs +++ b/core/src/shift/direction.rs @@ -2,14 +2,6 @@ Appellation: direction Contrib: FL03 */ -/// The [AsDirection] trait provides a convience method for converting a type into a [Direction]. -pub trait AsDirection { - fn as_direction(&self) -> Direction; -} -/// The [IntoDirection] trait provides a convience method for converting a type into a [Direction]. -pub trait IntoDirection { - fn into_direction(self) -> Direction; -} /// [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. @@ -30,9 +22,9 @@ pub trait IntoDirection { strum::VariantNames, )] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -#[repr(i8)] #[strum(serialize_all = "lowercase")] pub enum Direction { + /// Represents a single left shift #[cfg_attr( feature = "serde", serde( @@ -54,6 +46,7 @@ pub enum Direction { alias = "Right" ) )] + /// Represents a single right shift Right = 1, #[default] #[cfg_attr( @@ -66,6 +59,7 @@ pub enum Direction { alias = "Stay" ) )] + /// Represents no movement Stay = 0, } @@ -85,14 +79,6 @@ impl Direction { pub fn stay() -> Self { Self::Stay } - /// Applies the shift to the given position in the [direction](Direction) specified by the current instance. - pub fn apply(&self, cur: usize) -> usize { - match self { - Self::Left => cur - 1, - Self::Right => cur + 1, - Self::Stay => cur, - } - } /// Converts an [i8] value into a [`Direction`] by taking the modulus of the value. /// The function uses a modulator of 2 to determine the direction since there are /// only two actionable directions ([left](Direction::Left) and [right](Direction::Right)). @@ -103,7 +89,8 @@ impl Direction { _ => Self::Stay, } } - /// Converts a [char] value into a direction; matches the value to the corresponding [direction](Direction). + /// Converts a [char] value into a direction; matches the value to the corresponding + /// [direction](Direction). pub fn from_char(value: char) -> Self { match value { 'L' | 'l' => Self::Left, @@ -111,44 +98,93 @@ impl Direction { _ => Self::Stay, } } + /// Converts a [str] value into a [Direction] by matching the value to the corresponding + /// variant; defaults to [`Stay`](Direction::Stay) if the value does not match accepted + /// representations of neither [left](Direction::Left) nor [right](Direction::Right). + pub fn from_str(value: &str) -> Self { + match value { + "left" | "Left" | "LEFT" | "l" | "L" => Self::Left, + "right" | "Right" | "RIGHT" | "r" | "R" => Self::Right, + _ => Self::Stay, + } + } /// Returns a [char] representation of the [direction](Direction). + /// + /// ### standard [char] representation + /// + /// 'L' => [Direction::Left] + /// 'R' => [Direction::Right] + /// 'S' => [Direction::Stay] pub fn as_char(&self) -> char { - (*self).into_char() - } - - pub fn as_str(&self) -> &str { - (*self).as_ref() - } - /// Consumes the instance, returning a [char] representation of the [direction](Direction). - pub fn into_char(self) -> char { match self { Self::Left => 'L', Self::Right => 'R', Self::Stay => 'S', } } + /// Returns a [str] representation of the [direction](Direction). + pub fn as_str(&self) -> &str { + match self { + Self::Left => "left", + Self::Right => "right", + Self::Stay => "stay", + } + } + + /// Applies the shift to the given position in the [direction](Direction) specified by the + /// current instance. This is done using the [`wrapping_add_signed`](usize::wrapping_add_signed) + /// method. + pub fn apply_unsigned(self, cur: usize) -> usize { + cur.wrapping_add_signed(self as isize) + } } -impl AsDirection for T +impl core::ops::Add for Direction where - T: Clone + Into, + T: crate::Decrement + crate::Increment, { - fn as_direction(&self) -> Direction { - self.clone().into() + type Output = T; + + fn add(self, rhs: T) -> Self::Output { + match self { + Self::Left => rhs.decrement(), + Self::Right => rhs.increment(), + Self::Stay => rhs, + } } } -impl IntoDirection for T -where - T: Into, -{ - fn into_direction(self) -> Direction { - self.into() +impl core::ops::Add for isize { + type Output = isize; + + fn add(self, rhs: Direction) -> Self::Output { + self + rhs as isize + } +} + +impl core::ops::Add for usize { + type Output = usize; + + fn add(self, rhs: Direction) -> Self::Output { + self.wrapping_add_signed(rhs as isize) + } +} + +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 isize { + fn add_assign(&mut self, rhs: Direction) { + *self = core::ops::Add::add(*self, rhs); } } mod impl_from { use super::*; + use crate::shift::IntoDirection; macro_rules! impl_from_direction { ($($T:ident),*) => { diff --git a/core/src/shift/mod.rs b/core/src/shift/mod.rs new file mode 100644 index 0000000..7d7903b --- /dev/null +++ b/core/src/shift/mod.rs @@ -0,0 +1,45 @@ +/* + Appellation: shift + Contrib: FL03 +*/ +#[doc(inline)] +pub use self::direction::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; +} +/// The [IntoDirection] trait provides a convience method for converting a type into a [Direction]. +pub trait IntoDirection { + fn into_direction(self) -> Direction; +} + +/* + ************* Implementations ************* +*/ +impl AsDirection for T +where + T: Clone + Into, +{ + fn as_direction(&self) -> Direction { + self.clone().into() + } +} + +impl IntoDirection for T +where + T: Into, +{ + fn into_direction(self) -> Direction { + self.into() + } +} diff --git a/core/src/state/halt/mod.rs b/core/src/state/halt/mod.rs new file mode 100644 index 0000000..338b783 --- /dev/null +++ b/core/src/state/halt/mod.rs @@ -0,0 +1,23 @@ +/* + Appellation: halt + Contrib: FL03 +*/ +//! This module is responsible for defining the [Halt] state of a Turing machine. +//! +//! +#[doc(inline)] +pub use self::{state::Halt, wrap::HaltState}; + +pub(crate) mod state; +pub(crate) mod wrap; + +use crate::state::RawState; + +#[doc(hidden)] +pub trait Haltable { + const HALT: bool = true; + + type State: RawState; + + private!(); +} diff --git a/core/src/state/halt/state.rs b/core/src/state/halt/state.rs new file mode 100644 index 0000000..dd7162e --- /dev/null +++ b/core/src/state/halt/state.rs @@ -0,0 +1,99 @@ +/* + Appellation: halting + Contrib: FL03 +*/ +use crate::state::{halt::Haltable, State}; + +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct Halt(pub Q); + +impl Halt { + pub fn new(halt: Q) -> Self { + Self(halt) + } + + pub fn into_inner(self) -> Q { + self.0 + } + + pub const fn get(&self) -> &Q { + &self.0 + } + + pub fn get_mut(&mut self) -> &mut Q { + &mut self.0 + } + + 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. + 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. + pub fn as_state_mut(&mut self) -> State> { + State(Halt(&mut self.0)) + } + /// Wraps the halted state and returns a new [State] + pub fn into_state(self) -> State> { + State(self) + } + /// Returns an instance of [`Halt`] with an immutable reference to the inner value. + pub fn view(&self) -> Halt<&Q> { + Halt(&self.0) + } + /// Returns an instance of [`Halt`] with a mutable reference to the inner value. + pub fn view_mut(&mut self) -> Halt<&mut Q> { + Halt(&mut self.0) + } +} + +impl<'a, Q> Halt<&'a Q> { + pub fn cloned(&self) -> Halt + where + Q: Clone, + { + Halt(self.0.clone()) + } + + pub fn copied(&self) -> Halt + where + Q: Copy, + { + Halt(*self.0) + } +} + +impl<'a, Q> Halt<&'a mut Q> { + pub fn cloned(&self) -> Halt + where + Q: Clone, + { + Halt(self.0.clone()) + } + + pub fn copied(&self) -> Halt + where + Q: Copy, + { + Halt(*self.0) + } +} + +impl From> for Halt { + fn from(State(state): State) -> Self { + Self(state) + } +} + +impl Haltable for Halt { + type State = State; + seal!(); +} + +impl Haltable for State> { + type State = State; + seal!(); +} diff --git a/core/src/state/halt/wrap.rs b/core/src/state/halt/wrap.rs new file mode 100644 index 0000000..6355915 --- /dev/null +++ b/core/src/state/halt/wrap.rs @@ -0,0 +1,80 @@ +/* + Appellation: wrap + Contrib: FL03 +*/ +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)))] +#[strum_discriminants(name(HaltTag), derive(Hash, Ord, PartialOrd))] +pub enum HaltState { + Halt(Halt), + State(State), +} + +impl HaltState { + /// Creates a new instance of a [HaltState] with a halted state. + pub fn halt(Halt(state): Halt) -> Self { + Self::Halt(Halt(state)) + } + /// Creates a new instance of a [HaltState] with a continuing state. + pub fn state(state: State) -> Self { + Self::State(state) + } + + pub fn into_state(self) -> State { + match self { + Self::State(state) => state, + Self::Halt(halt) => State(halt.0), + } + } + + pub fn as_state(&self) -> State<&Q> { + State(self.get()) + } + + pub fn as_mut_state(&mut self) -> State<&mut Q> { + State(self.get_mut()) + } + + pub fn get(&self) -> &Q { + match self { + Self::State(inner) => inner.get(), + Self::Halt(inner) => inner.get(), + } + } + + pub fn get_mut(&mut self) -> &mut Q { + match self { + Self::State(inner) => inner.get_mut(), + Self::Halt(inner) => inner.get_mut(), + } + } + + pub fn set(&mut self, state: Q) { + match self { + Self::State(inner) => inner.set(state), + Self::Halt(inner) => inner.set(state), + } + } +} + +impl Default for HaltState { + fn default() -> Self { + Self::State(State::default()) + } +} + +impl From> for HaltState { + fn from(state: State) -> Self { + Self::State(state) + } +} + +impl From> for HaltState { + fn from(halt: Halt) -> Self { + Self::Halt(halt) + } +} \ No newline at end of file diff --git a/core/src/state/mod.rs b/core/src/state/mod.rs index c6e407d..8683728 100644 --- a/core/src/state/mod.rs +++ b/core/src/state/mod.rs @@ -3,39 +3,59 @@ Contrib: FL03 */ #[doc(inline)] -pub use self::{state::State, states::*}; +pub use self::{halt::*, state::State, states::*}; pub(crate) mod state; +pub mod halt; + pub(crate) mod states { #[doc(inline)] - pub use self::{binary::*, halting::*}; + pub use self::binary::*; pub(crate) mod binary; - pub(crate) mod halting; } pub(crate) mod prelude { - + pub use super::halt::*; pub use super::state::State; pub use super::states::*; + pub use super::AnyState; } +pub type AnyState = State>; + +pub trait BaseState: Eq + PartialOrd + core::hash::Hash {} + #[doc(hidden)] pub trait RawState { - type Data; + type Ctx; +} + +#[doc(hidden)] +pub trait Stated: RawState { + fn cloned(&self) -> Self + where + Q: Clone; + fn copied(&self) -> Self + where + Q: Copy; } #[doc(hidden)] pub trait Stateful { - type State: RawState; + type State: RawState; } /* ************* Implementations ************* */ +impl RawState for Halt { + type Ctx = Q; +} + impl RawState for State { - type Data = Q; + type Ctx = Q; } impl Stateful for State { diff --git a/core/src/state/state.rs b/core/src/state/state.rs index 66977b5..52693c6 100644 --- a/core/src/state/state.rs +++ b/core/src/state/state.rs @@ -8,49 +8,55 @@ use std::sync::Arc; #[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 { Self(state) } + /// Returns an immutable reference to the state. - pub fn as_ref(&self) -> &Q { + pub const fn get(&self) -> &Q { &self.0 } /// Returns a mutable reference to the state. - pub fn as_mut(&mut self) -> &mut Q { + pub fn get_mut(&mut self) -> &mut Q { &mut self.0 } - /// Consumes and returns the inner value of the [state](State). + /// Consumes and returns the inner value of the state. pub fn into_inner(self) -> Q { self.0 } - - pub fn into_owned(self) -> State - where - Q: Clone, - { - State(self.0.clone()) + /// Sets the state to a new value. + pub fn set(&mut self, state: Q) { + self.0 = state; } - /// Transforms the state into a shared reference. - pub fn into_shared(self) -> State> { - State(Arc::new(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())) + } + /// 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(State) -> R, + F: Fn(&Q) -> R, { - State(f(self)) + State(f(self.get())) } /// [State::map_mut] applies a [`FnMut`] closure to the state, returing the transformed state. pub fn map_mut(mut self, f: &mut F) -> State where F: FnMut(&mut Q) -> R, { - State(f(self.as_mut())) + State(f(self.get_mut())) } /// Maps the state to a new state using a closure that takes the state by value. pub fn map_once(self, f: F) -> State @@ -59,12 +65,9 @@ impl State { { State(f(self)) } - /// Returns a state with an owned inner value. - pub fn to_owned(&self) -> State - where - Q: Clone, - { - State(self.0.clone()) + /// 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)) } /// Returns a shared reference to the state. pub fn to_shared(&self) -> State> @@ -73,35 +76,46 @@ impl State { { State(Arc::new(self.0.clone())) } - /// Returns a state with a mutable reference to the inner value. - pub fn to_mut<'a>(&'a mut self) -> State<&'a mut Q> { - State(&mut self.0) - } /// Returns a state with an owned inner value. - pub fn to_ref<'a>(&'a self) -> State<&'a Q> { + pub fn to_ref(&self) -> State<&Q> { State(&self.0) } - + /// Returns a state with a mutable reference to the inner value. + pub fn to_mut(&mut self) -> State<&mut Q> { + State(&mut self.0) + } /// Returns the `name` of the generic inner type, `Q`. - pub fn inner_type_name(&self) -> &'static str { + pub fn get_inner_type_name(&self) -> &'static str { core::any::type_name::() } /// Returns the `type id` of the generic inner type, `Q`. - pub fn inner_type_id(&self) -> core::any::TypeId + pub fn get_inner_type_id(&self) -> core::any::TypeId where Q: 'static, { core::any::TypeId::of::() } + /// Wraps the inner value with a [Halt] state, returning a new instance of [State]. + pub fn halt(self) -> State> { + State(Halt(self.0)) + } + /// Returns `true` if the state is a [Halt] state. + pub fn is_halt(&self) -> bool + where + Q: 'static, + { + core::any::TypeId::of::() == core::any::TypeId::of::>>() + } } impl State> { - /// Returns a new instance of [State] with a [Halt] sub-state. - pub fn halt(Halt(inner): Halt) -> Self { + /// Creates a new instance of [State] from a [Halt] state. + pub fn halted(Halt(inner): Halt) -> Self { Self(Halt(inner)) } - pub fn is_halt(&self) -> bool { - true + + pub fn unhalt(self) -> State { + State(self.0.into_inner()) } } @@ -198,26 +212,91 @@ 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 core::cmp::PartialEq for State +impl PartialEq for State where - Q: core::cmp::PartialEq, + Q: PartialEq, { fn eq(&self, other: &Q) -> bool { - self.0 == *other + self.get().eq(other) } } -impl core::cmp::PartialOrd for State +impl PartialOrd for State where - Q: core::cmp::PartialOrd, + Q: PartialOrd, { fn partial_cmp(&self, other: &Q) -> Option { - self.0.partial_cmp(other) + self.get().partial_cmp(other) } } impl From for State { fn from(state: Q) -> Self { - Self(state) + 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 index c667768..939ced3 100644 --- a/core/src/state/states/binary.rs +++ b/core/src/state/states/binary.rs @@ -44,23 +44,23 @@ use crate::State; ) )] pub enum BinaryState { - Invalid(State), - Valid(State), + Invalid(I), + Valid(V), } impl BinaryState { - pub fn invalid(State(state): State) -> Self { - Self::Invalid(State(state)) + pub fn invalid(state: I) -> Self { + Self::Invalid(state) } - pub fn valid(State(state): State) -> Self { - Self::Valid(State(state)) + pub fn valid(state: V) -> Self { + Self::Valid(state) } pub fn invalidate(self, state: Q) -> BinaryState { match self { - Self::Invalid(_) => BinaryState::Invalid(State(state)), - Self::Valid(_) => BinaryState::Invalid(State(state)), + Self::Invalid(_) => BinaryState::Invalid(state), + Self::Valid(_) => BinaryState::Invalid(state), } } @@ -75,8 +75,8 @@ impl BinaryState { impl BinaryState { pub fn into_inner(self) -> State { match self { - Self::Invalid(q) => q, - Self::Valid(q) => q, + Self::Invalid(q) => State(q), + Self::Valid(q) => State(q), } } @@ -85,12 +85,6 @@ impl BinaryState { } } -impl Default for BinState { - fn default() -> Self { - Self::Invalid - } -} - impl AsRef for BinaryState { fn as_ref(&self) -> &Q { match self { @@ -121,9 +115,18 @@ impl core::borrow::BorrowMut for BinaryState { } } -impl Default for BinaryState { +impl Default for BinState { + fn default() -> Self { + Self::Invalid + } +} + +impl Default for BinaryState +where + I: Default, +{ fn default() -> Self { - Self::invalid(State::default()) + Self::invalid(::default()) } } diff --git a/core/src/state/states/halting.rs b/core/src/state/states/halting.rs deleted file mode 100644 index 537a3bd..0000000 --- a/core/src/state/states/halting.rs +++ /dev/null @@ -1,38 +0,0 @@ -/* - Appellation: halting - Contrib: FL03 -*/ -use crate::state::State; - -#[doc(hidden)] -pub trait Halter { - const HALT: bool = true; - - private!(); -} -#[doc(hidden)] -pub enum Halting { - Continue(T), - Halt(T), -} - -pub struct Halt(pub T); - -impl Halt { - pub fn new(halt: T) -> Self { - Self(halt) - } - pub fn into_inner(self) -> T { - self.0 - } - pub fn as_ref(&self) -> &T { - &self.0 - } - pub fn as_mut(&mut self) -> &mut T { - &mut self.0 - } -} - -impl Halter for State> { - seal!(); -} diff --git a/core/src/state/transition.rs b/core/src/state/transition.rs deleted file mode 100644 index a43e9e0..0000000 --- a/core/src/state/transition.rs +++ /dev/null @@ -1,9 +0,0 @@ -/* - Appellation: transition - Contrib: FL03 -*/ - -pub struct Entry { - key: Head, - value: Tail, -} \ No newline at end of file diff --git a/core/src/tape/entry.rs b/core/src/tape/entry.rs deleted file mode 100644 index 14c4474..0000000 --- a/core/src/tape/entry.rs +++ /dev/null @@ -1,29 +0,0 @@ -/* - Appellation: entry - Contrib: FL03 -*/ -#![allow(unused)] -use crate::Instruction; - -pub struct Entry<'a, Q, S> { - key: &'a mut usize, - value: &'a mut Instruction, -} - -impl<'a, Q, S> Entry<'a, Q, S> { - pub fn new(key: &'a mut usize, value: &'a mut Instruction) -> Self { - Self { key, value } - } - - pub fn key(&self) -> &usize { - &self.key - } - - pub fn set_key(&mut self, key: usize) { - *self.key = key; - } - - pub fn set_value(&mut self, value: Instruction) { - *self.value = value; - } -} diff --git a/core/src/tape/hash_tape.rs b/core/src/tape/hash_tape.rs new file mode 100644 index 0000000..47d302d --- /dev/null +++ b/core/src/tape/hash_tape.rs @@ -0,0 +1,104 @@ +/* + 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/iter.rs b/core/src/tape/iter.rs deleted file mode 100644 index ebeb3cc..0000000 --- a/core/src/tape/iter.rs +++ /dev/null @@ -1,4 +0,0 @@ -/* - Appellation: iter - Contrib: FL03 -*/ diff --git a/core/src/tape/mod.rs b/core/src/tape/mod.rs index 56489c9..3f52730 100644 --- a/core/src/tape/mod.rs +++ b/core/src/tape/mod.rs @@ -12,22 +12,170 @@ pub use self::tape::StdTape; pub(crate) mod tape; #[doc(hidden)] -pub mod entry; -#[doc(hidden)] -pub mod iter; -#[doc(hidden)] -pub mod slider; +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)] -pub trait Tape {} +/// [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/tape/slider.rs b/core/src/tape/slider.rs deleted file mode 100644 index 341e7eb..0000000 --- a/core/src/tape/slider.rs +++ /dev/null @@ -1,44 +0,0 @@ -/* - Appellation: slider - Contrib: FL03 -*/ -#![allow(dead_code)] -use crate::State; - -/// -/// -/// In the paper, ["On the topological dynamics of Turing Machines"](https://pdf.sciencedirectassets.com/271538/1-s2.0-S0304397500X00527/1-s2.0-S0304397596000254/main.pdf?X-Amz-Security-Token=IQoJb3JpZ2luX2VjEC4aCXVzLWVhc3QtMSJHMEUCIAs%2FIGbrXGO61g5aPm39Ib9mhu0rk0voDj%2FUKAD%2B0EmVAiEAtagmY%2FVIBX8CvMgEmXsSzaOck0zIZmmoLxZ5eicB7nwqsgUIFxAFGgwwNTkwMDM1NDY4NjUiDDYtSMTlhppTdI2XOiqPBQ0lXDEHbefl0B8yi5yFePwQGNd18aDmyxP8Gglg%2FY5BIAPdXUNRIoe50cEbiKFm8HdtxVoszJGOBqN71165Tulos2iAdYmhGkzcqMJMikG%2FpAfZu1x3uo3frEbNtIW2J%2FSYo0vrK2OXh3LXb4VQkoZpceVDrG26ZgZWIuUXuSomzBkxy%2BfqvaOeqNrKqN22mxQe59mJYQCgaDP0ev1zltb8ULFHKHv35%2FX98bXTIXXBp7IXMjHdXddO9jhHONGa6HbGazEBhpxy1xs3mADH1ErJ0JlWG0GZwrkz9VM9ap%2Bwnq9niYgYd1adWqeuep6POgA0SZtgAFFFGLtGeHTRx%2B8I%2BSSzzYXK4rrNjnsdJXMkK9fXPOsTZPbtafM5IVBCtCFIClrRx9AKe%2FpDGNGJ9um2Teh%2FgZdamXl0dLCHBmUxxXXf5cH7QBPV1YzOO%2FdsFxhvrST%2FW7BWDEeNV89UVAjJjRXD4gT64B1aujgX55UCgYRJKpi8r8Z7scMdywDUA8lTFxG8ckFy8VSJXP4XIUo76TdmRao2MiDo6a0S3QUvlXp9j01mB%2FLRF0%2Bj7HPOH6PI23YdvMI1U%2BnwYu9FgyfFt8gDO6a3JYDUYmco5K13YXn0BiJtO4bd8D9q2WyCYoHzog4fDPKLArmrEqbBXqcJ5E6SEz7OkQuVIm3eFmSMsU%2BmbXx8di%2FRWaCVpwht3okeVnsci%2F2IMBwCiZnoByJ9a3KD9xavV81n6h%2BAL8YD2tyR%2BqaZdgi%2Fx8eMS8H14cLJB6o8ZpiGmthUXT66emKAhZfhnTweLEuEavsZcMIMOuI%2B9FEC2dfeUWjdtm0VnrufqEID2%2BowOXodj65RLAiD2wKTLYyuVVApBRW%2FFw0wrp%2BZtQY6sQEvfJW%2BnFXtKiHbJusaao0u5Y3T6RDPxNbcZMt1A%2FkN6icm1%2BP32LwAQ2DLIxmT6SFpc1SxogGCfvggZtF%2BxERxIr9B3HpMNnfvvEh9NvD6NQQAqbINxzEoKIMXksRuc5wrnry1KmA51CtmfJ2TbJOlZ1KWI%2FIJW1gcoU0omdhntl3162JQQZFd9jIKQ07K2ojI8FXtcs3tAJQho5g4eM%2F7bCqvDb1Sgn0B77vPL8LEdKI%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240728T144819Z&X-Amz-SignedHeaders=host&X-Amz-Expires=300&X-Amz-Credential=ASIAQ3PHCVTY2B4ENHUE%2F20240728%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=35f40e1e6e1411dc3690307138e19043edef6f667f5ac5bbf191e0178217abc5&hash=0864d2d44896ab80e9b8766f9dd8b8d7516f124c8f19c29db323f504115bb2ba&host=68042c943591013ac2b2430a89b270f6af2c76d8dfd086a07176afe7c76c2c61&pii=S0304397596000254&tid=spdf-68cb7082-4608-4105-848d-6c23a206e008&sid=920ea93d24f32844d618d475ca675f16e0cbgxrqa&type=client&tsoh=d3d3LnNjaWVuY2VkaXJlY3QuY29t&ua=17155c0606530e0553&rr=8aa5ab6279d016b8&cc=us) -/// the authors define the state of a Turing machine to be its configuration. This -/// configuration consists of the inner state of the head, the symbol it is reading, -/// and the contents of the tape. -pub struct Slider { - pub(crate) state: State, // inner state of head - pub(crate) symbol: usize, // index of symbol - pub(crate) tape: Vec, // tape -} - -impl Slider { - pub fn new(State(state): State, tape: impl IntoIterator) -> Self { - Self { - state: State(state), - symbol: 0, - tape: tape.into_iter().collect(), - } - } - - pub fn state(&self) -> State<&Q> { - self.state.to_ref() - } - - pub fn symbol(&self) -> usize { - self.symbol - } - - pub fn read(&self) -> &S { - &self.tape[self.symbol] - } - - pub fn write(&mut self, symbol: S) { - self.tape[self.symbol] = symbol; - } -} diff --git a/core/src/tape/tape.rs b/core/src/tape/tape.rs index d9af628..2bf1c58 100644 --- a/core/src/tape/tape.rs +++ b/core/src/tape/tape.rs @@ -2,26 +2,26 @@ Appellation: tape Contrib: FL03 */ -use crate::{Direction, Error, Head, State, Tail}; +use crate::{Direction, Error}; use core::cell::Cell; -/// [StdTape] is an implementation of the traditional tape described by Turing machines. +#[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. /// -/// The tape, often thought of as the memory of the machine, is a one-dimensional array -/// of symbols in-which the tape head can read and write symbols. Furthermore, the tape -/// is infinite in both directions, meaning that the tape head can move left or right. -/// While this setup is largely hypothetical, it is a useful abstraction for understanding -/// the capabilities of Turing machines. /// -/// Here, the [StdTape] employs the use of a [Vec] to store symbols while leveraging a +/// 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 /// stores the number of steps or operations taken by the tape head in a [Cell]. /// This is done to quantify the impact of operations whose directions are defined to /// be [Direction::Stay]. Moving left and right within a linear space speaks directly /// to a translation or shift in space, however, staying in place does not result in /// any movement, shift, or translation within space. That being said, staying still -/// is an operation that does result in some change in-time. -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +/// is an operation that does result in some change in-time. +#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct StdTape { cursor: usize, @@ -73,105 +73,78 @@ impl StdTape { pub fn clear(&mut self) { self.store.clear(); } - /// Returns the number of elements in the tape. - pub fn len(&self) -> usize { - self.store.len() + /// Given an index, return a reference to the symbol at that index; + /// panics if the index is out of bounds. + pub fn get(&self, idx: I) -> Option<&I::Output> + where + I: core::slice::SliceIndex<[S]>, + { + self.store.get(idx) } /// Checks if the tape is empty; returns `true` if the tape is empty, /// `false` otherwise. pub fn is_empty(&self) -> bool { self.store.is_empty() } - + /// Returns an immutable iterator over the symbols stored on the tape. pub fn iter(&self) -> core::slice::Iter { self.store.iter() } - + /// Returns a mutable iterator over the symbols stored on the tape. + pub fn iter_mut(&mut self) -> core::slice::IterMut { + self.store.iter_mut() + } + /// Returns the number of elements in the tape. + pub fn len(&self) -> usize { + self.store.len() + } + /// Removes and returns the last element of the tape, or `None` if it is empty. + pub fn pop(&mut self) -> Option { + self.store.pop() + } + /// Appends the given element to the back of the collection. + pub fn push(&mut self, symbol: S) { + self.store.push(symbol); + } + /// Returns the number of steps or operations taken by the tape head; + /// this provides a measure of the impact of operations whose directions are defined to be + /// [Stay](Direction::Stay). pub fn ticks(&self) -> usize { self.ticks.get() } - - pub fn to_string(&self) -> String - where - S: core::fmt::Display, - { - format!("step ({}): {}", self.ticks(), self) - } - /// Returns the current position of the tape head; pub fn position(&self) -> usize { self.cursor } - - /// Returns an owned reference to the current symbol on the tape + /// 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())) } - /// - pub fn write(&mut self, direction: Direction, symbol: S) { - self.write_symbol(symbol); - self.shift(direction); - self.on_update(); - } - - /// - fn write_symbol(&mut self, symbol: S) { - if self.cursor < self.store.len() { - self.store[self.cursor] = symbol; - } else { + /// 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 { + self.store.insert(0, symbol); + } else if self.cursor == self.store.len() { self.store.push(symbol); + } else { + self.store[self.cursor] = symbol; } } - fn shift(&mut self, direction: Direction) -> usize { + pub fn update(&mut self, direction: Direction, symbol: S) { self.on_update(); - self.cursor = direction.apply(self.cursor); - self.position() - } - - pub fn update(self, direction: Direction, state: State, symbol: S) -> (Self, Head) - where - S: Clone, - { - let head = Head::new(state, symbol.clone()); - let mut tape = self; - tape.write(direction, symbol); - tape.shift(direction); - (tape, head) - } - - pub fn update_inplace(&mut self, tail: Tail) -> State { - let Tail { - direction, - state, - symbol, - } = tail; - - self.write(direction, symbol); + self.write(symbol); self.shift(direction); - state } fn on_update(&self) { - self.ticks.set(self.ticks.get() + 1); + self.ticks.set(self.ticks() + 1); } - /// Given an index, return a reference to the symbol at that index; - /// panics if the index is out of bounds. - pub fn get(&self, idx: I) -> Option<&I::Output> - where - I: core::slice::SliceIndex<[S]>, - { - self.store.get(idx) - } - /// Removes and returns the last element of the tape, or `None` if it is empty. - pub fn pop(&mut self) -> Option { - self.store.pop() - } - /// Appends the given element to the back of the collection. - pub fn push(&mut self, symbol: S) { - self.store.push(symbol); + fn shift(&mut self, direction: Direction) -> usize { + self.cursor = direction.apply_unsigned(self.cursor) % self.store.len(); + self.position() } } @@ -223,6 +196,21 @@ impl core::ops::DerefMut for StdTape { } } +impl core::fmt::Debug for StdTape +where + S: core::fmt::Debug, +{ + 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:?}]")?, + _ => write!(f, "{c:?}")?, + } + } + Ok(()) + } +} + impl core::fmt::Display for StdTape where S: core::fmt::Display, diff --git a/core/src/traits/actor.rs b/core/src/traits/actor.rs deleted file mode 100644 index 879b201..0000000 --- a/core/src/traits/actor.rs +++ /dev/null @@ -1,18 +0,0 @@ -/* - Appellation: fsm - Contrib: FL03 -*/ - -pub trait Automata { - type Rule; - type State; - type Symbol; - - fn current_state(&self) -> Self::State; -} - -pub enum Step { - Left(T), - Right(T), - Stay(T), -} diff --git a/core/src/traits/cspace.rs b/core/src/traits/cspace.rs new file mode 100644 index 0000000..c78b063 --- /dev/null +++ b/core/src/traits/cspace.rs @@ -0,0 +1,20 @@ +/* + 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 new file mode 100644 index 0000000..7a9e427 --- /dev/null +++ b/core/src/traits/execute.rs @@ -0,0 +1,10 @@ +/* + Appellation: execute + Contrib: FL03 +*/ + +pub trait Execute { + type Output; + + fn execute(&self) -> Result; +} diff --git a/core/src/traits/increment.rs b/core/src/traits/increment.rs new file mode 100644 index 0000000..c3e0579 --- /dev/null +++ b/core/src/traits/increment.rs @@ -0,0 +1,73 @@ +/* + Appellation: increment + Contrib: FL03 +*/ + +/// [Decrement] is a trait that provides a common interface for decrementing values; i.e., +/// subtracting one from a value. +pub trait Decrement { + fn decrement(self) -> Self; +} + +/// [DecrementAssign] is a trait that provides a common interface for decrementing values in +/// place. +pub trait DecrementAssign { + fn decrement_assign(&mut self); +} + +/// [Increment] is a trait that provides a common interface for incrementing values; i.e., +/// adding one to a value. +pub trait Increment { + fn increment(self) -> Self; +} + +/// [IncrementAssign] is a trait that provides a common interface for incrementing values in +/// place. +pub trait IncrementAssign { + fn increment_assign(&mut self); +} + +/// [Incremental] is a trait that provides a common interface for incrementing and decrementing values. +pub trait Incremental: Decrement + Increment + DecrementAssign + IncrementAssign {} + +/* + ************* Implementations ************* + */ + +impl Decrement for T +where + T: num::One + core::ops::Sub, +{ + fn decrement(self) -> Self { + self - T::one() + } +} + +impl DecrementAssign for T +where + T: num::One + core::ops::SubAssign, +{ + fn decrement_assign(&mut self) { + *self -= T::one(); + } +} + +impl Increment for T +where + T: num::One + core::ops::Add, +{ + fn increment(self) -> Self { + self + T::one() + } +} + +impl IncrementAssign for T +where + T: num::One + core::ops::AddAssign, +{ + fn increment_assign(&mut self) { + *self += T::one(); + } +} + +impl Incremental for T where T: Decrement + Increment + DecrementAssign + IncrementAssign {} diff --git a/core/src/traits/io.rs b/core/src/traits/io.rs index d04a1f6..9e1a52f 100644 --- a/core/src/traits/io.rs +++ b/core/src/traits/io.rs @@ -6,7 +6,9 @@ pub trait RawBuf { type Elem; + /// Returns an immutable reference to the buffer as a slice fn as_slice(&self) -> &[Self::Elem]; + /// Returns a mutable reference to the buffer as a slice fn as_mut_slice(&mut self) -> &mut [Self::Elem]; } diff --git a/core/src/traits/mod.rs b/core/src/traits/mod.rs index 1042dc1..b8a0271 100644 --- a/core/src/traits/mod.rs +++ b/core/src/traits/mod.rs @@ -3,15 +3,21 @@ Contrib: FL03 */ #[doc(inline)] -pub use self::symbolic::*; +pub use self::{execute::*, increment::*, symbolic::*, transform::*}; + +pub(crate) mod execute; +pub(crate) mod increment; +pub(crate) mod symbolic; +pub(crate) mod transform; #[doc(hidden)] -pub mod actor; +pub mod cspace; #[doc(hidden)] pub mod io; -pub mod symbolic; pub(crate) mod prelude { + pub use super::execute::*; + pub use super::increment::*; pub use super::symbolic::*; - // pub use super::transform::*; + pub use super::transform::*; } diff --git a/core/src/traits/symbolic.rs b/core/src/traits/symbolic.rs index 2102db5..57766d2 100644 --- a/core/src/traits/symbolic.rs +++ b/core/src/traits/symbolic.rs @@ -3,72 +3,116 @@ Contrib: FL03 */ -/// [Alphabet] abstractly describes the set of symbols used for both -/// the input and output of any given Turing machine. -/// -/// ### Definition -/// -/// An alphabet is formally defines to be a finite set of symbols. +/// [Alphabet] describes a finite set of symbols used to construct a formal language. /// /// Ideally, the alphabet should be implemented on unit enums since /// each symbol can be represented as a unique variant and assigned /// a particular value. The values of the variants may then be used /// as pointers, specifiying the location of the symbol w.r.t. the /// alphabet. -pub trait Alphabet: IntoIterator { - type Sym; +pub trait Alphabet { + type Elem; + + fn as_slice(&self) -> &[Self::Elem]; + + fn as_mut_slice(&mut self) -> &mut [Self::Elem]; + + fn is_empty(&self) -> bool { + self.len() == 0 + } fn len(&self) -> usize { - self.to_vec().len() + self.as_slice().len() } - fn to_vec(&self) -> Vec; + fn to_vec(&self) -> Vec; } -/// [Symbolic] is a generic trait automatically implemented for any -/// type that satisfies the following conditions: -/// - Clone -/// - Eq -/// - Ord +/// [Symbolic] is a trait denoting types that can be used as symbols; +/// this is useful for allowing symbols to represented with [char] or +/// be a position on the tape, value mapping for an alphabet,. pub trait Symbolic where Self: Clone + + Copy + + Default + Eq + Ord + PartialEq + PartialOrd + core::fmt::Debug + core::fmt::Display - + core::hash::Hash, + + core::hash::Hash + + Send + + Sync + + 'static, { } -#[doc(hidden)] -pub trait Symbol: Symbolic { - type Z; +/* + ************* Implementations ************* +*/ +#[cfg(feature = "alloc")] +use alloc::vec::Vec; - fn symbol(&self) -> char; +impl Alphabet for [S] { + type Elem = S; - fn is_symbol(&self, symbol: char) -> bool { - self.symbol() == symbol + fn as_slice(&self) -> &[S] { + self + } + + fn as_mut_slice(&mut self) -> &mut [S] { + self + } + + fn is_empty(&self) -> bool { + self.is_empty() + } + + fn len(&self) -> usize { + self.len() + } + + fn to_vec(&self) -> Vec { + self.to_vec() } - /// Returns the value assigned to the symbol; - fn value(&self) -> Self::Z; } -/* - ************* Implementations ************* -*/ +impl Alphabet for Vec { + type Elem = S; + + fn as_slice(&self) -> &[S] { + self.as_slice() + } + + fn as_mut_slice(&mut self) -> &mut [S] { + self.as_mut_slice() + } -impl Alphabet for Vec { - type Sym = char; + fn is_empty(&self) -> bool { + self.is_empty() + } + + fn len(&self) -> usize { + self.len() + } - fn to_vec(&self) -> Vec { + fn to_vec(&self) -> Vec { self.clone() } } impl Symbolic for S where - S: Clone + Eq + Ord + core::fmt::Debug + core::fmt::Display + core::hash::Hash + S: Copy + + Default + + Eq + + Ord + + core::fmt::Debug + + core::fmt::Display + + core::hash::Hash + + Send + + Sync + + 'static { } diff --git a/core/src/traits/transform.rs b/core/src/traits/transform.rs new file mode 100644 index 0000000..faabec5 --- /dev/null +++ b/core/src/traits/transform.rs @@ -0,0 +1,22 @@ +/* + Appellation: transform + Contrib: FL03 +*/ + +/// [`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, tape: &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 { + 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; +} diff --git a/core/src/types/cursor.rs b/core/src/types/cursor.rs new file mode 100644 index 0000000..de47332 --- /dev/null +++ b/core/src/types/cursor.rs @@ -0,0 +1,193 @@ +/* + 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/types/head.rs b/core/src/types/head.rs new file mode 100644 index 0000000..7c9be8c --- /dev/null +++ b/core/src/types/head.rs @@ -0,0 +1,234 @@ +/* + Appellation: head + Contrib: FL03 +*/ +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. +#[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 { + #[cfg_attr(feature = "serde", serde(alias = "current_state"))] + pub state: State, + #[cfg_attr(feature = "serde", serde(alias = "current_symbol"))] + pub symbol: S, +} + +impl Head { + pub fn new(State(state): State, symbol: S) -> Self { + Self { + state: State(state), + symbol, + } + } + /// Create a new instance of the [Head] using the given state and default symbol. + pub fn from_state(State(state): State) -> Self + where + S: Default, + { + Self { + state: State(state), + symbol: Default::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, + } + } + /// Create a new instance from a 2-tuple: ([state](State), symbol) + pub fn from_tuple((state, symbol): (State, S)) -> Self { + Self { state, symbol } + } + /// Updates the current [state](State) and returns a new head + pub fn with_state(self, State(state): State) -> Self { + Self { + state: State(state), + ..self + } + } + /// Updates the current symbol and returns a new head + pub fn with_symbol(self, symbol: S) -> Self { + Self { symbol, ..self } + } + /// Returns a reference to the current state and symbol returing a 2-tuple + pub fn as_tuple(&self) -> (&State, &S) { + (&self.state, &self.symbol) + } + /// Consumes the head and returns the current state and symbol as a 2-tuple + pub fn into_tuple(self) -> (State, S) { + (self.state, self.symbol) + } + /// Returns a mutable reference to the current state and symbol as a 2-tuple + pub fn as_mut_tuple(&mut self) -> (&mut State, &mut S) { + (&mut self.state, &mut self.symbol) + } + /// Updates the current state + pub fn set_state(&mut self, state: State) { + self.state = state; + } + /// Updates the current symbol + 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() + } + /// 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 + } + /// Updates the current [State] and symbol + pub fn update(&mut self, state: Option>, symbol: Option) { + if let Some(state) = state { + self.state = state; + } + if let Some(symbol) = symbol { + self.symbol = symbol; + } + } + /// Converts the current head into a new head with immutable references to the current state and symbol + pub fn to_ref(&self) -> Head<&Q, &S> { + Head { + state: self.state.to_ref(), + symbol: &self.symbol, + } + } + /// Converts the current head into a new head with mutable references to the current state and symbol + pub fn to_mut(&mut self) -> Head<&mut Q, &mut S> { + Head { + state: self.state.to_mut(), + symbol: &mut self.symbol, + } + } + + pub fn read(self, tape: &'_ [T]) -> Option<&::Output> + where + S: core::slice::SliceIndex<[T]>, + { + tape.get(self.symbol) + } +} + +impl Head { + pub fn shift(self, direction: crate::Direction) -> Self { + Self { + symbol: direction.apply_unsigned(self.symbol), + ..self + } + } + + pub fn shift_inplace(&mut self, direction: crate::Direction) { + self.symbol = direction.apply_unsigned(self.symbol); + } +} + +impl<'a, Q, S> Head<&'a Q, &'a S> { + pub fn cloned(&self) -> Head + where + Q: Clone, + S: Clone, + { + Head { + state: self.state.cloned(), + symbol: self.symbol.clone(), + } + } + + pub fn copied(&self) -> Head + where + Q: Copy, + S: Copy, + { + Head { + state: self.state.copied(), + symbol: *self.symbol, + } + } +} + +impl<'a, Q, S> Head<&'a mut Q, &'a mut S> { + pub fn cloned(&self) -> Head + where + Q: Clone, + S: Clone, + { + Head { + state: self.state.cloned(), + symbol: self.symbol.clone(), + } + } + + pub fn copied(&self) -> Head + where + Q: Copy, + S: Copy, + { + Head { + state: self.state.copied(), + symbol: *self.symbol, + } + } +} + +impl core::fmt::Debug for Head +where + Q: core::fmt::Debug, + S: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("Head") + .field(&self.state) + .field(&self.symbol) + .finish() + } +} + +impl core::fmt::Display for Head +where + Q: core::fmt::Display, + S: core::fmt::Display, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "({}, {})", self.state, self.symbol) + } +} + +impl From<(Q, S)> for Head { + fn from((state, symbol): (Q, S)) -> Self { + Self::new(State(state), symbol) + } +} + +impl From<(State, S)> for Head { + fn from((state, symbol): (State, S)) -> Self { + Self::new(state, symbol) + } +} + +impl From> for (State, S) { + fn from(head: Head) -> Self { + head.into_tuple() + } +} diff --git a/core/src/types/mod.rs b/core/src/types/mod.rs index 12139cb..8ebfe12 100644 --- a/core/src/types/mod.rs +++ b/core/src/types/mod.rs @@ -3,12 +3,18 @@ Contrib: FL03 */ #[doc(inline)] -pub use self::direction::Direction; +pub use self::{head::Head, tail::Tail}; -pub mod direction; +pub(crate) mod head; +pub(crate) mod tail; + +#[doc(hidden)] +pub mod cursor; pub(crate) mod prelude { - pub use super::direction::Direction; + pub use super::head::Head; + pub use super::tail::Tail; + pub use super::IndexedHead; #[allow(unused)] pub(crate) use super::Idx; @@ -16,3 +22,5 @@ pub(crate) mod prelude { /// 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; diff --git a/core/src/rules/parts/tail.rs b/core/src/types/tail.rs similarity index 70% rename from core/src/rules/parts/tail.rs rename to core/src/types/tail.rs index 1c89fc9..308712a 100644 --- a/core/src/rules/parts/tail.rs +++ b/core/src/types/tail.rs @@ -6,18 +6,21 @@ pub use self::builder::TailBuilder; use crate::{Direction, Head, State}; -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Ord, PartialOrd)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +/// The [Tail] is a 3-tuple containing the direction, state, and symbol that an actor is +/// instructed to execute whenever it assumes the head configuration assigned to the tail. +#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, Ord, PartialOrd)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(rename_all = "lowercase") +)] pub struct Tail { pub direction: Direction, - #[cfg_attr( - feature = "serde", - serde(flatten, alias = "state", alias = "next_state") - )] + #[cfg_attr(feature = "serde", serde(alias = "next_state"))] pub state: State, #[cfg_attr( feature = "serde", - serde(flatten, alias = "symbol", alias = "write_symbol") + serde(alias = "next_symbol", alias = "write_symbol") )] pub symbol: S, } @@ -46,12 +49,16 @@ impl Tail { pub fn direction(&self) -> Direction { self.direction } - /// Returns the next [state](State) the agent is instructed to move to - pub fn next_state(&self) -> State<&'_ Q> { + /// Returns the next state with an immutable reference to the inner value + pub fn state(&self) -> State<&'_ Q> { self.state.to_ref() } + /// Returns the next state with a mutable reference to the inner value + pub fn state_mut(&mut self) -> State<&'_ mut Q> { + self.state.to_mut() + } /// Returns the symbol the [head](Head) is instructed to write - pub const fn write_symbol(&self) -> &S { + pub const fn symbol(&self) -> &S { &self.symbol } /// Consumes the tail and returns a new instance of the [Head] @@ -117,6 +124,18 @@ impl<'a, Q, S> Tail<&'a Q, &'a S> { symbol: self.symbol.clone(), } } + + pub fn copied(&self) -> Tail + where + Q: Copy, + S: Copy, + { + Tail { + direction: self.direction, + state: self.state.copied(), + symbol: *self.symbol, + } + } } impl<'a, Q, S> Tail<&'a mut Q, &'a mut S> { @@ -131,6 +150,42 @@ impl<'a, Q, S> Tail<&'a mut Q, &'a mut S> { symbol: self.symbol.clone(), } } + + pub fn copied(&self) -> Tail + where + Q: Copy, + S: Copy, + { + Tail { + direction: self.direction, + state: self.state.copied(), + symbol: *self.symbol, + } + } +} + +impl core::fmt::Debug for Tail +where + Q: core::fmt::Debug, + S: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("Tail") + .field(&self.direction) + .field(&self.state) + .field(&self.symbol) + .finish() + } +} + +impl core::fmt::Display for Tail +where + Q: core::fmt::Display, + S: core::fmt::Display, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}({}, {})", self.direction, self.state, self.symbol) + } } mod builder { diff --git a/core/tests/actor.rs b/core/tests/actor.rs new file mode 100644 index 0000000..8742ef4 --- /dev/null +++ b/core/tests/actor.rs @@ -0,0 +1,40 @@ +/* + Appellation: actor + Contrib: FL03 +*/ +extern crate rstm_core as rstm; + +use rstm::actors::Actor; +use rstm::rules::{Program, Rule}; +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), + (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), + ]; + + static ref PROGRAM: Program = Program::from_iter(RULES.clone()).with_initial_state(INITIAL_STATE); +} + +#[test] +fn busy_beaver() { + let input = [0_usize; 10]; + + let program = Program::new() + .rules(RULES.clone()) + .initial_state(INITIAL_STATE) + .build(); + + let actor = Actor::from_state(State(0)).with_tape(input); + let mut rt = actor.execute(program); + for _ in 0..10 { + assert!(rt.next().is_some()); + } +} diff --git a/rstm/Cargo.toml b/rstm/Cargo.toml index 68d92b4..18c8ec2 100644 --- a/rstm/Cargo.toml +++ b/rstm/Cargo.toml @@ -13,7 +13,9 @@ repository.workspace = true version.workspace = true [features] -default = [] +default = [ + "alloc", +] full = [ "default", @@ -22,9 +24,9 @@ full = [ ] # ********* [FF] Dependencies ********* -# alloc = [ -# "serde?/alloc", -# ] +alloc = [ + "serde?/alloc", +] serde = [ "dep:serde", @@ -50,11 +52,11 @@ doctest = false test = true [[example]] -name = "basic" +name = "actor" required-features = ["tracing"] [[example]] -name = "utm" +name = "basic" required-features = ["tracing"] # ****************** Dependencies ****************** @@ -63,7 +65,7 @@ thiserror.workspace = true [dependencies.rstm-core] path = "../core" -version = "0.0.2" +version = "0.0.3" [dependencies.serde] # default-features = false @@ -84,7 +86,7 @@ version = "0.1" # ****************** Dev Dependencies ****************** [dev-dependencies] lazy_static.workspace = true -tracing-subscriber = { features = [], version = "0.3" } +tracing-subscriber = "0.3" [package.metadata.docs.rs] all-features = true diff --git a/rstm/examples/actor.rs b/rstm/examples/actor.rs new file mode 100644 index 0000000..5e53b2e --- /dev/null +++ b/rstm/examples/actor.rs @@ -0,0 +1,52 @@ +/* + Appellation: actor + Contrib: FL03 +*/ +extern crate rstm; + +use rstm::{ruleset, Actor, Program, State}; + +fn main() -> Result<(), Box> { + _tracing(); + // initialize the tape data + let alpha = vec![0u8; 10]; + // initialize the state of the machine + let initial_state = State(0isize); + // define the ruleset 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), + ]; + + let program = Program::new() + .initial_state(initial_state) + .rules(rules) + .build(); + + // create a new instance of the machine + let tm = dbg!(Actor::from_state(initial_state).with_tape(alpha)); + tm.execute(program).run()?; + Ok(()) +} + +fn _tracing() { + let timer = tracing_subscriber::fmt::time::uptime(); + tracing_subscriber::fmt() + .with_max_level(tracing::Level::INFO) + .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 index 1bc296a..0872b87 100644 --- a/rstm/examples/basic.rs +++ b/rstm/examples/basic.rs @@ -4,28 +4,45 @@ */ extern crate rstm; -use rstm::{ - rule, - state::{self, State}, - StdTape, TM, -}; - -use state::BinState::*; +use rstm::{ruleset, Program, State, StdTape, Turm}; fn main() -> Result<(), Box> { - tracing_subscriber::fmt().with_target(false).init(); - println!("{}", -1_isize as u8); - let tape_data: Vec = vec![0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1]; + _tracing("debug"); - let rules = vec![ - rule![(Invalid, 0) -> Left(Invalid, 0)], - rule![(Invalid, 1) -> Right(Valid, 0)], - rule![(Valid, 0) -> Right(Valid, 1)], - rule![(Valid, 1) -> Left(Valid, 0)], + // 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), ]; - - let tape = StdTape::from_iter(tape_data); - let tm = TM::new(State(Invalid), rules, tape); - tm.run()?; + // 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/examples/utm.rs b/rstm/examples/utm.rs deleted file mode 100644 index a176e96..0000000 --- a/rstm/examples/utm.rs +++ /dev/null @@ -1,84 +0,0 @@ -/* - Appellation: wolfram - Contrib: FL03 -*/ -extern crate rstm; - -use rstm::{ - rule, - state::{self, State}, - StdTape, TM, -}; - -use state::BinState::*; - -fn main() -> Result<(), Box> { - tracing_subscriber::fmt().with_level(true).init(); - tracing::info!("Example: Wolfram [2, 3] UTM"); - - let tape = StdTape::::from_iter([0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1]); - let initial_state = State(Invalid); - - let rules = vec![ - rule![(Invalid, 0) -> Right(Invalid, 0)], - rule![(Invalid, 1) -> Right(Valid, 0)], - rule![(Valid, 0) -> Right(Valid, 1)], - rule![(Valid, 1) -> Right(Valid, 0)], - ]; - - let tm = TM::new(initial_state, rules, tape); - tm.run()?; - Ok(()) -} - -pub mod wolfram { - - #[derive( - Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, strum::EnumIter, - )] - #[repr(u8)] - pub enum Three { - #[default] - A = 0, - B = 1, - C = 2, - } - - impl Three { - pub fn as_u8(&self) -> u8 { - *self as u8 - } - - pub fn iter() -> impl Iterator { - use Three::*; - [A, B, C].into_iter() - } - - pub fn iter_value() -> impl Iterator { - [0, 1, 2].into_iter() - } - } - - impl rstm::Alphabet for Three { - type Sym = Self; - - fn to_vec(&self) -> Vec { - use Three::*; - vec![A, B, C] - } - } - - impl IntoIterator for Three { - type Item = Self; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - vec![Three::A, Three::B, Three::C].into_iter() - } - } - - pub enum Two { - A(T), - B(T), - } -} diff --git a/rstm/src/lib.rs b/rstm/src/lib.rs index aa74352..04b9f08 100644 --- a/rstm/src/lib.rs +++ b/rstm/src/lib.rs @@ -4,27 +4,30 @@ */ //! # rstm //! +//! `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. +//! #![crate_name = "rstm"] // #![cfg_attr(not(feature = "std"), no_std)] -// #[cfg(feature = "alloc")] -// extern crate alloc; +#[cfg(feature = "alloc")] +extern crate alloc; pub use rstm_core::*; #[doc(inline)] -pub use self::turing::TM; +pub use self::turing::Turm; #[macro_use] -pub(crate) mod macros { - #[macro_use] - pub mod rules; -} +pub(crate) mod macros {} -#[doc(hidden)] -pub mod sand; pub mod turing; pub mod prelude { - pub use crate::turing::prelude::*; + pub use crate::turing::Turm; pub use rstm_core::prelude::*; } diff --git a/rstm/src/macros/rules.rs b/rstm/src/macros/rules.rs deleted file mode 100644 index 598dedf..0000000 --- a/rstm/src/macros/rules.rs +++ /dev/null @@ -1,25 +0,0 @@ -/* - Appellation: rules - Contrib: FL03 -*/ -/// A macro for creating. -/// -/// # Example -/// -/// ``` -/// -/// use rstm::rule; -/// -/// let rule = rule![(0, '0') -> Right(1, '1')]; -#[macro_export] -macro_rules! rule { - [($state:expr, $symbol:literal $(,)?) -> $direction:ident($next:expr, $write:literal $(,)?) $(,)?] => { - rstm_core::Instruction::new() - .state(rstm_core::State($state)) - .symbol($symbol) - .write_symbol($write) - .direction(rstm_core::Direction::$direction) - .next_state(rstm_core::State($next)) - .build() - }; -} diff --git a/rstm/src/sand/mod.rs b/rstm/src/sand/mod.rs deleted file mode 100644 index f224617..0000000 --- a/rstm/src/sand/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -/* - Appellation: sand - Contrib: FL03 -*/ -#![allow(unused)] - -pub mod types; diff --git a/rstm/src/sand/types/mod.rs b/rstm/src/sand/types/mod.rs deleted file mode 100644 index 2134f89..0000000 --- a/rstm/src/sand/types/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -/* - Appellation: types - Contrib: FL03 -*/ - -pub mod registry; -pub mod scope; -pub mod store; - -pub(crate) mod prelude { - - 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; diff --git a/rstm/src/sand/types/registry.rs b/rstm/src/sand/types/registry.rs deleted file mode 100644 index fdc83e0..0000000 --- a/rstm/src/sand/types/registry.rs +++ /dev/null @@ -1,17 +0,0 @@ -/* - Appellation: registry - Contrib: FL03 -*/ -use crate::rules::Instruction; -use std::collections::hash_map::{self, HashMap}; - -type Store = HashMap>; - -pub struct Registry { - pub(crate) rules: HashMap>, -} - -pub struct Actor { - pub(crate) pos: isize, // - pub(crate) rule: Instruction, -} diff --git a/rstm/src/sand/types/scope.rs b/rstm/src/sand/types/scope.rs deleted file mode 100644 index b245e3f..0000000 --- a/rstm/src/sand/types/scope.rs +++ /dev/null @@ -1,11 +0,0 @@ -/* - Appellation: agent - Contrib: FL03 -*/ - -use crate::state::State; - -pub struct Observer { - pub position: usize, - pub state: State, -} diff --git a/rstm/src/sand/types/store.rs b/rstm/src/sand/types/store.rs deleted file mode 100644 index b1eea15..0000000 --- a/rstm/src/sand/types/store.rs +++ /dev/null @@ -1,128 +0,0 @@ -/* - Appellation: symbol - Contrib: FL03 -*/ -use crate::{Head, Symbolic, Tail}; -use std::collections::hash_map::{self, Entry, HashMap}; - -pub struct Ruleset { - pub(crate) rules: HashMap, Tail>, -} - -impl Ruleset -where - Q: Eq + core::hash::Hash, - S: Symbolic, -{ - pub fn new() -> Self { - Ruleset { - rules: HashMap::new(), - } - } - pub fn from_iter(iter: I) -> Self - where - I: IntoIterator, - R: Into<(Head, Tail)>, - HashMap, Tail>: FromIterator, - { - Ruleset { - rules: iter.into_iter().collect(), - } - } - pub fn from_rules(rules: HashMap, Tail>) -> Self { - Ruleset { rules } - } - /// Given a head, returns an [entry](Entry) in the ruleset for in-place manipulation - pub fn entry(&mut self, key: Head) -> Entry<'_, Head, Tail> { - self.rules.entry(key) - } - /// Inserts a new rule into the ruleset - pub fn insert(&mut self, head: Head, tail: Tail) { - self.rules.insert(head, tail); - } - /// Returns a reference to the tail of a given head; - /// if the head is not in the ruleset, returns [None](Option::None) - pub fn get(&self, head: &Head) -> Option<&Tail> { - self.rules.get(head) - } - /// Returns a mutable reference to the tail of a given head; - /// if the head is not in the ruleset, returns [None](Option::None) - pub fn get_mut(&mut self, head: &Head) -> Option<&mut Tail> { - self.rules.get_mut(head) - } - /// Returns the number of rules in the ruleset - pub fn len(&self) -> usize { - self.rules.len() - } - /// Check to see whether the ruleset is empty - pub fn is_empty(&self) -> bool { - self.rules.is_empty() - } - - pub fn iter(&self) -> hash_map::Iter<'_, Head, Tail> { - self.rules.iter() - } - - pub fn iter_mut(&mut self) -> hash_map::IterMut<'_, Head, Tail> { - self.rules.iter_mut() - } - - pub fn keys(&self) -> hash_map::Keys<'_, Head, Tail> { - self.rules.keys() - } - - pub fn values(&self) -> hash_map::Values<'_, Head, Tail> { - self.rules.values() - } - - pub fn values_mut(&mut self) -> hash_map::ValuesMut<'_, Head, Tail> { - self.rules.values_mut() - } - - pub fn retain(&mut self, f: F) - where - F: FnMut(&Head, &mut Tail) -> bool, - { - self.rules.retain(f) - } - - pub fn remove(&mut self, head: &Head) -> Option> { - self.rules.remove(head) - } -} - -impl Extend<(Head, Tail)> for Ruleset -where - Q: Eq + core::hash::Hash, - S: Symbolic, -{ - fn extend(&mut self, iter: T) - where - T: IntoIterator, Tail)>, - { - self.rules.extend(iter) - } -} - -impl Extend> for Ruleset -where - Q: Eq + core::hash::Hash, - S: Symbolic, -{ - fn extend(&mut self, iter: T) - where - T: IntoIterator>, - { - self.rules - .extend(iter.into_iter().map(|i| (i.head, i.tail))) - } -} - -impl IntoIterator for Ruleset { - type Item = (Head, Tail); - type IntoIter = hash_map::IntoIter, Tail>; - - fn into_iter(self) -> Self::IntoIter { - self.rules.into_iter() - } -} diff --git a/rstm/src/turing/model.rs b/rstm/src/turing.rs similarity index 50% rename from rstm/src/turing/model.rs rename to rstm/src/turing.rs index da916ae..49deae4 100644 --- a/rstm/src/turing/model.rs +++ b/rstm/src/turing.rs @@ -2,68 +2,58 @@ Appellation: tm Contrib: FL03 */ -use crate::prelude::{Error, Head, StdTape, Symbolic}; -use crate::rules::{Instruction, Program}; + + +use crate::prelude::{Error, HaltState, Head, StdTape, Symbolic, Tail}; +use crate::rules::Program; use crate::state::State; -/// # Turing Machine ([TM]) +/// # Turing Machine ([Turm]) /// /// 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 TM { +pub struct Turm { pub(crate) program: Program, - pub(crate) state: State, + pub(crate) state: HaltState, pub(crate) tape: StdTape, } -impl TM { - pub fn new( - State(state): State, - instructions: impl IntoIterator>, - tape: StdTape, - ) -> Self +impl Turm { + pub fn new(program: Program, tape: StdTape) -> Self where - Q: Clone, - S: Clone + Default, + Q: Clone + Default, + S: Default, { - let state = State(state); - let program = Program::new(state.clone()).with_instructions(instructions); - TM { + let state = program.initial_state().cloned(); + Turm { program, - state, + state: HaltState::state(state), tape, } } - /// Creates a new instance of a [head](Head) from references to the current state and symbol; - pub fn head(&self) -> Head<&'_ Q, &'_ S> - where - Q: Clone, - S: Clone, - { - let state = self.state(); - let symbol = self.tape().read().unwrap(); - Head::new(state, symbol) - } /// Returns an immutable reference to the [program](Program) pub const fn program(&self) -> &Program { &self.program } + /// Creates a new instance of a [head](Head) from references to the current state and symbol; + pub fn read(&self) -> Option> { + 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.to_ref() + 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.to_mut() - } - /// Returns an instance of the [state](State) with an immutable - pub fn set_state(&mut self, state: State) { - self.state = state; + self.state.as_mut_state() } /// Returns an immutable reference to the [tape](StdTape) pub const fn tape(&self) -> &StdTape { @@ -73,55 +63,74 @@ impl TM { pub fn tape_mut(&mut self) -> &mut StdTape { &mut self.tape } - /// Runs the program until the - /// + /// Runs the program until the + /// /// The program will continue to run until the current state is a halt state. #[cfg_attr( feature = "tracing", - tracing::instrument(skip_all, name = "run", target = "fsm") + tracing::instrument(skip_all, name = "run", target = "turing") )] - pub fn run(mut self) -> Result<(), Error> where Q: Clone + PartialEq, S: Symbolic { + pub fn execute(mut self) -> Result<(), Error> + where + Q: Clone + PartialEq + 'static, + S: Symbolic, + { #[cfg(feature = "tracing")] - tracing::info!("Running the program..."); + tracing::trace!("Executing the program..."); loop { #[cfg(feature = "tracing")] - tracing::info!("{}", &self.tape); - match self.next() { + tracing::info!("{tape}", tape = self.tape()); + match self.process() { Some(_) => { - // if self.current_state().is_halt() { - // return Ok(()); - // } - continue; + if self.state.is_halt() { + return Ok(()); + } else { + continue; + } } None => { - return Err(Error::unknown("Runtime Error")); + return Err(Error::runtime_error("")); } } } } -} - -impl core::iter::Iterator for TM -where - Q: Clone + PartialEq, - S: Symbolic, -{ - type Item = Head; #[cfg_attr( feature = "tracing", - tracing::instrument(skip_all, name = "step", target = "fsm") + tracing::instrument(skip_all, name = "process", target = "turing") )] - fn next(&mut self) -> Option { + fn process(&mut self) -> Option> + where + Q: Clone + PartialEq, + S: Clone + PartialEq, + { #[cfg(feature = "tracing")] - tracing::info!("Stepping..."); - // Create a new head from the current state and symbol - let head = self.head().cloned(); + tracing::trace!("Processing the current instruction..."); // Get the first instruction for the current head - if let Some(&tail) = self.program.get_head(&head).first() { - self.state = self.tape.update_inplace(tail.cloned()); - return Some(tail.cloned().into_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)); } - 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/turing/context.rs b/rstm/src/turing/context.rs deleted file mode 100644 index 018b949..0000000 --- a/rstm/src/turing/context.rs +++ /dev/null @@ -1,63 +0,0 @@ -/* - Appellation: context - Contrib: FL03 -*/ -use crate::rules::Program; -use crate::state::State; - -#[derive(Clone, Debug, Default)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Context { - pub(crate) program: Program, - pub(crate) state: Option>, -} - -impl Context { - pub fn new(program: Program, state: State) -> Self { - Self { - program, - state: Some(state), - } - } - /// - pub fn from_program(program: Program) -> Self { - Self { - program, - state: None, - } - } - - pub fn from_state(state: State) -> Self - where - Q: Default, - S: Default, - { - Self { - program: Program::default(), - state: Some(state), - } - } - - // /// Returns the current state of the system; - // /// if the state is [none](Option::None), assumes the initial state. - // pub fn current_state(&self) -> State<&'_ Q> { - // let q = &self.state.map(State::into_inner).unwrap_or(self.program.initial_state.into_inner()); - // State(q) - // } - - pub fn initial_state(&self) -> State<&'_ Q> { - self.program.initial_state() - } - - pub fn program(&self) -> &Program { - &self.program - } - - pub fn program_mut(&mut self) -> &mut Program { - &mut self.program - } - - pub fn set_state(&mut self, state: State) { - self.state = Some(state); - } -} diff --git a/rstm/src/turing/mod.rs b/rstm/src/turing/mod.rs deleted file mode 100644 index 6981970..0000000 --- a/rstm/src/turing/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -/* - Appellation: turing - Contrib: FL03 -*/ -//! # Turing Machine ([TM]) -//! -//! ### Overview -//! -//! A Turing machine is a mathematical model describing the generalization of computation using -//! a set of symbols and a [tape](Tape). Assuming an infinite tape, the machine can read, write, and move linearly -//! across the tape. The machine uses a set of pre-defined rules to determine the next state and symbol. -//! -#[doc(inline)] -pub use self::{context::Context, model::TM}; - -pub(crate) mod context; -pub(crate) mod model; - -pub(crate) mod prelude { - pub use super::model::TM; -} - -#[doc(hidden)] -pub trait Turing { - type Alpha; // input alphabet - type Beta; // output alphabet - type Gamma; // -} - -pub trait Ctx {} -pub trait Actor { - type Ctx; - - fn handle(&self, context: Context) -> Result<(), Box>; -}