tuck5 0.2.0

A pragmatic lexer/parser generator
Documentation
use std::marker::PhantomData;

use crate::*;
use token::*;

use self::transform::Transform;

#[cfg(test)]
mod tests;

pub trait Sequence<T> {
    fn match_tokens(&self, tokens: &[&Token<T>]) -> Option<usize>;
}

pub fn match_all_tokens<T>(seq: &impl Sequence<T>, tokens: &[&Token<T>]) -> bool {
    Some(tokens.len()) == seq.match_tokens(tokens)
}

pub fn test_and_transform<T>(
    seq: &dyn Sequence<T>,
    transform: &dyn Transform<T>,
    tokens: &mut Vec<Token<'_, T>>,
    start_index: usize,
) -> Option<usize> {
    let test_result = seq.match_tokens(&tokens[start_index..].iter().collect::<Vec<_>>());
    if let Some(len) = test_result {
        let end_index = start_index + len;
        let new_tox = transform.transform(tokens.drain(start_index..end_index).collect());
        let new_len = new_tox.len();
        tokens.splice(start_index..start_index, new_tox);
        Some(new_len)
    } else {
        None
    }
}

pub fn replace_first_match<T>(
    seq: &dyn Sequence<T>,
    transform: &dyn Transform<T>,
    tokens: &mut Vec<Token<'_, T>>,
) -> bool {
    let mut start_index = 0usize;
    while start_index < tokens.len() {
        let inc = test_and_transform(seq, transform, tokens, start_index);
        if inc.is_some() {
            return true;
        } else {
            start_index += 1;
        }
    }
    false
}

pub fn replace_all_matches<T>(
    seq: &dyn Sequence<T>,
    transform: &dyn Transform<T>,
    tokens: &mut Vec<Token<'_, T>>,
) -> bool {
    let mut changed = false;
    while replace_first_match(seq, transform, tokens) {
        changed = true;
    }
    changed
}

pub fn replace_all_matches_once<T>(
    seq: &dyn Sequence<T>,
    transform: &dyn Transform<T>,
    tokens: &mut Vec<Token<'_, T>>,
) -> bool {
    let mut start_index = 0usize;
    let mut changed = false;
    while start_index < tokens.len() {
        let inc = test_and_transform(seq, transform, tokens, start_index);
        if let Some(match_size) = inc {
            start_index += match_size;
            changed = true;
        } else {
            start_index += 1;
        }
    }
    changed
}

pub fn assert_match(seq: impl Sequence<()>, text: &str, should_match: bool) {
    let tox = Token::token_vec_from_str(text, &|_, _| ());
    assert_eq!(
        match_all_tokens(&seq, tox.iter().collect::<Vec<&Token<()>>>().as_slice()),
        should_match
    );
}

#[derive(Clone)]
pub struct RawSeq<T> {
    pub text: String,
    _t: PhantomData<T>,
}

impl<T> RawSeq<T> {
    pub fn new(text: &str) -> RawSeq<T> {
        RawSeq {
            text: text.to_string(),
            _t: PhantomData,
        }
    }

    pub fn new_from_owned(text: String) -> RawSeq<T> {
        RawSeq {
            text: text.to_string(),
            _t: PhantomData,
        }
    }
}

impl<T> Sequence<T> for RawSeq<T> {
    fn match_tokens(&self, tokens: &[&Token<T>]) -> Option<usize> {
        if tokens.get(0)?.content() == self.text {
            Some(1)
        } else {
            None
        }
    }
}

#[derive(Clone)]
pub struct FirstTokenSeq<T, F>
where
    F: Fn(&Token<'_, T>) -> bool,
{
    pub predicate: F,
    _t: PhantomData<T>,
}

impl<T, F> FirstTokenSeq<T, F>
where
    F: Fn(&Token<'_, T>) -> bool,
{
    pub fn new(predicate: F) -> FirstTokenSeq<T, F> {
        FirstTokenSeq {
            predicate,
            _t: PhantomData,
        }
    }
}

impl<T, F> Sequence<T> for FirstTokenSeq<T, F>
where
    F: Fn(&Token<'_, T>) -> bool,
{
    fn match_tokens(&self, tokens: &[&Token<T>]) -> Option<usize> {
        if (self.predicate)(tokens.get(0)?) {
            Some(1)
        } else {
            None
        }
    }
}

pub struct ChooseSeq<T> {
    pub options: Vec<Box<dyn Sequence<T>>>,
}

impl<T: 'static> ChooseSeq<T> {
    pub fn new(options: Vec<Box<dyn Sequence<T>>>) -> ChooseSeq<T> {
        ChooseSeq { options }
    }

    pub fn from_str(text: &str) -> ChooseSeq<T> {
        ChooseSeq {
            options: text
                .chars()
                .map(|c| Box::new(RawSeq::new_from_owned(format!("{c}"))) as Box<dyn Sequence<T>>)
                .collect(),
        }
    }
}

impl<T> Sequence<T> for ChooseSeq<T> {
    fn match_tokens(&self, tokens: &[&Token<T>]) -> Option<usize> {
        self.options.iter().find_map(|seq| seq.match_tokens(tokens))
    }
}

pub struct OptionalSeq<T> {
    pub option: Box<dyn Sequence<T>>,
}

impl<T: 'static> OptionalSeq<T> {
    pub fn new(option: Box<dyn Sequence<T>>) -> OptionalSeq<T> {
        OptionalSeq { option }
    }
}

impl<T> Sequence<T> for OptionalSeq<T> {
    fn match_tokens(&self, tokens: &[&Token<T>]) -> Option<usize> {
        match self.option.match_tokens(tokens) {
            None => Some(0),
            Some(len) => Some(len),
        }
    }
}

pub struct RepeatedSeq<T> {
    pub to_repeat: Box<dyn Sequence<T>>,
}

impl<T> RepeatedSeq<T> {
    pub fn new(to_repeat: Box<dyn Sequence<T>>) -> RepeatedSeq<T> {
        RepeatedSeq { to_repeat }
    }
}

impl<T> Sequence<T> for RepeatedSeq<T> {
    fn match_tokens(&self, tokens: &[&Token<T>]) -> Option<usize> {
        let mut index = 0usize;
        loop {
            if index > tokens.len() {
                return Some(tokens.len());
            }

            let res = self.to_repeat.match_tokens(&tokens[index..]);
            if let Some(len) = res {
                index += len;
            } else {
                return Some(index);
            }
        }
    }
}

pub struct MultipleSeq<T> {
    pub seqs: Vec<Box<dyn Sequence<T>>>,
}

impl<T> MultipleSeq<T> {
    pub fn new(seqs: Vec<Box<dyn Sequence<T>>>) -> MultipleSeq<T> {
        MultipleSeq { seqs }
    }
}

impl<T> Sequence<T> for MultipleSeq<T> {
    fn match_tokens(&self, tokens: &[&Token<T>]) -> Option<usize> {
        let mut index = 0usize;
        for seq in &self.seqs {
            if index > tokens.len() {
                return None;
            }

            let res = seq.match_tokens(&tokens[index..]);

            if let Some(len) = res {
                index += len;
            } else {
                return None;
            }
        }
        Some(index)
    }
}