1use std::{fmt::Debug, marker::PhantomData, ops::Range};
4
5use winnow::{LocatingSlice, Parser, error::ContextError};
6
7use crate::syntax;
8
9use super::error::{self, ParseError};
10
11#[derive(Debug, Clone)]
13pub struct ParseOptions {
14 error_style: annotate_snippets::Renderer,
15}
16
17impl Default for ParseOptions {
18 fn default() -> Self {
19 Self {
20 error_style: annotate_snippets::Renderer::plain(),
21 }
22 }
23}
24
25impl ParseOptions {
26 pub fn with_error_style(mut self, error_style: annotate_snippets::Renderer) -> Self {
28 self.error_style = error_style;
29 self
30 }
31
32 pub(super) fn parse_single<'i, Out, P>(
33 &self,
34 parser: P,
35 input: &'i str,
36 ) -> Result<(ParsedContext<'i>, Out), ParseError>
37 where
38 Out: 'i,
39 P: Parser<LocatingSlice<&'i str>, Out, ContextError> + 'i,
40 {
41 use winnow::stream::Stream as _;
42 let initial = input;
43 let input = LocatingSlice::new(input);
44 let start = input.checkpoint();
45 match parser.with_span().parse(input) {
46 Err(e) => Err(ParseError::new(
47 self.error_style.clone(),
48 initial,
49 input,
50 start,
51 e.into_inner(),
52 )),
53 Ok((entry, span)) => Ok((
54 ParsedContext {
55 initial,
56 span,
57 separator: 0..0,
58 },
59 entry,
60 )),
61 }
62 }
63
64 pub(super) fn parse_repeated<'i, Out, Sep, P, Q, E>(
66 &self,
67 parser: P,
68 separator: Q,
69 input: &'i str,
70 ) -> impl Iterator<Item = Result<(ParsedContext<'i>, Out), ParseError>> + 'i
71 where
72 Out: 'i,
73 Sep: 'i,
74 P: Parser<LocatingSlice<&'i str>, Out, E> + 'i,
75 Q: Parser<LocatingSlice<&'i str>, Sep, E> + 'i,
76 E: winnow::error::ParserError<&'i str, Inner = ContextError> + Debug + 'i,
77 {
78 ParsedIter {
79 parser,
80 separator,
81 initial: input,
82 input: LocatingSlice::new(input),
83 renderer: self.error_style.clone(),
84 _phantom: PhantomData,
85 }
86 }
87}
88
89#[derive(Debug, PartialEq, Eq)]
91pub struct ParsedContext<'i> {
92 pub(super) initial: &'i str,
94 pub(super) separator: Range<usize>,
96 pub(super) span: Range<usize>,
98}
99
100impl ParsedContext<'_> {
101 pub(super) fn has_blank_line_before(&self) -> bool {
106 !self.separator.is_empty()
107 }
108
109 pub fn compute_line_start(&self) -> usize {
112 error::compute_line_number(self.initial, self.span.start)
113 }
114
115 pub fn as_str(&self) -> &str {
117 self.initial
118 .get(self.span.clone())
119 .expect("ParsedContext::span must be a valid UTF-8 boundary")
120 }
121
122 pub fn span(&self) -> ParsedSpan {
125 ParsedSpan(self.span.clone())
126 }
127}
128
129#[derive(Debug)]
131pub struct ParsedSpan(Range<usize>);
132
133impl ParsedSpan {
134 pub fn resolve(&self, span: &syntax::tracked::TrackedSpan) -> Range<usize> {
136 let target = span.as_range();
137 clip(self.0.clone(), target)
138 }
139}
140
141fn clip(parent: Range<usize>, child: Range<usize>) -> Range<usize> {
142 let start = std::cmp::max(parent.start, child.start) - parent.start;
143 let end = std::cmp::min(parent.end, child.end) - parent.start;
144 start..end
145}
146
147struct ParsedIter<'i, Out, Sep, P, Q, E> {
149 parser: P,
150 separator: Q,
151 initial: &'i str,
152 input: LocatingSlice<&'i str>,
153 renderer: annotate_snippets::Renderer,
154 _phantom: PhantomData<(Out, Sep, E)>,
155}
156
157impl<'i, Out, Sep, P, Q, E> Iterator for ParsedIter<'i, Out, Sep, P, Q, E>
158where
159 P: Parser<LocatingSlice<&'i str>, Out, E>,
160 Q: Parser<LocatingSlice<&'i str>, Sep, E>,
161 E: winnow::error::ParserError<&'i str, Inner = ContextError> + Debug + 'i,
162{
163 type Item = Result<(ParsedContext<'i>, Out), ParseError>;
164
165 fn next(&mut self) -> Option<Self::Item> {
166 use winnow::stream::Stream as _;
167 let start = self.input.checkpoint();
168 self.next_impl()
169 .map_err(|e| {
170 ParseError::new(
171 self.renderer.clone(),
172 self.initial,
173 self.input,
174 start,
175 e.into_inner()
176 .expect("ParseIter doesn't work with streaming parse yet"),
177 )
178 })
179 .transpose()
180 }
181}
182
183impl<'i, Out, Sep, P, Q, E> ParsedIter<'i, Out, Sep, P, Q, E>
184where
185 P: Parser<LocatingSlice<&'i str>, Out, E>,
186 Q: Parser<LocatingSlice<&'i str>, Sep, E>,
187 E: winnow::error::ParserError<&'i str> + 'i,
188{
189 fn next_impl(&mut self) -> Result<Option<(ParsedContext<'i>, Out)>, E> {
190 let separator = self.separator.by_ref().span().parse_next(&mut self.input)?;
191 if self.input.is_empty() {
192 return Ok(None);
193 }
194 let (entry, span) = self
195 .parser
196 .by_ref()
197 .with_span()
198 .parse_next(&mut self.input)?;
199 Ok(Some((
200 ParsedContext {
201 initial: self.initial,
202 span,
203 separator,
204 },
205 entry,
206 )))
207 }
208}