diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index e274b10..b3dec9f 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -1,6 +1,6 @@ name: changelog.yml on: - pull_request: + push: branches: - main diff --git a/Readme.md b/Readme.md index 709ad26..208ae62 100644 --- a/Readme.md +++ b/Readme.md @@ -589,4 +589,49 @@ fn main() { } ``` -An example of the peeking usage is available in the [expression](examples/expression.rs) example. \ No newline at end of file +An example of the peeking usage is available in the [expression](examples/expression.rs) example. + +## Separated List + +The `SeparatedList` component is used to parse a list of elements separated by a separator. + +If you have this expression: "1 + 2 + 3 + 4", you want to get all the numbers. + +The data are separated by the ` + ` pattern. + +The `SeparatedList` takes two `Visitor` as type parameters: + +- The element visitor : the one that will be used to parse each element of the list +- The separator visitor : the one that will be used to parse the separator between each element of the list + +Once one of parsers fails, the `SeparatedList` will stop parsing the list and return the result. + +```rust +use noa_parser::bytes::primitives::number::Number; +use noa_parser::bytes::token::Token; +use noa_parser::errors::ParseResult; +use noa_parser::recognizer::recognize; +use noa_parser::scanner::Scanner; +use noa_parser::separated_list::SeparatedList; +use noa_parser::visitor::Visitor; + +#[derive(Debug)] +struct Separator; + +impl<'a> Visitor<'a, u8> for Separator { + fn accept(scanner: &mut noa_parser::scanner::Scanner) -> ParseResult { + recognize(Token::Tilde, scanner)?; + recognize(Token::Tilde, scanner)?; + recognize(Token::Tilde, scanner)?; + Ok(Separator) + } +} + +fn main() { + let data = b"1~~~2~~~3~~~4"; + let mut scanner = Scanner::new(data); + let result = + SeparatedList::, Separator>::accept(&mut scanner).map(|x| x.data); + println!("{:?}", result); // Ok([Number(1), Number(2), Number(3), Number(4)]) +} +``` \ No newline at end of file diff --git a/examples/separated_list.rs b/examples/separated_list.rs new file mode 100644 index 0000000..2567b07 --- /dev/null +++ b/examples/separated_list.rs @@ -0,0 +1,27 @@ +use noa_parser::bytes::primitives::number::Number; +use noa_parser::bytes::token::Token; +use noa_parser::errors::ParseResult; +use noa_parser::recognizer::recognize; +use noa_parser::scanner::Scanner; +use noa_parser::separated_list::SeparatedList; +use noa_parser::visitor::Visitor; + +#[derive(Debug)] +struct Separator; + +impl<'a> Visitor<'a, u8> for Separator { + fn accept(scanner: &mut noa_parser::scanner::Scanner) -> ParseResult { + recognize(Token::Tilde, scanner)?; + recognize(Token::Tilde, scanner)?; + recognize(Token::Tilde, scanner)?; + Ok(Separator) + } +} + +fn main() { + let data = b"1~~~2~~~3~~~4"; + let mut scanner = Scanner::new(data); + let result = + SeparatedList::, Separator>::accept(&mut scanner).map(|x| x.data); + println!("{:?}", result); // Ok([Number(1), Number(2), Number(3), Number(4)]) +} diff --git a/src/separated_list.rs b/src/separated_list.rs index 361941c..6b166f2 100644 --- a/src/separated_list.rs +++ b/src/separated_list.rs @@ -3,8 +3,9 @@ use crate::scanner::Scanner; use crate::visitor::Visitor; use std::marker::PhantomData; +#[derive(Debug)] pub struct SeparatedList { - pub(crate) data: Vec, + pub data: Vec, separator: PhantomData<(S, T)>, } @@ -92,6 +93,14 @@ where let mut elements = vec![]; let cursor = scanner.current_position(); + // if the scanner is empty, return an empty list + if scanner.remaining().is_empty() { + return Ok(SeparatedList { + data: elements, + separator: PhantomData, + }); + } + loop { if let Ok(result) = yield_element::(scanner) { let element: YieldResult = result; @@ -154,5 +163,12 @@ mod tests { vec![Number(12), Number(4), Number(78), Number(22)] ); assert_eq!(scanner.current_position(), 10); + + let data = b""; + let mut scanner = Scanner::new(data); + let result = scanner + .visit::, SeparatorComma>>() + .expect("failed to parse"); + assert_eq!(result.data, vec![]); } }