diff options
Diffstat (limited to '')
-rw-r--r-- | src/construct/partial_title.rs | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/src/construct/partial_title.rs b/src/construct/partial_title.rs new file mode 100644 index 0000000..4c7b527 --- /dev/null +++ b/src/construct/partial_title.rs @@ -0,0 +1,136 @@ +// To do: pass token types in. + +use crate::construct::partial_whitespace::start as whitespace; +use crate::tokenizer::{Code, State, StateFnResult, TokenType, Tokenizer}; + +/// Type of quote, if we’re in an attribure, in complete (condition 7). +#[derive(Debug, Clone, PartialEq)] +enum TitleKind { + /// In a parenthesised (`(` and `)`) title. + Paren, + /// In a double quoted (`"`) title. + Double, + /// In a single quoted (`"`) title. + Single, +} + +fn kind_to_marker(kind: &TitleKind) -> char { + match kind { + TitleKind::Double => '"', + TitleKind::Single => '\'', + TitleKind::Paren => ')', + } +} + +pub fn start(tokenizer: &mut Tokenizer, code: Code) -> StateFnResult { + let kind = match code { + Code::Char('"') => Some(TitleKind::Double), + Code::Char('\'') => Some(TitleKind::Single), + Code::Char('(') => Some(TitleKind::Paren), + _ => None, + }; + + if let Some(kind) = kind { + tokenizer.enter(TokenType::DefinitionTitle); + tokenizer.enter(TokenType::DefinitionTitleMarker); + tokenizer.consume(code); + tokenizer.exit(TokenType::DefinitionTitleMarker); + (State::Fn(Box::new(|t, c| at_first_break(t, c, kind))), None) + } else { + (State::Nok, None) + } +} + +/// To do. +fn at_first_break(tokenizer: &mut Tokenizer, code: Code, kind: TitleKind) -> StateFnResult { + match code { + Code::Char(char) if char == kind_to_marker(&kind) => { + tokenizer.enter(TokenType::DefinitionTitleMarker); + tokenizer.consume(code); + tokenizer.exit(TokenType::DefinitionTitleMarker); + tokenizer.exit(TokenType::DefinitionTitle); + (State::Ok, None) + } + _ => { + tokenizer.enter(TokenType::DefinitionTitleString); + at_break(tokenizer, code, kind) + } + } +} + +/// To do. +fn at_break(tokenizer: &mut Tokenizer, code: Code, kind: TitleKind) -> StateFnResult { + match code { + Code::Char(char) if char == kind_to_marker(&kind) => { + tokenizer.exit(TokenType::DefinitionTitleString); + at_first_break(tokenizer, code, kind) + } + Code::None => (State::Nok, None), + Code::CarriageReturnLineFeed | Code::Char('\r' | '\n') => { + tokenizer.enter(TokenType::LineEnding); + tokenizer.consume(code); + tokenizer.exit(TokenType::LineEnding); + ( + State::Fn(Box::new(|t, c| at_break_line_start(t, c, kind))), + None, + ) + } + _ => { + // To do: link. + tokenizer.enter(TokenType::ChunkString); + title(tokenizer, code, kind) + } + } +} + +fn at_break_line_start(tokenizer: &mut Tokenizer, code: Code, kind: TitleKind) -> StateFnResult { + tokenizer.attempt( + |t, c| whitespace(t, c, TokenType::Whitespace), + |_ok| Box::new(|t, c| at_break_line_begin(t, c, kind)), + )(tokenizer, code) +} + +fn at_break_line_begin(tokenizer: &mut Tokenizer, code: Code, kind: TitleKind) -> StateFnResult { + match code { + // Blank line not allowed. + Code::CarriageReturnLineFeed | Code::Char('\r' | '\n') => (State::Nok, None), + _ => at_break(tokenizer, code, kind), + } +} + +/// To do. +fn title(tokenizer: &mut Tokenizer, code: Code, kind: TitleKind) -> StateFnResult { + match code { + Code::Char(char) if char == kind_to_marker(&kind) => { + tokenizer.exit(TokenType::ChunkString); + at_break(tokenizer, code, kind) + } + Code::None | Code::CarriageReturnLineFeed | Code::Char('\r' | '\n') => { + tokenizer.exit(TokenType::ChunkString); + at_break(tokenizer, code, kind) + } + Code::Char('\\') => { + tokenizer.consume(code); + (State::Fn(Box::new(|t, c| escape(t, c, kind))), None) + } + _ => { + tokenizer.consume(code); + (State::Fn(Box::new(|t, c| title(t, c, kind))), None) + } + } +} + +/// To do. +fn escape(tokenizer: &mut Tokenizer, code: Code, kind: TitleKind) -> StateFnResult { + match code { + Code::Char(char) if char == kind_to_marker(&kind) => { + tokenizer.consume(code); + (State::Fn(Box::new(move |t, c| title(t, c, kind))), None) + } + Code::Char('\\') => { + tokenizer.consume(code); + (State::Fn(Box::new(move |t, c| title(t, c, kind))), None) + } + _ => title(tokenizer, code, kind), + } +} |