aboutsummaryrefslogtreecommitdiffstats
path: root/src/construct/partial_space_or_tab.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/construct/partial_space_or_tab.rs')
-rw-r--r--src/construct/partial_space_or_tab.rs218
1 files changed, 127 insertions, 91 deletions
diff --git a/src/construct/partial_space_or_tab.rs b/src/construct/partial_space_or_tab.rs
index 8df7601..d2934b3 100644
--- a/src/construct/partial_space_or_tab.rs
+++ b/src/construct/partial_space_or_tab.rs
@@ -1,4 +1,4 @@
-//! Several helpers to parse whitespace (`space_or_tab`).
+//! Several helpers to parse whitespace (`space_or_tab`, `space_or_tab_eol`).
//!
//! ## References
//!
@@ -7,7 +7,7 @@
use crate::subtokenize::link;
use crate::tokenizer::{Code, ContentType, State, StateFn, StateFnResult, TokenType, Tokenizer};
-/// Options to parse whitespace.
+/// Options to parse `space_or_tab`.
#[derive(Debug)]
pub struct Options {
/// Minimum allowed characters (inclusive).
@@ -16,28 +16,22 @@ pub struct Options {
pub max: usize,
/// Token type to use for whitespace events.
pub kind: TokenType,
- /// To do.
- pub content_type: Option<ContentType>,
+ /// Connect this whitespace to the previous.
pub connect: bool,
-}
-
-#[derive(Debug)]
-pub struct OneLineEndingOptions {
- /// To do.
+ /// Embedded content type to use.
pub content_type: Option<ContentType>,
- pub connect: bool,
}
-/// Options to parse whitespace.
+/// Options to parse `space_or_tab` and one optional eol, but no blank line.
#[derive(Debug)]
-struct OneLineInfo {
- /// Whether something was seen.
- connect: bool,
- /// Configuration.
- options: OneLineEndingOptions,
+pub struct EolOptions {
+ /// Connect this whitespace to the previous.
+ pub connect: bool,
+ /// Embedded content type to use.
+ pub content_type: Option<ContentType>,
}
-/// Options to parse whitespace.
+/// State needed to parse `space_or_tab`.
#[derive(Debug)]
struct Info {
/// Current size.
@@ -46,6 +40,17 @@ struct Info {
options: Options,
}
+/// State needed to parse `space_or_tab_eol`.
+#[derive(Debug)]
+struct EolInfo {
+ /// Whether to connect the next whitespace to the event before.
+ connect: bool,
+ /// Whether there was initial whitespace.
+ ok: bool,
+ /// Configuration.
+ options: EolOptions,
+}
+
/// One or more `space_or_tab`.
///
/// ```bnf
@@ -55,7 +60,7 @@ pub fn space_or_tab() -> Box<StateFn> {
space_or_tab_min_max(1, usize::MAX)
}
-/// Between `x` and `y` `space_or_tab`
+/// Between `x` and `y` `space_or_tab`.
///
/// ```bnf
/// space_or_tab_min_max ::= x*y( ' ' '\t' )
@@ -70,16 +75,57 @@ pub fn space_or_tab_min_max(min: usize, max: usize) -> Box<StateFn> {
})
}
-/// Between `x` and `y` `space_or_tab`, with the given token type.
+/// `space_or_tab`, with the given options.
+pub fn space_or_tab_with_options(options: Options) -> Box<StateFn> {
+ Box::new(|t, c| start(t, c, Info { size: 0, options }))
+}
+
+/// `space_or_tab`, or optionally `space_or_tab`, one `eol`, and
+/// optionally `space_or_tab`.
///
/// ```bnf
-/// space_or_tab ::= x*y( ' ' '\t' )
+/// space_or_tab_eol ::= 1*( ' ' '\t' ) | 0*( ' ' '\t' ) eol 0*( ' ' '\t' )
/// ```
-pub fn space_or_tab_with_options(options: Options) -> Box<StateFn> {
- Box::new(|t, c| start(t, c, Info { size: 0, options }))
+pub fn space_or_tab_eol() -> Box<StateFn> {
+ space_or_tab_eol_with_options(EolOptions {
+ content_type: None,
+ connect: false,
+ })
}
-/// Before whitespace.
+/// `space_or_tab_eol`, with the given options.
+pub fn space_or_tab_eol_with_options(options: EolOptions) -> Box<StateFn> {
+ Box::new(move |tokenizer, code| {
+ let mut info = EolInfo {
+ connect: false,
+ ok: false,
+ options,
+ };
+
+ tokenizer.attempt(
+ space_or_tab_with_options(Options {
+ kind: TokenType::SpaceOrTab,
+ min: 1,
+ max: usize::MAX,
+ content_type: info.options.content_type,
+ connect: info.options.connect,
+ }),
+ move |ok| {
+ if ok {
+ info.ok = ok;
+
+ if info.options.content_type.is_some() {
+ info.connect = true;
+ }
+ }
+
+ Box::new(|t, c| after_space_or_tab(t, c, info))
+ },
+ )(tokenizer, code)
+ })
+}
+
+/// Before `space_or_tab`.
///
/// ```markdown
/// alpha| bravo
@@ -109,7 +155,7 @@ fn start(tokenizer: &mut Tokenizer, code: Code, mut info: Info) -> StateFnResult
}
}
-/// In whitespace.
+/// In `space_or_tab`.
///
/// ```markdown
/// alpha |bravo
@@ -136,85 +182,75 @@ fn inside(tokenizer: &mut Tokenizer, code: Code, mut info: Info) -> StateFnResul
}
}
-pub fn space_or_tab_one_line_ending() -> Box<StateFn> {
- space_or_tab_one_line_ending_with_options(OneLineEndingOptions {
- content_type: None,
- connect: false,
- })
-}
-
-pub fn space_or_tab_one_line_ending_with_options(options: OneLineEndingOptions) -> Box<StateFn> {
- Box::new(move |tokenizer, code| {
- let mut info = OneLineInfo {
- connect: false,
- options,
- };
-
- tokenizer.attempt(
- space_or_tab_with_options(Options {
- kind: TokenType::SpaceOrTab,
- min: 1,
- max: usize::MAX,
- content_type: info.options.content_type,
- connect: info.options.connect,
- }),
- move |ok| {
- if ok && info.options.content_type.is_some() {
- info.connect = true;
- }
-
- Box::new(move |tokenizer, code| match code {
- Code::CarriageReturnLineFeed | Code::Char('\r' | '\n') => {
- at_eol(tokenizer, code, info)
- }
- _ => {
- if ok {
- (State::Ok, Some(vec![code]))
- } else {
- (State::Nok, None)
- }
- }
- })
- },
- )(tokenizer, code)
- })
-}
-
-fn at_eol(tokenizer: &mut Tokenizer, code: Code, mut info: OneLineInfo) -> StateFnResult {
+/// `space_or_tab_eol`: after optionally first `space_or_tab`.
+///
+/// ```markdown
+/// alpha |
+/// bravo
+/// ```
+///
+/// ```markdown
+/// alpha|
+/// bravo
+/// ```
+fn after_space_or_tab(tokenizer: &mut Tokenizer, code: Code, mut info: EolInfo) -> StateFnResult {
match code {
Code::CarriageReturnLineFeed | Code::Char('\r' | '\n') => {
tokenizer.enter_with_content(TokenType::LineEnding, info.options.content_type);
- if info.options.content_type.is_some() {
- if info.connect {
- let index = tokenizer.events.len() - 1;
- link(&mut tokenizer.events, index);
- } else {
- info.connect = true;
- }
+ if info.connect {
+ let index = tokenizer.events.len() - 1;
+ link(&mut tokenizer.events, index);
+ } else if info.options.content_type.is_some() {
+ info.connect = true;
}
tokenizer.consume(code);
tokenizer.exit(TokenType::LineEnding);
- (
- State::Fn(Box::new(tokenizer.attempt_opt(
- space_or_tab_with_options(Options {
- kind: TokenType::SpaceOrTab,
- min: 1,
- max: usize::MAX,
- content_type: info.options.content_type,
- connect: info.connect,
- }),
- after_eol,
- ))),
- None,
- )
+ (State::Fn(Box::new(|t, c| after_eol(t, c, info))), None)
}
- _ => unreachable!("expected eol"),
+ _ if info.ok => (State::Ok, Some(vec![code])),
+ _ => (State::Nok, None),
}
}
-fn after_eol(_tokenizer: &mut Tokenizer, code: Code) -> StateFnResult {
+/// `space_or_tab_eol`: after eol.
+///
+/// ```markdown
+/// alpha
+/// |bravo
+/// ```
+///
+/// ```markdown
+/// alpha
+/// |bravo
+/// ```
+#[allow(clippy::needless_pass_by_value)]
+fn after_eol(tokenizer: &mut Tokenizer, code: Code, info: EolInfo) -> StateFnResult {
+ tokenizer.attempt_opt(
+ space_or_tab_with_options(Options {
+ kind: TokenType::SpaceOrTab,
+ min: 1,
+ max: usize::MAX,
+ content_type: info.options.content_type,
+ connect: info.connect,
+ }),
+ after_more_space_or_tab,
+ )(tokenizer, code)
+}
+
+/// `space_or_tab_eol`: after more (optional) `space_or_tab`.
+///
+/// ```markdown
+/// alpha
+/// |bravo
+/// ```
+///
+/// ```markdown
+/// alpha
+/// |bravo
+/// ```
+fn after_more_space_or_tab(_tokenizer: &mut Tokenizer, code: Code) -> StateFnResult {
// Blank line not allowed.
if matches!(
code,