ram 8.0.0

A library that helps to parse source code with finite state machines
Documentation
use ram::*;
use regex::Regex;

#[cfg(test)]
enum TokenType {
    StringSeparator,
    StringSingleQuoteContent,
    StringDoubleQuoteContent,
    StringJoined,
    Whitespace,
    End,
}

#[cfg(test)]
fn automaton_string_quoted_single() -> Automaton {
    let mut am = Automaton::new(0, 3);

    am.find_regex(
        TokenType::StringSeparator as i32,
        0,
        1,
        Regex::new("'").unwrap(),
    );
    am.find_regex(
        TokenType::StringSingleQuoteContent as i32,
        1,
        2,
        Regex::new("[^']*").unwrap(),
    );
    am.find_regex(
        TokenType::StringSeparator as i32,
        2,
        3,
        Regex::new("'").unwrap(),
    );

    am
}

#[cfg(test)]
fn automaton_string_quoted_double() -> Automaton {
    let mut am = Automaton::new(0, 3);

    am.find_regex(
        TokenType::StringSeparator as i32,
        0,
        1,
        Regex::new("\"").unwrap(),
    );
    am.find_regex(
        TokenType::StringDoubleQuoteContent as i32,
        1,
        2,
        Regex::new("(\\\\\"|[^\"])*").unwrap(),
    );
    am.find_regex(
        TokenType::StringSeparator as i32,
        2,
        3,
        Regex::new("\"").unwrap(),
    );
    am
}

#[cfg(test)]
fn automaton_string() -> Automaton {
    let mut am = Automaton::new(0, 1);

    am.find_automaton(0, 1, automaton_string_quoted_single());
    am.find_automaton(0, 1, automaton_string_quoted_double());

    am
}

#[cfg(test)]
fn automaton_shell() -> Automaton {
    let mut am = Automaton::new(0, 1);

    am.find_end(TokenType::End as i32, 0, 1);
    am.find_whitespace(TokenType::Whitespace as i32, 0, 1);
    am.find_automaton(0, 1, automaton_string());

    am
}

#[cfg(test)]
fn automaton_shell_rec() -> Automaton {
    let mut am = Automaton::new(0, 2);

    am.find_end(TokenType::End as i32, 0, 2);
    am.find_whitespace(TokenType::Whitespace as i32, 0, 1);
    am.find_automaton(0, 1, automaton_string())
        .join_tokens(TokenType::StringJoined as i32);
    am.find_me(1, 2);

    am
}

#[cfg(test)]
fn get_test_source_code() -> String {
    "'That thing'  \t  \"Hello\\\"'stuff\"".to_string()
}

#[cfg(test)]
fn test_finder(_runner: &mut Runner, _finder: &Finder) -> bool {
    false
}

#[test]
fn test_find_custom() {
    let source_code = get_test_source_code();

    let mut am = Automaton::new(0, 1);
    am.find_custom(0, 0, 1, test_finder);

    let runner = am.run(source_code);

    assert!(!runner.completed());
}

#[test]
fn test_rec() {
    let source_code = get_test_source_code();

    // recursive version
    let am = automaton_shell_rec();
    let runner = am.run(source_code);

    assert!(runner.tokens[0].text == "'That thing'".to_string());
    assert!(runner.tokens[1].text == "  \t  ".to_string());
    assert!(runner.tokens[2].text == "\"Hello\\\"'stuff\"".to_string());
    assert!(runner.tokens[3].text == "".to_string());

    assert!(runner.tokens[0].type_id == TokenType::StringJoined as i32);
    assert!(runner.tokens[1].type_id == TokenType::Whitespace as i32);
    assert!(runner.tokens[2].type_id == TokenType::StringJoined as i32);
    assert!(runner.tokens[3].type_id == TokenType::End as i32);

    assert!(runner.completed());
}

#[test]
fn test_ite() {
    let source_code = get_test_source_code();

    // iterative version
    let am = automaton_shell();
    let runner = am.run_loop(source_code);

    assert!(runner.tokens[0].text == "'".to_string());
    assert!(runner.tokens[1].text == "That thing".to_string());
    assert!(runner.tokens[2].text == "'".to_string());
    assert!(runner.tokens[3].text == "  \t  ".to_string());
    assert!(runner.tokens[4].text == "\"".to_string());
    assert!(runner.tokens[5].text == "Hello\\\"'stuff".to_string());
    assert!(runner.tokens[6].text == "\"".to_string());
    assert!(runner.tokens[7].text == "".to_string());

    assert!(runner.tokens[0].type_id == TokenType::StringSeparator as i32);
    assert!(runner.tokens[1].type_id == TokenType::StringSingleQuoteContent as i32);
    assert!(runner.tokens[2].type_id == TokenType::StringSeparator as i32);
    assert!(runner.tokens[3].type_id == TokenType::Whitespace as i32);
    assert!(runner.tokens[4].type_id == TokenType::StringSeparator as i32);
    assert!(runner.tokens[5].type_id == TokenType::StringDoubleQuoteContent as i32);
    assert!(runner.tokens[6].type_id == TokenType::StringSeparator as i32);
    assert!(runner.tokens[7].type_id == TokenType::End as i32);

    assert!(runner.completed());
}