brainfrick/
lib.rs

1/*!
2    An optimizing Brainfuck interpreter.
3
4    - For the full documentation, see [`Brainfuck`].
5    - If you don't like swearing, `Brainfuck` is also re-exported as `Brainfrick`.
6
7    # Example
8    ```
9    use brainfrick::Brainfuck;
10
11    let purpzie_sucks = Brainfuck::execute("
12        ++++++++[>++++++++++<-]>.<++[>++++++++++<-]+++[>+++++<-]>+
13        +.---.--.++++++++++.<++[>----------<-]>+++.----.<+++++++[>
14        ----------<-]>+.<++++++++[>++++++++++<-]>+++.++.<+++[>----
15        --<-]>.++++++++.++++++++.<++++++++[>----------<-]>--.
16    ")?;
17
18    assert_eq!(purpzie_sucks, "Purpzie sucks!");
19    # Ok::<(), brainfrick::Error>(())
20    ```
21*/
22
23#![allow(clippy::match_bool)]
24#![doc(html_root_url = "https://docs.rs/brainfrick/1.1.2")]
25
26mod error;
27mod execute;
28mod mem;
29mod parse;
30mod step;
31
32use execute::execute;
33use parse::parse;
34use std::sync::Arc;
35
36pub use error::{Error, ErrorKind};
37
38/**
39    A struct that parses and runs brainfuck.
40
41    # Example
42    ```
43    use brainfrick::Brainfuck;
44
45    let purpzie_sucks = Brainfuck::execute("
46        ++++++++[>++++++++++<-]>.<++[>++++++++++<-]+++[>+++++<-]>+
47        +.---.--.++++++++++.<++[>----------<-]>+++.----.<+++++++[>
48        ----------<-]>+.<++++++++[>++++++++++<-]>+++.++.<+++[>----
49        --<-]>.++++++++.++++++++.<++++++++[>----------<-]>--.
50    ")?;
51
52    assert_eq!(purpzie_sucks, "Purpzie sucks!");
53    # Ok::<(), brainfrick::Error>(())
54    ```
55
56    ## The debug character
57    To aid whoever is crazy enough to write in brainfuck, the question mark `?` will output the
58    current cell number and value.
59
60    ```
61    # use brainfrick::Brainfuck;
62    let where_am_i = Brainfuck::execute(">>+++++?")?;
63    assert_eq!(where_am_i, "[2,5]");
64    # Ok::<(), brainfrick::Error>(())
65    ```
66
67    ## Memory details
68    Memory is infinite in both directions. In order to prevent malicious brainfuck from running
69    forever, there is a configurable, maximum number of 'steps' you can allow to be executed before
70    stopping.
71
72    ```
73    # use brainfrick::Brainfuck;
74    let mut infinite = Brainfuck::parse("+[>+]")?;
75    infinite.max_steps = 100;
76    let result = infinite.run();
77
78    assert!(result.is_err());
79    # Ok::<(), brainfrick::Error>(())
80    ```
81
82    ## Testing multiple inputs
83    If you'd like to quickly test the same brainfuck with many different inputs, you can
84    parse it beforehand to speed up the process. See [`Brainfuck::input`] for information.
85*/
86#[derive(Debug, Clone, PartialEq, Eq)]
87pub struct Brainfuck {
88    steps: Vec<step::Step>,
89    indexes: Vec<error::Index>,
90    code: Arc<String>,
91    /// The maximum number of 'steps' that will be run before stopping. Defaults to
92    /// [`Brainfuck::MAX_STEPS`].
93    pub max_steps: usize,
94}
95
96impl Brainfuck {
97    /// The default maximum number of 'steps' that will be run before stopping.
98    pub const MAX_STEPS: usize = 100_000;
99
100    /**
101        Run some brainfuck.
102
103        # Errors
104        May return an [`Error`] of kind [`UnmatchedBracket`](ErrorKind::UnmatchedBracket) or
105        [`MaxSteps`](ErrorKind::MaxSteps).
106
107        # Example
108        ```
109        # use brainfrick::Brainfuck;
110        let purpzie_sucks = Brainfuck::execute("
111            ++++++++[>++++++++++<-]>.<++[>++++++++++<-]+++[>+++++<-]>+
112            +.---.--.++++++++++.<++[>----------<-]>+++.----.<+++++++[>
113            ----------<-]>+.<++++++++[>++++++++++<-]>+++.++.<+++[>----
114            --<-]>.++++++++.++++++++.<++++++++[>----------<-]>--.
115        ")?;
116
117        assert_eq!(purpzie_sucks, "Purpzie sucks!");
118        # Ok::<(), brainfrick::Error>(())
119        ```
120    */
121    pub fn execute<S: Into<String>>(code: S) -> Result<String, Error> {
122        Self::parse(code)?.run()
123    }
124
125    /**
126        Run some brainfuck with input.
127
128        # Errors
129        May return an [`Error`] of kind [`UnmatchedBracket`](ErrorKind::UnmatchedBracket) or
130        [`MaxSteps`](ErrorKind::MaxSteps).
131
132        # Example
133        ```
134        # use brainfrick::Brainfuck;
135        let loud = Brainfuck::execute_with_input(
136            ",[>++++[<-------->-]<.,]",
137            "foobar",
138        )?;
139
140        assert_eq!(loud, "FOOBAR");
141        # Ok::<(), brainfrick::Error>(())
142        ```
143    */
144    pub fn execute_with_input<O: Into<String>, R: AsRef<str>>(
145        code: O,
146        input: R,
147    ) -> Result<String, Error> {
148        Self::parse(code)?.input(input)
149    }
150
151    /**
152        Parse some brainfuck for later use.
153
154        This essentially just creates a [`Brainfuck`] struct.
155
156        # Errors
157        May return an [`UnmatchedBracket`](ErrorKind::UnmatchedBracket) error.
158
159        # Example
160        ```
161        # use brainfrick::Brainfuck;
162        let purpzie = Brainfuck::parse("
163            ++++++++[>++++++++++<-]>.<++[>++++++++++<-]+++[>+++++<-]>+
164            +.---.--.++++++++++.<++[>----------<-]>+++.----.<+++++++[>
165            ----------<-]>+.<++++++++[>++++++++++<-]>+++.++.<+++[>----
166            --<-]>.++++++++.++++++++.<++++++++[>----------<-]>--.
167        ")?;
168
169        // ...later
170
171        let sucks = purpzie.run()?;
172
173        assert_eq!(sucks, "Purpzie sucks!");
174        # Ok::<(), brainfrick::Error>(())
175        ```
176    */
177    pub fn parse<S: Into<String>>(code: S) -> Result<Brainfuck, Error> {
178        parse(code.into(), Self::MAX_STEPS)
179    }
180
181    /**
182        Run the parsed brainfuck.
183
184        Note that, for a single [`Brainfuck`], this will always output the same result.
185
186        # Errors
187        May return a [`MaxSteps`](ErrorKind::MaxSteps) error.
188
189        # Example
190        ```
191        # use brainfrick::Brainfuck;
192        let purpzie = Brainfuck::parse("
193            ++++++++[>++++++++++<-]>.<++[>++++++++++<-]+++[>+++++<-]>+
194            +.---.--.++++++++++.<++[>----------<-]>+++.----.<+++++++[>
195            ----------<-]>+.<++++++++[>++++++++++<-]>+++.++.<+++[>----
196            --<-]>.++++++++.++++++++.<++++++++[>----------<-]>--.
197        ")?;
198
199        // ...later
200
201        let sucks = purpzie.run()?;
202
203        assert_eq!(sucks, "Purpzie sucks!");
204        # Ok::<(), brainfrick::Error>(())
205        ```
206    */
207    pub fn run(&self) -> Result<String, Error> {
208        execute(self, None)
209    }
210
211    /**
212        Run the parsed brainfuck with input.
213
214        # Errors
215        May return a [`MaxSteps`](ErrorKind::MaxSteps) error.
216
217        # Example
218        ```
219        # use brainfrick::Brainfuck;
220        let loud = Brainfuck::parse(",[>++++[<-------->-]<.,]")?;
221
222        assert_eq!(loud.input("foobar")?, "FOOBAR");
223        assert_eq!(loud.input("heck")?, "HECK");
224        assert_eq!(loud.input("aaaaa")?, "AAAAA");
225        # Ok::<(), brainfrick::Error>(())
226        ```
227    */
228    pub fn input<S: AsRef<str>>(&self, input: S) -> Result<String, Error> {
229        execute(self, Some(input.as_ref()))
230    }
231
232    /**
233        The original brainfuck 'source'.
234
235        # Example
236        ```
237        # use brainfrick::Brainfuck;
238        let code = ",[>++++[<-------->-]<.,]";
239        let example = Brainfuck::parse(code)?;
240        assert_eq!(code, example.code());
241        # Ok::<(), brainfrick::Error>(())
242        ```
243    */
244    pub fn code(&self) -> &str {
245        &self.code
246    }
247}
248
249// for people who don't like swearing
250pub use Brainfuck as Brainfrick;