use crate::bytes::token::Token;
use crate::errors::ParseResult;
use crate::peek::{PeekResult, Peekable};
use crate::recognizer::Recognizable;
use crate::scanner::Scanner;
pub fn match_for_balanced_group<'a, V1, T1, V2, T2>(
tokenizer: &mut Scanner<'a, u8>,
balance: &mut usize,
start: T1,
end: T2,
) -> ParseResult<()>
where
T1: Recognizable<'a, u8, V1> + Copy,
T2: Recognizable<'a, u8, V2> + Copy,
{
match start.recognize(tokenizer)? {
None => match end.recognize(tokenizer)? {
Some(_end_token) => *balance -= 1,
None => {
tokenizer.bump_by(1);
return Ok(());
}
},
Some(_start_token) => *balance += 1,
};
Ok(())
}
pub fn match_group<'a, V1, T1, V2, T2>(
start: T1,
end: T2,
) -> impl Fn(&'a [u8]) -> ParseResult<PeekResult<T1, T2>> + 'a
where
T1: Recognizable<'a, u8, V1> + Copy + 'a,
T2: Recognizable<'a, u8, V2> + Copy + 'a,
{
move |input: &'a [u8]| {
let mut balance = 0;
let mut tokenizer = Scanner::new(input);
loop {
match_for_balanced_group(&mut tokenizer, &mut balance, start, end)?;
if balance == 0 {
break;
}
}
if tokenizer.current_position() == 1 {
return Ok(PeekResult::NotFound);
}
Ok(PeekResult::Found {
end_slice: tokenizer.current_position(),
start,
end,
})
}
}
pub fn match_for_delimited_group<'a, V, T, V2, T2>(
token: T,
escape_token: T2,
) -> impl Fn(&'a [u8]) -> ParseResult<PeekResult<T, T>> + 'a
where
T: Recognizable<'a, u8, V> + Copy + 'a,
T2: Recognizable<'a, u8, V2> + Copy + 'a,
{
move |input: &'a [u8]| {
if input.len() < token.size() * 2 {
return Ok(PeekResult::NotFound);
}
let mut tokenizer = Scanner::new(input);
if token.recognize(&mut tokenizer)?.is_none() {
return Ok(PeekResult::NotFound);
}
tokenizer.bump_by(token.size());
let mut found = false;
while !tokenizer.remaining().is_empty() {
if token.recognize(&mut tokenizer)?.is_some() {
let mut rewind_tokenizer = Scanner::new(
&tokenizer.data()
[tokenizer.current_position() - token.size() - escape_token.size()..],
);
if escape_token.recognize(&mut rewind_tokenizer)?.is_some() {
continue;
}
found = true;
break;
}
tokenizer.bump_by(1);
}
if !found {
return Ok(PeekResult::NotFound);
}
Ok(PeekResult::Found {
end_slice: tokenizer.current_position(),
start: token,
end: token,
})
}
}
pub enum GroupKind {
Parenthesis,
Quotes,
DoubleQuotes,
}
impl GroupKind {
fn matcher<'a>(&self) -> Box<dyn Fn(&'a [u8]) -> ParseResult<PeekResult<Token, Token>> + 'a>
where {
match self {
GroupKind::Parenthesis => Box::new(match_group(Token::OpenParen, Token::CloseParen)),
GroupKind::Quotes => {
Box::new(match_for_delimited_group(Token::Quote, Token::Backslash))
}
GroupKind::DoubleQuotes => Box::new(match_for_delimited_group(
Token::DoubleQuote,
Token::Backslash,
)),
}
}
}
impl<'a> Peekable<'a, u8, Token, Token> for GroupKind {
fn peek(&self, data: &Scanner<'a, u8>) -> ParseResult<PeekResult<Token, Token>> {
self.matcher()(data.remaining())
}
}
#[cfg(test)]
mod tests {
use crate::bytes::components::groups::{GroupKind, match_for_delimited_group, match_group};
use crate::bytes::token::Token;
use crate::peek::{PeekResult, Peeking, peek};
use crate::scanner::Scanner;
#[test]
fn test_match_group() {
let data = b"( 5 + 3 - ( 10 * 8 ) ) + 54";
let result =
match_group(Token::OpenParen, Token::CloseParen)(data).expect("failed to parse");
assert_eq!(
result,
PeekResult::Found {
end_slice: 22,
start: Token::OpenParen,
end: Token::CloseParen
}
);
assert_eq!(&data[..22], b"( 5 + 3 - ( 10 * 8 ) )");
}
#[test]
fn test_match_group_delimited() {
let data = b"( 5 + 3 - ( 10 * 8 ) ) + 54";
let mut tokenizer = Scanner::new(data);
let result = peek(GroupKind::Parenthesis, &mut tokenizer).expect("failed to parse");
assert_eq!(
result,
Some(Peeking {
start: Token::OpenParen,
end: Token::CloseParen,
data: &data[0..22],
end_slice: 22
})
);
assert_eq!(&data[..22], b"( 5 + 3 - ( 10 * 8 ) )");
}
#[test]
fn test_match_quotes() {
let data = b"'hello world' data";
let result = match_for_delimited_group(Token::Quote, Token::Backslash)(data)
.expect("failed to parse");
assert_eq!(
result,
PeekResult::Found {
end_slice: 13,
start: Token::Quote,
end: Token::Quote
}
);
assert_eq!(&data[..13], b"'hello world'");
let data = r#"'hello world l\'éléphant' data"#;
let result = match_for_delimited_group(Token::Quote, Token::Backslash)(data.as_bytes())
.expect("failed to parse");
assert_eq!(
result,
PeekResult::Found {
end_slice: 27,
start: Token::Quote,
end: Token::Quote
}
);
assert_eq!(&data[..27], r#"'hello world l\'éléphant'"#);
let data = "\"hello world\" data";
let result =
match_for_delimited_group(Token::DoubleQuote, Token::Backslash)(data.as_bytes())
.expect("failed to parse");
assert_eq!(
result,
PeekResult::Found {
end_slice: 13,
start: Token::DoubleQuote,
end: Token::DoubleQuote
}
);
assert_eq!(&data[..13], "\"hello world\"");
let data = r#""hello world" data"#;
let result =
match_for_delimited_group(Token::DoubleQuote, Token::Backslash)(data.as_bytes())
.expect("failed to parse");
assert_eq!(
result,
PeekResult::Found {
end_slice: 13,
start: Token::DoubleQuote,
end: Token::DoubleQuote
}
);
assert_eq!(&data[..13], r#""hello world""#);
}
}