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
//! A parser from Brainfuck source code to sequences of BrainfuckInstructions.

/// Represents any of the eight standard Brainfuck instructions:
///
/// * `+`
/// * `-`
/// * `>`
/// * `<`
/// * `,`
/// * `.`
/// * `[`
/// * `]`
///
/// as well as any instructions generated by optimizations.
#[derive(Debug, Clone, PartialEq)]
pub enum BrainfuckInstruction {
    /// Add represents some number of Brainfuck `+` instructions.
    Add(u8),
    /// Sub represents some number of Brainfuck `-` instructions.
    Sub(u8),
    /// Right represents some number of Brainfuck '>' instructions.
    Right(usize),
    /// Left represents some number of Brainfuck '<' instructions.
    Left(usize),
    /// Read represents a Brainfuck `,` instruction.
    Read,
    /// Write represents a Brainfuck `.` instruction.
    Write,
    /// Open represents a Brainfuck `[` instruction.
    Open,
    /// Close represents a Brainfuck `]` instruction.
    Close,
    /// Set is an instruction that assigns a cell in the tape to some value.
    Set(u8),
    /// ScanLeft represents the following sequence of Brainfuck instructions: `[<]`
    ScanLeft,
    /// ScanRight represents the following sequence of Brainfuck instructions: `[>]`
    ScanRight,
}

/// Parses a sequence of BrainfuckInstructions from a string.
/// Ignores all non-Brainfuck characters.
///
/// # Arguments
///
/// * `code` - The Brainfuck source code to parse.
pub fn parse_str(code: String) -> Vec<BrainfuckInstruction> {
    parse(&code.chars().collect::<Vec<char>>())
}

/// Parses a sequence of BrainfuckInstructions from a slice of characters.
/// Ignores all non-Brainfuck characters.
///
/// # Arguments
///
/// * `code` - The Brainfuck source code to parse.
pub fn parse(code: &[char]) -> Vec<BrainfuckInstruction> {
    let mut result = Vec::new();

    let mut index = 0;
    while index < code.len() {
        let c = code[index];
        index += 1;
        match c {
            '+' => result.push(BrainfuckInstruction::Add(1)),
            '-' => result.push(BrainfuckInstruction::Sub(1)),
            '>' => result.push(BrainfuckInstruction::Right(1)),
            '<' => result.push(BrainfuckInstruction::Left(1)),
            ',' => result.push(BrainfuckInstruction::Read),
            '.' => result.push(BrainfuckInstruction::Write),
            '[' => result.push(BrainfuckInstruction::Open),
            ']' => result.push(BrainfuckInstruction::Close),
            _ => {}
        }
    }

    result
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parse_str_parses_brainfuck_instructions() {
        let code = "++--,.[]<<>> [-]";

        let result = parse_str(code.to_string());

        assert_eq!(
            result,
            vec![
                BrainfuckInstruction::Add(1),
                BrainfuckInstruction::Add(1),
                BrainfuckInstruction::Sub(1),
                BrainfuckInstruction::Sub(1),
                BrainfuckInstruction::Read,
                BrainfuckInstruction::Write,
                BrainfuckInstruction::Open,
                BrainfuckInstruction::Close,
                BrainfuckInstruction::Left(1),
                BrainfuckInstruction::Left(1),
                BrainfuckInstruction::Right(1),
                BrainfuckInstruction::Right(1),
                BrainfuckInstruction::Open,
                BrainfuckInstruction::Sub(1),
                BrainfuckInstruction::Close
            ]
        );
    }

    #[test]
    fn parse_parses_brainfuck_instructions() {
        let code = "++--,.[]<<>> [-]";

        let result = parse(&code.chars().collect::<Vec<char>>());

        assert_eq!(
            result,
            vec![
                BrainfuckInstruction::Add(1),
                BrainfuckInstruction::Add(1),
                BrainfuckInstruction::Sub(1),
                BrainfuckInstruction::Sub(1),
                BrainfuckInstruction::Read,
                BrainfuckInstruction::Write,
                BrainfuckInstruction::Open,
                BrainfuckInstruction::Close,
                BrainfuckInstruction::Left(1),
                BrainfuckInstruction::Left(1),
                BrainfuckInstruction::Right(1),
                BrainfuckInstruction::Right(1),
                BrainfuckInstruction::Open,
                BrainfuckInstruction::Sub(1),
                BrainfuckInstruction::Close
            ]
        );
    }
}