use super::arguments::Arguments;
use super::{BlockRule, RULE_BLOCK};
use crate::parsing::collect::{collect_text, collect_text_keep};
use crate::parsing::condition::ParseCondition;
use crate::parsing::consume::consume;
use crate::parsing::{
ExtractedToken, ParseError, ParseErrorKind, ParseResult, Parser, Token,
gather_paragraphs,
};
use crate::tree::Element;
use regex::Regex;
use std::sync::LazyLock;
static ARGUMENT_KEY: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"[A-Za-z0-9_\-]+").unwrap());
impl<'r, 't> Parser<'r, 't>
where
'r: 't,
{
pub fn get_block_name(
&mut self,
flag_star: bool,
) -> Result<(&'t str, bool), ParseError> {
debug!("Looking for identifier");
if flag_star {
self.get_optional_token(Token::LeftBlockStar)?;
} else {
self.get_optional_token(Token::LeftBlock)?;
}
self.get_optional_space()?;
self.get_block_name_internal(ParseErrorKind::BlockMissingName)
}
fn get_block_name_internal(
&mut self,
kind: ParseErrorKind,
) -> Result<(&'t str, bool), ParseError> {
collect_text_keep(
self,
self.rule(),
&[
ParseCondition::current(Token::Whitespace),
ParseCondition::current(Token::LineBreak),
ParseCondition::current(Token::ParagraphBreak),
ParseCondition::current(Token::RightBlock),
],
&[],
Some(kind),
)
.map(|(name, last)| {
let name = name.trim();
let in_head = match last.token {
Token::Whitespace | Token::LineBreak | Token::ParagraphBreak => true,
Token::RightBlock => false,
_ => unreachable!(),
};
(name, in_head)
})
}
pub fn get_end_block(&mut self) -> Result<&'t str, ParseError> {
debug!("Looking for end block");
self.get_token(Token::LeftBlockEnd, ParseErrorKind::BlockExpectedEnd)?;
self.get_optional_space()?;
let (name, in_head) = self.get_block_name(false)?;
if in_head {
self.get_optional_space()?;
self.get_token(Token::RightBlock, ParseErrorKind::BlockExpectedEnd)?;
}
Ok(name)
}
fn verify_end_block(
&mut self,
first_iteration: bool,
block_rule: &BlockRule,
) -> Option<&'r ExtractedToken<'t>> {
self.save_evaluate_fn(|parser| {
if block_rule.accepts_newlines {
if !first_iteration {
parser.get_optional_line_break()?;
}
}
let name = parser.get_end_block()?;
let name = name.strip_suffix('_').unwrap_or(name);
for end_block_name in block_rule.accepts_names {
if name.eq_ignore_ascii_case(end_block_name) {
return Ok(true);
}
}
Ok(false)
})
}
fn get_body_generic<F>(
&mut self,
block_rule: &BlockRule,
mut process: F,
) -> Result<(&'r ExtractedToken<'t>, &'r ExtractedToken<'t>), ParseError>
where
F: FnMut(&mut Parser<'r, 't>) -> Result<(), ParseError>,
{
trace!("Running generic in block body parser");
debug_assert!(
!block_rule.accepts_names.is_empty(),
"List of valid end block names is empty, no success is possible",
);
let mut first = true;
let start = self.current();
loop {
let at_end_block = self.verify_end_block(first, block_rule);
if let Some(end) = at_end_block {
return Ok((start, end));
}
process(self)?;
self.step()?;
first = false;
}
}
pub fn get_body_text(
&mut self,
block_rule: &BlockRule,
) -> Result<&'t str, ParseError> {
debug!("Getting block body as text (rule {})", block_rule.name);
let (start, end) = self.get_body_generic(block_rule, |_| Ok(()))?;
let slice = self.full_text().slice_partial(start, end);
Ok(slice)
}
#[inline]
pub fn get_body_elements(
&mut self,
block_rule: &BlockRule,
as_paragraphs: bool,
) -> ParseResult<'r, 't, Vec<Element<'t>>> {
debug!(
"Getting block body as elements (block rule {}, as-paragraphs {})",
block_rule.name, as_paragraphs,
);
if as_paragraphs {
self.get_body_elements_paragraphs(block_rule)
} else {
self.get_body_elements_no_paragraphs(block_rule)
}
}
fn get_body_elements_paragraphs(
&mut self,
block_rule: &BlockRule,
) -> ParseResult<'r, 't, Vec<Element<'t>>> {
let mut first = true;
gather_paragraphs(
self,
self.rule(),
Some(move |parser: &mut Parser<'r, 't>| {
let result = parser.verify_end_block(first, block_rule);
first = false;
Ok(result.is_some())
}),
)
}
fn get_body_elements_no_paragraphs(
&mut self,
block_rule: &BlockRule,
) -> ParseResult<'r, 't, Vec<Element<'t>>> {
let mut all_elements = Vec::new();
let mut all_errors = Vec::new();
let mut paragraph_safe = true;
let mut first = true;
loop {
let result = self.verify_end_block(first, block_rule);
if result.is_some() {
return ok!(paragraph_safe; all_elements, all_errors);
}
first = false;
let old_remaining = self.remaining();
let elements = consume(self)?.chain(&mut all_errors, &mut paragraph_safe);
all_elements.extend(elements);
if self.same_pointer(old_remaining) {
self.step()?;
}
}
}
pub fn get_head_map(
&mut self,
block_rule: &BlockRule,
in_head: bool,
) -> Result<Arguments<'t>, ParseError> {
trace!("Looking for key value arguments, then ']]'");
let mut map = Arguments::new();
if in_head {
loop {
self.get_optional_spaces_any()?;
let key = {
let start = self.current();
let mut args_finished = false;
loop {
let current = self.current();
match current.token {
Token::RightBlock => {
args_finished = true;
break;
}
Token::Whitespace
| Token::LineBreak
| Token::ParagraphBreak
| Token::Equals => break,
_ if ARGUMENT_KEY.is_match(current.slice) => {
self.step()?;
}
_ => {
return Err(self
.make_err(ParseErrorKind::BlockMalformedArguments));
}
}
}
if args_finished {
break;
}
let end = self.current();
self.full_text().slice_partial(start, end)
};
self.get_optional_space()?;
self.get_token(Token::Equals, ParseErrorKind::BlockMalformedArguments)?;
self.get_optional_space()?;
let value = self.get_quoted_string(RULE_BLOCK)?;
map.insert(key, value);
}
}
self.get_head_block(block_rule, in_head)?;
Ok(map)
}
pub fn get_head_name_map(
&mut self,
block_rule: &BlockRule,
in_head: bool,
) -> Result<(&'t str, Arguments<'t>), ParseError> {
trace!("Looking for a name, then key value arguments, then ']]'");
if !in_head {
warn!("Block is already over, there is no name or arguments");
return Err(self.make_err(ParseErrorKind::BlockMissingName));
}
let (subname, in_head) =
self.get_block_name_internal(ParseErrorKind::ModuleMissingName)?;
let arguments = self.get_head_map(block_rule, in_head)?;
Ok((subname, arguments))
}
pub fn get_head_value<F, T>(
&mut self,
block_rule: &BlockRule,
in_head: bool,
convert: F,
) -> Result<T, ParseError>
where
F: FnOnce(&Self, Option<&'t str>) -> Result<T, ParseError>,
{
debug!("Looking for a value argument, then ']]' (in-head {in_head})");
let argument = if in_head {
let slice = collect_text(
self,
self.rule(),
&[ParseCondition::current(Token::RightBlock)],
&[
ParseCondition::current(Token::ParagraphBreak),
ParseCondition::current(Token::LineBreak),
],
Some(ParseErrorKind::BlockMalformedArguments),
)?;
Some(slice)
} else {
None
};
let value = convert(self, argument)?;
self.get_head_block(block_rule, false)?;
Ok(value)
}
pub fn get_head_none(
&mut self,
block_rule: &BlockRule,
in_head: bool,
) -> Result<(), ParseError> {
debug!("No arguments, looking for end of head block");
self.get_optional_space()?;
self.get_head_block(block_rule, in_head)?;
Ok(())
}
fn get_head_block(
&mut self,
block_rule: &BlockRule,
in_head: bool,
) -> Result<(), ParseError> {
trace!("Getting end of the head block");
if in_head {
self.get_token(Token::RightBlock, ParseErrorKind::BlockMissingCloseBrackets)?;
}
if self.current().token != Token::InputEnd && block_rule.accepts_newlines {
self.get_optional_line_break()?;
}
Ok(())
}
#[inline]
pub fn set_block(&mut self, block_rule: &BlockRule) {
debug!("Running block rule {} for these tokens", block_rule.name);
self.set_rule(block_rule.rule());
}
}