1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
#![cfg_attr(feature = "no_std", no_std)]
#![cfg_attr(feature = "require_docs", deny(missing_docs))]
#![deny(warnings, clippy::all, clippy::pedantic)]
#![doc(test(attr(deny(warnings))))]
/*!
Type-level implementation of [SF](https://esolangs.org/wiki/Smallfuck).
Proving that type-system turing-complete. Highly inspired
by [this article](https://sdleffler.github.io/RustTypeSystemTuringComplete/).
# Features
`typed-sf` supports "no_std" feature with limitations. Runtime
representation of [`List`]'s and [`State`][StateTrait]'s unavailable
(they are using [`Vec`] to represent themselves).
Full list of unsupported features:
* [`List::val()`] (That includes [`Nil`] and [`Cons`]).
* [`RuntimeState`].
* [`StateTrait::val()`] (That includes [`State`] and [`DefaultState`]).
Also where is "require_docs" feature which forbids undocumented `pub` items.
If You want to contrubute, feel free to turn off this default feature.
# How it works
First, there's some traits that i'm calling type-level enums ([`Bit`], [`List`]).
Have a look at it:
```
# #[derive(PartialEq, Debug)]
enum Bit {
True,
False
}
impl Bit {
fn neg(self) -> Bit {
match self {
Bit::True => Bit::False,
Bit::False => Bit::True
}
}
}
assert_eq!(Bit::True, Bit::False.neg());
assert_eq!(Bit::False, Bit::True.neg());
```
```
trait Bit {
type Neg: Bit;
fn real() -> bool;
}
struct True;
struct False;
impl Bit for True {
type Neg = False;
fn real() -> bool { true }
}
impl Bit for False {
type Neg = True;
fn real() -> bool { false }
}
assert_eq!(True::real(), <False as Bit>::Neg::real());
assert_eq!(False::real(), <True as Bit>::Neg::real());
```
Of course, what's more bolierplate, but also instead of
matching in runtime, compiler solves type-equasions in
compile-time for us.
To type-encode functions i'm using something like this:
```
# use std::ops::{Add, Sub};
#
// A and B are arguments to 'function'.
trait Call<A, B> { type Return; }
struct Sum;
struct Diff;
impl<A, B> Call<A, B> for Sum
where
A: Add<B>
{
type Return = <A as Add<B>>::Output;
}
impl<A, B> Call<A, B> for Diff
where
A: Sub<B>
{
type Return = <A as Sub<B>>::Output;
}
# #[allow(dead_code)]
type Apply<A, Fn, B> = <Fn as Call<A, B>>::Return;
```
What we can use later as:
```
# use std::ops::{Add, Sub};
# trait Call<A, B> { type Return; }
# struct Sum;
# struct Diff;
# impl<A, B> Call<A, B> for Sum where A: Add<B> { type Return = <A as Add<B>>::Output; }
# impl<A, B> Call<A, B> for Diff where A: Sub<B> { type Return = <A as Sub<B>>::Output; }
# type Apply<A, Fn, B> = <Fn as Call<A, B>>::Return;
#
struct X;
struct Y;
# #[derive(Debug, PartialEq)]
struct XaddY;
# #[derive(Debug, PartialEq)]
struct XsubY;
impl Add<Y> for X {
type Output = XaddY;
# #[allow(unused)]
# fn add(self, y: Y) -> XaddY {unimplemented!()}
}
impl Sub<Y> for X {
type Output = XsubY;
# #[allow(unused)]
# fn sub(self, y: Y) -> XsubY {unimplemented!()}
}
/* Add `::val() -> Self` function to all types... */
# impl XaddY { fn val() -> Self {XaddY} }
# impl XsubY { fn val() -> Self {XsubY} }
assert_eq!(
<Apply<X, Sum, Y>>::val(),
XaddY::val()
);
assert_eq!(
<Apply<X, Diff, Y>>::val(),
XsubY::val()
);
```
That's basically how [`Runner`]`<`[`Left`][List]`, `[`Value`][Bit]`, `[`Right`][List]`>` works.
But commands such as [`Left`] take a generic argument, so it runs next command on
modified state which does the same and so on...
# Examples
Basic usage
```
# use typed_sf::*;
type SetTrue<Next = EOF> = Cycle<Flip, Flip<Next>>;
// [*<<[*]*>>>] // Move any-sized chunk of True's 2 cells left
# #[allow(non_camel_case_types)]
type prog = Cycle<Flip<Left<Left<SetTrue<Right<Right<Right>>>>>>>;
# #[allow(non_camel_case_types)]
type result = Run<
prog,
State<Nil, True, Cons<True, Nil>, False>
>;
assert_eq!(
<result as StateTrait>::val(),
(vec![true, true, false, false], false, Vec::new())
); // ^^^^^^^^^^^^
// Some waste, honestly don't why it is here...
```
*/
#[cfg(test)]
#[cfg(not(feature = "no_std"))]
mod tests;
#[cfg(test)]
#[cfg(feature = "no_std")]
#[path = "tests_no_std.rs"]
mod tests;
pub(crate) mod bit;
pub(crate) mod core;
pub(crate) mod list;
pub(crate) mod cmds {
pub(crate) mod cycle;
pub use self::cycle::Cycle;
pub(crate) mod eof;
pub use self::eof::EOF;
pub(crate) mod flip;
pub use self::flip::Flip;
pub(crate) mod left;
pub use left::Left;
pub(crate) mod right;
pub use right::Right;
pub(crate) mod utils;
pub use utils::*;
}
pub use crate::bit::*;
pub use crate::cmds::utils::*;
pub use crate::cmds::*;
pub use crate::core::*;
pub use crate::list::*;
#[cfg(not(feature = "no_std"))]
pub use crate::core::RuntimeState;