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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
/*!
    An optimizing Brainfuck interpreter.

    - For the full documentation, see [`Brainfuck`].
    - If you don't like swearing, `Brainfuck` is also re-exported as `Brainfrick`.

    # Example
    ```
    use brainfrick::Brainfuck;

    let purpzie_sucks = Brainfuck::execute("
        ++++++++[>++++++++++<-]>.<++[>++++++++++<-]+++[>+++++<-]>+
        +.---.--.++++++++++.<++[>----------<-]>+++.----.<+++++++[>
        ----------<-]>+.<++++++++[>++++++++++<-]>+++.++.<+++[>----
        --<-]>.++++++++.++++++++.<++++++++[>----------<-]>--.
    ")?;

    assert_eq!(purpzie_sucks, "Purpzie sucks!");
    # Ok::<(), brainfrick::Error>(())
    ```
*/

#![allow(clippy::match_bool)]
#![doc(html_root_url = "https://docs.rs/brainfrick/1.1.0")]

mod error;
mod func;
mod mem;
mod step;

pub use error::{Error, ErrorKind};

/**
    A struct that parses and runs brainfuck.

    # Example
    ```
    use brainfrick::Brainfuck;

    let purpzie_sucks = Brainfuck::execute("
        ++++++++[>++++++++++<-]>.<++[>++++++++++<-]+++[>+++++<-]>+
        +.---.--.++++++++++.<++[>----------<-]>+++.----.<+++++++[>
        ----------<-]>+.<++++++++[>++++++++++<-]>+++.++.<+++[>----
        --<-]>.++++++++.++++++++.<++++++++[>----------<-]>--.
    ")?;

    assert_eq!(purpzie_sucks, "Purpzie sucks!");
    # Ok::<(), brainfrick::Error>(())
    ```

    ## The debug character
    To aid whoever is crazy enough to write in brainfuck, the question mark `?` will output the current cell number and value.

    ```
    # use brainfrick::Brainfuck;
    let where_am_i = Brainfuck::execute(">>+++++?")?;
    assert_eq!(where_am_i, "[2,5]");
    # Ok::<(), brainfrick::Error>(())
    ```

    ## Memory details
    Memory is infinite in both directions. In order to prevent malicious brainfuck from running forever, there is a configurable,
    maximum number of 'steps' you can allow to be executed before stopping.

    ```
    # use brainfrick::Brainfuck;
    let mut infinite = Brainfuck::parse("+[>+]")?;
    infinite.max_steps = 100;
    let result = infinite.run();

    assert!(result.is_err());
    # Ok::<(), brainfrick::Error>(())
    ```

    ## Testing multiple inputs
    If you'd like to quickly test the same brainfuck with many different inputs, you can
    parse it beforehand to speed up the process. See [`Brainfuck::input`] for information.
*/
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Brainfuck {
    steps: Vec<step::Step>,
    indexes: Vec<error::Index>,
    code: std::sync::Arc<String>,
    /// The maximum number of 'steps' that will be run before stopping. Defaults to [`Brainfuck::MAX_STEPS`].
    pub max_steps: usize,
}

impl Brainfuck {
    /// The default maximum number of 'steps' that will be run before stopping.
    pub const MAX_STEPS: usize = 100_000;

    /**
        Run some brainfuck.

        # Errors
        May return an [`Error`] of kind [`UnmatchedBracket`](ErrorKind::UnmatchedBracket) or [`MaxSteps`](ErrorKind::MaxSteps).

        # Example
        ```
        # use brainfrick::Brainfuck;
        let purpzie_sucks = Brainfuck::execute("
            ++++++++[>++++++++++<-]>.<++[>++++++++++<-]+++[>+++++<-]>+
            +.---.--.++++++++++.<++[>----------<-]>+++.----.<+++++++[>
            ----------<-]>+.<++++++++[>++++++++++<-]>+++.++.<+++[>----
            --<-]>.++++++++.++++++++.<++++++++[>----------<-]>--.
        ")?;

        assert_eq!(purpzie_sucks, "Purpzie sucks!");
        # Ok::<(), brainfrick::Error>(())
        ```
    */
    #[inline]
    pub fn execute<S: Into<String>>(code: S) -> Result<String, Error> {
        Self::parse(code)?.run()
    }

    /**
        Run some brainfuck with input.

        # Errors
        May return an [`Error`] of kind [`UnmatchedBracket`](ErrorKind::UnmatchedBracket) or [`MaxSteps`](ErrorKind::MaxSteps).

        # Example
        ```
        # use brainfrick::Brainfuck;
        let loud = Brainfuck::execute_with_input(
            ",[>++++[<-------->-]<.,]",
            "foobar",
        )?;

        assert_eq!(loud, "FOOBAR");
        # Ok::<(), brainfrick::Error>(())
        ```
    */
    #[inline]
    pub fn execute_with_input<O: Into<String>, R: AsRef<str>>(
        code: O,
        input: R,
    ) -> Result<String, Error> {
        Self::parse(code)?.input(input)
    }

    /**
        Parse some brainfuck for later use.

        This essentially just creates a [`Brainfuck`] struct.

        # Errors
        May return an [`UnmatchedBracket`](ErrorKind::UnmatchedBracket) error.

        # Example
        ```
        # use brainfrick::Brainfuck;
        let purpzie = Brainfuck::parse("
            ++++++++[>++++++++++<-]>.<++[>++++++++++<-]+++[>+++++<-]>+
            +.---.--.++++++++++.<++[>----------<-]>+++.----.<+++++++[>
            ----------<-]>+.<++++++++[>++++++++++<-]>+++.++.<+++[>----
            --<-]>.++++++++.++++++++.<++++++++[>----------<-]>--.
        ")?;

        // ...later

        let sucks = purpzie.run()?;

        assert_eq!(sucks, "Purpzie sucks!");
        # Ok::<(), brainfrick::Error>(())
        ```
    */
    #[inline]
    pub fn parse<S: Into<String>>(code: S) -> Result<Brainfuck, Error> {
        func::parse(std::sync::Arc::new(code.into()), Self::MAX_STEPS)
    }

    /**
        Run the brainfuck.

        Note that, for a single [`Brainfuck`], this will always output the same result.

        # Errors
        May return a [`MaxSteps`](ErrorKind::MaxSteps) error.

        # Example
        ```
        # use brainfrick::Brainfuck;
        let purpzie = Brainfuck::parse("
            ++++++++[>++++++++++<-]>.<++[>++++++++++<-]+++[>+++++<-]>+
            +.---.--.++++++++++.<++[>----------<-]>+++.----.<+++++++[>
            ----------<-]>+.<++++++++[>++++++++++<-]>+++.++.<+++[>----
            --<-]>.++++++++.++++++++.<++++++++[>----------<-]>--.
        ")?;

        // ...later

        let sucks = purpzie.run()?;

        assert_eq!(sucks, "Purpzie sucks!");
        # Ok::<(), brainfrick::Error>(())
        ```
    */
    #[inline]
    pub fn run(&self) -> Result<String, Error> {
        func::execute(self, std::iter::empty())
    }

    /**
        Run the brainfuck with input.

        # Errors
        May return a [`MaxSteps`](ErrorKind::MaxSteps) error.

        # Example
        ```
        # use brainfrick::Brainfuck;
        let loud = Brainfuck::parse(",[>++++[<-------->-]<.,]")?;

        assert_eq!(loud.input("foobar")?, "FOOBAR");
        assert_eq!(loud.input("heck")?, "HECK");
        assert_eq!(loud.input("aaaaa")?, "AAAAA");
        # Ok::<(), brainfrick::Error>(())
        ```
    */
    #[inline]
    pub fn input<S: AsRef<str>>(&self, input: S) -> Result<String, Error> {
        func::execute(self, input.as_ref().bytes())
    }

    /**
        The original, brainfuck 'source'.

        # Example
        ```
        # use brainfrick::Brainfuck;
        let code = ",[>++++[<-------->-]<.,]";
        let example = Brainfuck::parse(code)?;
        assert_eq!(code, example.code());
        # Ok::<(), brainfrick::Error>(())
        ```
    */
    #[inline]
    pub fn code(&self) -> &str {
        &self.code
    }
}

// for people who don't like swearing
pub use Brainfuck as Brainfrick;