use std::marker::PhantomData;
use crate::{
parsers::{star, EmptyParser, RepeatParser},
types::ParserOutput,
ParseContext, ParseError, ParseIter, Parser, Reported, Result,
};
pub trait Region: Copy + Clone {
fn check_at_start(context: &mut ParseContext, start: usize) -> Result<(), Reported>;
fn find_end(context: &mut ParseContext, start: usize) -> Result<(usize, usize), Reported>;
fn report_incomplete_match(context: &mut ParseContext, end: usize) -> Reported;
}
#[derive(Debug, Clone, Copy)]
pub struct Line;
impl Region for Line {
fn check_at_start(context: &mut ParseContext, start: usize) -> Result<(), Reported> {
let source = context.source();
if start == 0 || source[..start].ends_with('\n') {
Ok(())
} else {
Err(context.report(ParseError::new_bad_line_start(source, start)))
}
}
fn find_end(context: &mut ParseContext, start: usize) -> Result<(usize, usize), Reported> {
let source = context.source();
match source[start..].find('\n') {
Some(offset) => Ok((start + offset, start + offset + 1)),
None if start != source.len() => Ok((source.len(), source.len())),
None => Err(context.error_expected(source.len(), "line")),
}
}
fn report_incomplete_match(context: &mut ParseContext, end: usize) -> Reported {
context.report(ParseError::new_line_extra(context.source(), end))
}
}
#[derive(Debug, Clone, Copy)]
pub struct Section;
impl Region for Section {
fn check_at_start(context: &mut ParseContext, start: usize) -> Result<(), Reported> {
let source = context.source();
if start == 0 || &source[..start] == "\n" || source[..start].ends_with("\n\n") {
Ok(())
} else {
Err(context.report(ParseError::new_bad_section_start(source, start)))
}
}
fn find_end(context: &mut ParseContext, start: usize) -> Result<(usize, usize), Reported> {
let source = context.source();
match source[start..].find("\n\n") {
Some(index) => Ok((start + index + 1, start + index + 2)),
None if start < source.len() => Ok((source.len(), source.len())),
None => Err(context.error_expected(source.len(), "section")),
}
}
fn report_incomplete_match(context: &mut ParseContext, end: usize) -> Reported {
context.report(ParseError::new_section_extra(context.source(), end))
}
}
fn match_fully<'parse, R, P>(
context: &mut ParseContext<'parse>,
parser: &'parse P,
) -> Result<P::Iter<'parse>, Reported>
where
R: Region,
P: Parser,
{
let source = context.source();
let mut iter = parser.parse_iter(context, 0)?;
while iter.match_end() != source.len() {
R::report_incomplete_match(context, iter.match_end());
iter.backtrack(context)?;
}
Ok(iter)
}
#[derive(Copy, Clone)]
pub struct RegionParser<R: Region, P> {
parser: P,
phantom: PhantomData<fn() -> R>,
}
impl<R, P> Parser for RegionParser<R, P>
where
R: Region,
P: Parser,
{
type RawOutput = (P::Output,);
type Output = P::Output;
type Iter<'parse> = RegionParseIter<'parse, P>
where
R: 'parse,
P: 'parse;
fn parse_iter<'parse>(
&'parse self,
context: &mut ParseContext<'parse>,
start: usize,
) -> Result<Self::Iter<'parse>, Reported> {
R::check_at_start(context, start)?;
let (inner_end, outer_end) = R::find_end(context, start)?;
let iter = context.with_slice(start, inner_end, |inner_context| {
match_fully::<R, P>(inner_context, &self.parser)
})?;
Ok(RegionParseIter { iter, outer_end })
}
}
pub struct RegionParseIter<'parse, P>
where
P: Parser + 'parse,
{
iter: P::Iter<'parse>,
outer_end: usize,
}
impl<'parse, P> ParseIter<'parse> for RegionParseIter<'parse, P>
where
P: Parser,
{
type RawOutput = (P::Output,);
fn match_end(&self) -> usize {
self.outer_end
}
fn backtrack(&mut self, _context: &mut ParseContext<'parse>) -> Result<(), Reported> {
Err(Reported)
}
fn convert(&self) -> Self::RawOutput {
let v = self.iter.convert().into_user_type();
(v,)
}
}
pub type LineParser<P> = RegionParser<Line, P>;
pub type SectionParser<P> = RegionParser<Section, P>;
pub fn line<P>(parser: P) -> LineParser<P> {
LineParser {
parser,
phantom: PhantomData,
}
}
pub fn lines<P>(parser: P) -> RepeatParser<LineParser<P>, EmptyParser> {
star(line(parser))
}
pub fn section<P>(parser: P) -> SectionParser<P> {
SectionParser {
parser,
phantom: PhantomData,
}
}
pub fn sections<P>(parser: P) -> RepeatParser<SectionParser<P>, EmptyParser> {
star(section(parser))
}
#[cfg(test)]
mod tests {
use super::{line, section};
use crate::prelude::u32;
use crate::testing::*;
#[test]
fn test_newline_handling() {
let p = line("hello world");
assert_parse_eq(p, "hello world\n", ());
assert_parse_eq(p, "hello world", ());
assert_no_parse(p, "hello world\n\n");
let p = sequence(line("dog"), line("cat"));
assert_no_parse(p, "dog\n");
assert_no_parse(p, "dogcat");
assert_no_parse(p, "dogcat\n");
assert_parse_eq(p, "dog\ncat", ((), ()));
assert_parse_eq(p, "dog\ncat\n", ((), ()));
let p = section(plus(line(u32)));
assert_no_parse(p, "15\n16\n\n\n");
assert_parse_eq(p, "15\n16\n\n", vec![15, 16]);
assert_parse_eq(p, "15\n16\n", vec![15, 16]);
assert_parse_eq(p, "15\n16", vec![15, 16]);
let p = sequence(section(line("sec1")), section(line("sec2")));
assert_parse_eq(p, "sec1\n\nsec2\n\n", ((), ()));
assert_parse_eq(p, "sec1\n\nsec2\n", ((), ()));
assert_parse_eq(p, "sec1\n\nsec2", ((), ()));
assert_no_parse(p, "sec1\nsec2\n\n");
assert_no_parse(p, "sec1\nsec2\n");
assert_no_parse(p, "sec1\nsec2");
assert_no_parse(p, "sec1sec2\n\n");
}
}