typed_sf/
lib.rs

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