1use std::{fmt::Debug, marker::PhantomData, ops::Range};
4
5use winnow::{error::ContextError, LocatingSlice, Parser};
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((ParsedContext { initial, span }, entry)),
54 }
55 }
56
57 pub(super) fn parse_repeated<'i, Out, Sep, P, Q, E>(
59 &self,
60 parser: P,
61 separator: Q,
62 input: &'i str,
63 ) -> impl Iterator<Item = Result<(ParsedContext<'i>, Out), ParseError>> + 'i
64 where
65 Out: 'i,
66 Sep: 'i,
67 P: Parser<LocatingSlice<&'i str>, Out, E> + 'i,
68 Q: Parser<LocatingSlice<&'i str>, Sep, E> + 'i,
69 E: winnow::error::ParserError<&'i str, Inner = ContextError> + Debug + 'i,
70 {
71 ParsedIter {
72 parser,
73 separator,
74 initial: input,
75 input: LocatingSlice::new(input),
76 renderer: self.error_style.clone(),
77 _phantom: PhantomData,
78 }
79 }
80}
81
82#[derive(Debug, PartialEq, Eq)]
84pub struct ParsedContext<'i> {
85 pub(super) initial: &'i str,
86 pub(super) span: Range<usize>,
87}
88
89impl ParsedContext<'_> {
90 pub fn compute_line_start(&self) -> usize {
93 error::compute_line_number(self.initial, self.span.start)
94 }
95
96 pub fn as_str(&self) -> &str {
98 self.initial
99 .get(self.span.clone())
100 .expect("ParsedContext::span must be a valid UTF-8 boundary")
101 }
102
103 pub fn span(&self) -> ParsedSpan {
106 ParsedSpan(self.span.clone())
107 }
108}
109
110#[derive(Debug)]
112pub struct ParsedSpan(Range<usize>);
113
114impl ParsedSpan {
115 pub fn resolve(&self, span: &syntax::tracked::TrackedSpan) -> Range<usize> {
117 let target = span.as_range();
118 clip(self.0.clone(), target)
119 }
120}
121
122fn clip(parent: Range<usize>, child: Range<usize>) -> Range<usize> {
123 let start = std::cmp::max(parent.start, child.start) - parent.start;
124 let end = std::cmp::min(parent.end, child.end) - parent.start;
125 start..end
126}
127
128struct ParsedIter<'i, Out, Sep, P, Q, E> {
130 parser: P,
131 separator: Q,
132 initial: &'i str,
133 input: LocatingSlice<&'i str>,
134 renderer: annotate_snippets::Renderer,
135 _phantom: PhantomData<(Out, Sep, E)>,
136}
137
138impl<'i, Out, Sep, P, Q, E> Iterator for ParsedIter<'i, Out, Sep, P, Q, E>
139where
140 P: Parser<LocatingSlice<&'i str>, Out, E>,
141 Q: Parser<LocatingSlice<&'i str>, Sep, E>,
142 E: winnow::error::ParserError<&'i str, Inner = ContextError> + Debug + 'i,
143{
144 type Item = Result<(ParsedContext<'i>, Out), ParseError>;
145
146 fn next(&mut self) -> Option<Self::Item> {
147 use winnow::stream::Stream as _;
148 let start = self.input.checkpoint();
149 self.next_impl()
150 .map_err(|e| {
151 ParseError::new(
152 self.renderer.clone(),
153 self.initial,
154 self.input,
155 start,
156 e.into_inner()
157 .expect("ParseIter doesn't work with streaming parse yet"),
158 )
159 })
160 .transpose()
161 }
162}
163
164impl<'i, Out, Sep, P, Q, E> ParsedIter<'i, Out, Sep, P, Q, E>
165where
166 P: Parser<LocatingSlice<&'i str>, Out, E>,
167 Q: Parser<LocatingSlice<&'i str>, Sep, E>,
168 E: winnow::error::ParserError<&'i str> + 'i,
169{
170 fn next_impl(&mut self) -> Result<Option<(ParsedContext<'i>, Out)>, E> {
171 self.separator.parse_next(&mut self.input)?;
172 if self.input.is_empty() {
173 return Ok(None);
174 }
175 let (entry, span) = self
176 .parser
177 .by_ref()
178 .with_span()
179 .parse_next(&mut self.input)?;
180 Ok(Some((
181 ParsedContext {
182 initial: self.initial,
183 span,
184 },
185 entry,
186 )))
187 }
188}