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
//! Definitions common to multiple passes.
//!
//! This includes error handling and the basic definition of Brainfuck commands.

use std::fmt;

/// The result type for Brainfuck operations that can fail.
///
/// This is `Result` specialized to the four kinds of Brainfuck
/// [`Error`](enum.Error.html)s
pub type BfResult<T> = Result<T, Error>;

/// The static and dynamic errors that can happen in Brainfuck.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
    /// Unmatched ‘[’ (syntax error)
    UnmatchedBegin,
    /// Unmatched ‘]’ (syntax error)
    UnmatchedEnd,
    /// If execution continues, the pointer will go below 0 (run-time error)
    PointerUnderflow,
    /// If execution continues, the pointer will go beyond the high end of the
    /// memory (run-time error)
    PointerOverflow,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;

        match *self {
            UnmatchedBegin => write!(f, "unmatched ‘[’"),
            UnmatchedEnd => write!(f, "unmatched ‘]’"),
            PointerUnderflow => write!(f, "pointer underflow"),
            PointerOverflow => write!(f, "pointer overflow"),
        }
    }
}

/// The eight Brainfuck commands.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum Command {
    /// `>`: Increment the data pointer.
    Right,
    /// `<`: Decrement the data pointer.
    Left,
    /// `+`: Increment the byte value at the data pointer.
    Up,
    /// `-`: Decrement the byte value at the data pointer.
    Down,
    /// `,`: Read a byte from the standard input.
    In,
    /// `.`: Write a byte to the standard output.
    Out,
    /// `[`: Begin a loop, which executes if the byte at the pointer is non-zero.
    Begin,
    /// `]`: End a loop, which repeats if the byte at the pointer is non-zero.
    End,
}

#[cfg(not(any(feature = "u16count", feature = "u32count")))]
/// The number of times to repeat a command when run-length encoded.
///
/// This can be changed to `u16` or `u32` by enabling the `u16count` or `u32count` features,
/// respectively. Making `Count` smaller may make bytecode instructions to be smaller, which
/// may enable more bytecode to fit in cache.
pub type Count = usize;

#[cfg(feature = "u16count")]
/// The number of times to repeat a command when run-length encoded.
///
/// This type is `usize` by default, but was set to `u16` by enabling the `u16count` feature. It
/// can also be set to `u32` via the `u32count` feature.
pub type Count = u16;

#[cfg(feature = "u32count")]
/// The number of times to repeat a command when run-length encoded.
///
/// This type is `usize` by default, but was set to `u32` by enabling the `u32count` feature. It
/// can also be set to `u16` via the `u16count` feature.
pub type Count = u32;

/// Instructions as output by the bytecode flattener.
///
/// These include the result of peephole optimizations that turn sequences of Brainfuck commands
/// into non-Brainfuck instructions that are understood by the appropriate interpreters and the
/// JIT compiler.
///
/// Unlike in the earlier passes, the loop instructions
/// do not include a boxed slice of instructions as a
/// subtree. Note that this type is `Copy`.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Instruction {
    /// Decrease the pointer by the specified offset.
    Left(Count),
    /// Increase the pointer by the specified offset.
    Right(Count),
    /// Increase the current byte value by the specified offset.
    Add(u8),
    /// Read a byte of input.
    In,
    /// Write a byte of output.
    Out,
    /// Begin a loop, jumping to the end if the current byte value is 0.
    ///
    /// The `Count` is the address of the matching `JumpNotZero` instruction.
    JumpZero(Count),
    /// End a loop if the current byte value is 0; otherwise repeat the loop.
    ///
    /// The `Count` is the address of the matching `JumpZero` instruction.
    JumpNotZero(Count),
    /// Set the current byte value to 0.
    ///
    /// Equivalent to the concrete Braincode loop `[-]`.
    SetZero,
    /// Add the byte at the pointer to the byte at the specified offset and zero the byte at the
    /// pointer.
    ///
    /// `OffsetAddRight(5)` is equivalent to the concrete Brainfuck loop `[->>>>>+<<<<<]`.
    OffsetAddRight(Count),
    /// Add the byte at the pointer to the byte at the specified offset and zero the byte at the
    /// pointer.
    ///
    /// `OffsetAddRight(5)` is equivalent to the concrete Brainfuck loop `[-<<<<<+>>>>>]`.
    OffsetAddLeft(Count),
    /// Finds the nearest zero to the left that appears offset by a multiple of the given `Count`.
    ///
    /// `FindZeroRight(3)` is equivalent to the concrete Brainfuck loop `[>>>]`.
    FindZeroRight(Count),
    /// Finds the nearest zero to the right that appears offset by a multiple of the given `Count`.
    ///
    /// `FindZeroLeft(3)` is equivalent to the concrete Brainfuck loop `[<<<]`.
    FindZeroLeft(Count),
}