bfmod 0.1.0

Brainfuck lexer and compiler library for Rust.
Documentation

#[derive(Debug)]
pub enum Commands {
    AddByte,
    RemoveByte,
    LoopBegin,
    LoopEnd,
    GoNext,
    GoLast,
    ReadByte,
    WriteByte,
}

#[derive(Debug)]
pub struct Token {
    pub value: Commands,
    pub line: i32,
    pub column: i32,
    pub repeats: i32
}

fn generate_optimization(index: usize, input: &str, ignore_list: &mut Vec<usize>, handle_char: char, give_val: Commands) -> Token {
    let input_slice = &input[index..input.len()];
    let mut total_found = 0;

    for (i, char) in input_slice.chars().enumerate() {
        if char == handle_char {
            total_found += 1;
            ignore_list.push(i+index);
        } else {
            break;
        }
    }

    return Token{
        value: give_val,
        repeats: total_found,
        line: 0,
        column: 0
    }
}

/// Lexerize the input.
///
/// # Arguments
///
/// * `input` (`&str`) - The brainfuck code will be lexerized.
///
/// # Examples
///
/// ```
/// let tokens: Vec<Token> = bfmod::lexer::execute("
///     >++++++++[<+++++++++>-]<.>++++[<+++++++>-]
///     <+.+++++++..+++.>>++++++[<+++++++>-]<++.--
///     ----------.>++++++[<+++++++++>-]<+.<.+++.-
///     -----.--------.>>>++++[<++++++++>-]<+.");
/// ```
pub fn execute(input: &str) -> Vec<Token> {
    let mut tokenized: Vec<Token> = Vec::new();
    let mut line = 0;
    let mut column = 0;
    let mut ignore_list: Vec<usize> = Vec::new();

    for (i, char) in input.chars().enumerate() {
        if ignore_list.contains(&i) {
            continue
        }

        match char {
            '\n' => {
                line += 1;
                column = 0;
            },
            '+' => {
                let mut optimized_token = generate_optimization(i, input, &mut ignore_list, '+', Commands::AddByte);
                optimized_token.line = line;
                optimized_token.column = column;

                tokenized.push(optimized_token)
            },
            '-' => {
                let mut optimized_token = generate_optimization(i, input, &mut ignore_list, '-', Commands::RemoveByte);
                optimized_token.line = line;
                optimized_token.column = column;

                tokenized.push(optimized_token)
            },
            '[' => tokenized.push(Token{
                value: Commands::LoopBegin,
                line: line,
                column: column,
                repeats: 0
            }),
            ']' => tokenized.push(Token{
                value: Commands::LoopEnd,
                line: line,
                column: column,
                repeats: 0
            }),
            '>' => {
                let mut optimized_token = generate_optimization(i, input, &mut ignore_list, '>', Commands::GoNext);
                optimized_token.line = line;
                optimized_token.column = column;

                tokenized.push(optimized_token)
            },
            '<' => {
                let mut optimized_token = generate_optimization(i, input, &mut ignore_list, '<', Commands::GoLast);
                optimized_token.line = line;
                optimized_token.column = column;

                tokenized.push(optimized_token)
            },
            '.' => tokenized.push(Token{
                value: Commands::WriteByte,
                line: line,
                column: column,
                repeats: 0
            }),
            ',' => tokenized.push(Token{
                value: Commands::ReadByte,
                line: line,
                column: column,
                repeats: 0
            }),
            _ => {
                column += 1;
                continue;
            }
        };
        
        column += 1;
    };

    tokenized
}

/// Check brackets for lexerized input.
///
/// # Arguments
///
/// * `tokens` (`&Vec<Token>`) - The lexerized code will be checked.
///
/// # Examples
///
/// ```
/// match bfmod::lexer::check_brackets(&tokens) {
///     Ok(()) => {
///         // ...
///     },
///     Err(msg) => println!(msg)
/// }
/// ```
pub fn check_brackets(tokens: &Vec<Token>) -> Result<(), String> {
    let mut loop_reference_count = 0;
    let (mut open_brackets_line, mut open_brackets_column) = (0, 0);
    let (mut close_brackets_line, mut close_brackets_column) = (0, 0);

    for token in tokens {
        match token.value {
            Commands::LoopBegin => {
                loop_reference_count += 1;
                open_brackets_line = token.line;
                open_brackets_column = token.column;
            },
            Commands::LoopEnd => {
                loop_reference_count -= 1;
                close_brackets_line = token.line;
                close_brackets_column = token.column;
            },
            _ => continue
        };
    };

    if loop_reference_count > 0 {
        return Err(format!("un-closed loop at line {}, column {}.", open_brackets_line, open_brackets_column))
    }

    if loop_reference_count < 0 {
        return Err(format!("closing the undefined loop at line {}, column {}.", close_brackets_line, close_brackets_column))
    }

    Ok(())
}