1use std::marker::PhantomData;
4
5use crate::{
6 parsers::{star, EmptyParser, RepeatParser},
7 types::ParserOutput,
8 ParseContext, ParseError, ParseIter, Parser, Reported, Result,
9};
10
11pub trait Region: Copy + Clone {
13 fn check_at_start(context: &mut ParseContext, start: usize) -> Result<(), Reported>;
20
21 fn find_end(context: &mut ParseContext, start: usize) -> Result<(usize, usize), Reported>;
29
30 fn report_incomplete_match(context: &mut ParseContext, end: usize) -> Reported;
34}
35
36#[derive(Debug, Clone, Copy)]
40pub struct Line;
41
42impl Region for Line {
43 fn check_at_start(context: &mut ParseContext, start: usize) -> Result<(), Reported> {
44 let source = context.source();
45 if start == 0 || source[..start].ends_with('\n') {
46 Ok(())
47 } else {
48 Err(context.report(ParseError::new_bad_line_start(source, start)))
49 }
50 }
51
52 fn find_end(context: &mut ParseContext, start: usize) -> Result<(usize, usize), Reported> {
53 let source = context.source();
54 match source[start..].find('\n') {
55 Some(offset) => Ok((start + offset, start + offset + 1)),
56 None if start != source.len() => Ok((source.len(), source.len())),
57 None => Err(context.error_expected(source.len(), "line")),
58 }
59 }
60
61 fn report_incomplete_match(context: &mut ParseContext, end: usize) -> Reported {
62 context.report(ParseError::new_line_extra(context.source(), end))
63 }
64}
65
66#[derive(Debug, Clone, Copy)]
70pub struct Section;
71
72impl Region for Section {
73 fn check_at_start(context: &mut ParseContext, start: usize) -> Result<(), Reported> {
74 let source = context.source();
75 if start == 0 || &source[..start] == "\n" || source[..start].ends_with("\n\n") {
76 Ok(())
77 } else {
78 Err(context.report(ParseError::new_bad_section_start(source, start)))
79 }
80 }
81
82 fn find_end(context: &mut ParseContext, start: usize) -> Result<(usize, usize), Reported> {
83 let source = context.source();
87 match source[start..].find("\n\n") {
88 Some(index) => Ok((start + index + 1, start + index + 2)),
90 None if start < source.len() => Ok((source.len(), source.len())),
92 None => Err(context.error_expected(source.len(), "section")),
94 }
95 }
96
97 fn report_incomplete_match(context: &mut ParseContext, end: usize) -> Reported {
98 context.report(ParseError::new_section_extra(context.source(), end))
99 }
100}
101
102fn match_fully<'parse, R, P>(
105 context: &mut ParseContext<'parse>,
106 parser: &'parse P,
107) -> Result<P::Iter<'parse>, Reported>
108where
109 R: Region,
110 P: Parser,
111{
112 let source = context.source();
113 let mut iter = parser.parse_iter(context, 0)?;
114 while iter.match_end() != source.len() {
115 R::report_incomplete_match(context, iter.match_end());
116 iter.backtrack(context)?;
117 }
118 Ok(iter)
119}
120
121#[derive(Copy, Clone)]
122pub struct RegionParser<R: Region, P> {
123 parser: P,
124 phantom: PhantomData<fn() -> R>,
125}
126
127impl<R, P> Parser for RegionParser<R, P>
128where
129 R: Region,
130 P: Parser,
131{
132 type RawOutput = (P::Output,);
133 type Output = P::Output;
134 type Iter<'parse> = RegionParseIter<'parse, P>
135 where
136 R: 'parse,
137 P: 'parse;
138
139 fn parse_iter<'parse>(
140 &'parse self,
141 context: &mut ParseContext<'parse>,
142 start: usize,
143 ) -> Result<Self::Iter<'parse>, Reported> {
144 R::check_at_start(context, start)?;
145 let (inner_end, outer_end) = R::find_end(context, start)?;
146
147 let iter = context.with_slice(start, inner_end, |inner_context| {
148 match_fully::<R, P>(inner_context, &self.parser)
149 })?;
150 Ok(RegionParseIter { iter, outer_end })
151 }
152}
153
154pub struct RegionParseIter<'parse, P>
155where
156 P: Parser + 'parse,
157{
158 iter: P::Iter<'parse>,
159 outer_end: usize,
160}
161
162impl<'parse, P> ParseIter<'parse> for RegionParseIter<'parse, P>
163where
164 P: Parser,
165{
166 type RawOutput = (P::Output,);
167
168 fn match_end(&self) -> usize {
169 self.outer_end
170 }
171
172 fn backtrack(&mut self, _context: &mut ParseContext<'parse>) -> Result<(), Reported> {
173 Err(Reported)
174 }
175
176 fn convert(&self) -> Self::RawOutput {
177 let v = self.iter.convert().into_user_type();
178 (v,)
179 }
180}
181
182pub type LineParser<P> = RegionParser<Line, P>;
183pub type SectionParser<P> = RegionParser<Section, P>;
184
185pub fn line<P>(parser: P) -> LineParser<P> {
197 LineParser {
198 parser,
199 phantom: PhantomData,
200 }
201}
202
203pub fn lines<P>(parser: P) -> RepeatParser<LineParser<P>, EmptyParser> {
217 star(line(parser))
218}
219
220pub fn section<P>(parser: P) -> SectionParser<P> {
233 SectionParser {
234 parser,
235 phantom: PhantomData,
236 }
237}
238
239pub fn sections<P>(parser: P) -> RepeatParser<SectionParser<P>, EmptyParser> {
243 star(section(parser))
244}
245
246#[cfg(test)]
247mod tests {
248 use super::{line, section};
249 use crate::prelude::u32;
250 use crate::testing::*;
251
252 #[test]
253 fn test_newline_handling() {
254 let p = line("hello world");
255 assert_parse_eq(p, "hello world\n", ());
256 assert_parse_eq(p, "hello world", ());
257 assert_no_parse(p, "hello world\n\n");
258
259 let p = sequence(line("dog"), line("cat"));
260 assert_no_parse(p, "dog\n");
261 assert_no_parse(p, "dogcat");
262 assert_no_parse(p, "dogcat\n");
263 assert_parse_eq(p, "dog\ncat", ((), ()));
264 assert_parse_eq(p, "dog\ncat\n", ((), ()));
265
266 let p = section(plus(line(u32)));
267 assert_no_parse(p, "15\n16\n\n\n");
268 assert_parse_eq(p, "15\n16\n\n", vec![15, 16]);
269 assert_parse_eq(p, "15\n16\n", vec![15, 16]);
270 assert_parse_eq(p, "15\n16", vec![15, 16]);
271
272 let p = sequence(section(line("sec1")), section(line("sec2")));
273 assert_parse_eq(p, "sec1\n\nsec2\n\n", ((), ()));
274 assert_parse_eq(p, "sec1\n\nsec2\n", ((), ()));
275 assert_parse_eq(p, "sec1\n\nsec2", ((), ()));
276 assert_no_parse(p, "sec1\nsec2\n\n");
277 assert_no_parse(p, "sec1\nsec2\n");
278 assert_no_parse(p, "sec1\nsec2");
279 assert_no_parse(p, "sec1sec2\n\n");
280 }
281}