1use crate::block_parser::{Blocks, Body};
6use crate::definitions::Definitions;
7use crate::inline_parser::InlineParser;
8use crate::stream::{Event, EventKind, StreamError, Tag, TagKind, Text};
9use crate::util::line_ending::terminal_ending_start;
10use crate::value_builder::ValueBuilder;
11use crate::{LineEnding, ParseOptions};
12use alloc::string::ToString;
13
14pub struct EventParser<'a> {
16 blocks: Blocks,
17 source: &'a str,
18 options: &'a ParseOptions,
19 definitions: Definitions<'a>,
20 inline: InlineParser,
21 extensions: bool,
22 cursor: usize,
23 closing: bool,
24 phase: Phase,
25}
26impl<'a> EventParser<'a> {
27 pub fn new(source: &'a str, options: &'a ParseOptions) -> Self {
30 let (blocks, definitions) = Blocks::parse(source, options);
31 Self {
32 blocks,
33 source,
34 options,
35 definitions,
36 inline: InlineParser::default(),
37 extensions: options.constructs != crate::Constructs::default(),
38 cursor: 0,
39 closing: false,
40 phase: Phase::Start,
41 }
42 }
43}
44impl Iterator for EventParser<'_> {
45 type Item = Result<Event<'static>, StreamError>;
46 #[allow(clippy::inline_always)]
48 #[inline(always)]
49 fn next(&mut self) -> Option<Self::Item> {
50 self.next_event()
51 }
52}
53enum Phase {
55 Start,
56 Body,
57 Inline,
58 Separator(usize),
59 DocumentEnd,
60 Done,
61}
62impl EventParser<'_> {
63 fn prepare_inline<const EXTENSIONS: bool>(&mut self, index: usize, task: bool, table: bool) {
64 let fragments = self
65 .blocks
66 .children(index)
67 .map(|(_, node)| node.start..node.end);
68 if self.definitions.is_empty() {
69 self.inline.prepare::<false, EXTENSIONS>(
70 self.source,
71 fragments,
72 &self.definitions,
73 self.options,
74 task,
75 table,
76 );
77 } else {
78 self.inline.prepare::<true, EXTENSIONS>(
79 self.source,
80 fragments,
81 &self.definitions,
82 self.options,
83 task,
84 table,
85 );
86 }
87 }
88 fn advance(&mut self) {
89 let node = self.blocks.nodes[self.cursor];
90 if node.next != 0 {
91 self.cursor = node.next;
92 self.closing = false;
93 } else {
94 self.cursor = node.parent;
95 self.closing = true;
96 }
97 }
98 fn value(&self, index: usize) -> Text<'static> {
99 let mut value = ValueBuilder::new(self.source);
100 for (_, node) in self.blocks.children(index) {
101 let Body::Fragment(spaces) = node.body else {
102 unreachable!("literal block children are source fragments");
103 };
104 if spaces > 0 {
105 value.append(Text::Literal(&" "[..spaces]));
106 }
107 value.append(Text::Source(node.start..node.end));
108 }
109 value.finish()
110 }
111 fn separator(&self, end: usize) -> Event<'static> {
112 let bytes = self.source.as_bytes();
113 let start = match bytes[end - 1] {
115 b'\n' => end - 1 - usize::from(end > 1 && bytes[end - 2] == b'\r'),
116 b'\r' => end - 1,
117 _ => unreachable!("authored block separators end at a source newline"),
118 };
119 Event {
120 kind: EventKind::Separator {
121 value: Text::Source(start..end),
122 conditional: false,
123 },
124 span: end..end,
125 }
126 }
127 fn end_document(&mut self) -> Event<'static> {
128 self.phase = Phase::Done;
129 Event {
130 kind: EventKind::End(TagKind::Document),
131 span: self.source.len()..self.source.len(),
132 }
133 }
134 #[cold]
136 fn document_boundary(&mut self) -> Option<Result<Event<'static>, StreamError>> {
137 match self.phase {
138 Phase::Start => {
139 self.phase = Phase::Body;
140 self.cursor = self.blocks.nodes[0].child;
141 self.closing = self.cursor == 0;
142 let line_ending = LineEnding::from_source(self.source);
143 Some(Ok(Event {
144 kind: EventKind::Start(Tag::Document(line_ending)),
145 span: 0..self.source.len(),
146 }))
147 }
148 Phase::DocumentEnd => Some(Ok(self.end_document())),
149 Phase::Done => None,
150 Phase::Body => {
151 self.phase = Phase::DocumentEnd;
152 let last = self.blocks.children(0).last().map_or(0, |(index, _)| index);
153 let mut leaf = last;
154 while matches!(
155 self.blocks.nodes[leaf].body,
156 Body::Quote | Body::List { .. } | Body::Item(_)
157 ) {
158 let Some((child, _)) = self.blocks.children(leaf).last() else {
159 break;
160 };
161 leaf = child;
162 }
163 let last = self.blocks.nodes[last];
164 let leaf = self.blocks.nodes[leaf];
165 let populated = matches!(
166 leaf.body,
167 Body::Paragraph
168 | Body::Heading(_)
169 | Body::Rule
170 | Body::Code(_)
171 | Body::Html(_)
172 | Body::Table
173 );
174 let suppress_separator = match leaf.body {
175 Body::Html(end) => end == leaf.end && leaf.end == self.source.len(),
176 Body::Code(metadata) if leaf.parent == 0 => {
177 let code = &self.blocks.codes[metadata];
178 code.unclosed && leaf.end == self.source.len()
179 || code.fence.is_none()
180 && self.source[leaf.end..].trim().is_empty()
181 && !self.source.ends_with(['\r', '\n'])
182 }
183 _ => false,
184 };
185 if !suppress_separator
186 && self.blocks.nodes[0].child != 0
187 && (!matches!(last.body, Body::Definition(_)) || last.end < self.source.len())
188 && (self.source.ends_with(['\r', '\n'])
189 || (matches!(
190 last.body,
191 Body::Paragraph | Body::Heading(_) | Body::Rule | Body::Table
192 ) || last.body == Body::Quote && populated)
193 && self.source[..last.end]
194 .trim_end_matches([' ', '\t'])
195 .ends_with(['\r', '\n']))
196 {
197 return Some(Ok(
199 if !populated
200 || matches!(last.body, Body::List { .. })
201 && leaf.end != self.source.len()
202 || leaf.body == Body::Table && last.end > leaf.end
203 {
204 Event {
205 kind: EventKind::Separator {
206 value: Text::Literal(""),
207 conditional: true,
208 },
209 span: last.end..last.end,
210 }
211 } else {
212 self.separator(leaf.end)
213 },
214 ));
215 }
216 Some(Ok(self.end_document()))
217 }
218 Phase::Inline | Phase::Separator(_) => {
219 unreachable!("active body delivery bypasses document boundary handling")
220 }
221 }
222 }
223 #[allow(clippy::inline_always)]
225 #[inline(always)]
226 fn next_event(&mut self) -> Option<Result<Event<'static>, StreamError>> {
227 match self.phase {
228 Phase::Inline => {
229 if let Some(event) = self
230 .inline
231 .next(self.source, &self.definitions, self.options)
232 .map(Ok)
233 {
234 return Some(event);
235 }
236 self.phase = Phase::Body;
237 self.closing = true;
238 }
239 Phase::Body => {}
240 Phase::Separator(end) => {
241 self.phase = Phase::Body;
242 return Some(Ok(self.separator(end)));
243 }
244 _ => return self.document_boundary(),
245 }
246 if self.cursor == 0 {
247 return self.document_boundary();
248 }
249 let index = self.cursor;
250 let node = self.blocks.nodes[index];
251 if self.closing {
252 let tag = match node.body {
253 Body::Paragraph => TagKind::Paragraph,
254 Body::Heading(_) => TagKind::Heading,
255 Body::Quote => TagKind::BlockQuote,
256 Body::Footnote(_) => TagKind::FootnoteDefinition,
257 Body::List { .. } => TagKind::List,
258 Body::Item(_) => TagKind::Item,
259 Body::Table => TagKind::Table,
260 Body::TableHead => TagKind::TableHead,
261 Body::TableBody => TagKind::TableBody,
262 Body::TableRow => TagKind::TableRow,
263 Body::TableCell { .. } => TagKind::TableCell,
264 _ => unreachable!("only block containers yield closing events"),
265 };
266 if !self.blocks.tight_paragraph(&node)
268 && !matches!(
269 node.body,
270 Body::Item(_) | Body::Footnote(_) | Body::TableCell { .. }
271 )
272 && (node.next != 0
273 || node.body == Body::TableRow
274 && self.blocks.nodes[node.parent].body == Body::TableHead
275 || node.body == Body::Table
276 && node.parent != 0
277 && self.blocks.nodes[node.parent].end > node.end)
278 && !(matches!(node.body, Body::List { .. })
279 && matches!(self.blocks.nodes[node.next].body, Body::Definition(_)))
280 {
281 self.phase = Phase::Separator(node.end);
282 }
283 self.advance();
284 let end = node.span_end(self.source);
285 return Some(Ok(Event {
286 kind: EventKind::End(tag),
287 span: end..end,
288 }));
289 }
290 let kind = match node.body {
291 Body::Paragraph | Body::Heading(_) | Body::TableCell { .. } => {
292 let nodes = &self.blocks.nodes;
293 let task = self.options.constructs.task_list_item
294 && matches!(node.body, Body::Paragraph)
295 && matches!(nodes[node.parent].body, Body::Item(_))
296 && self.source[node.start..node.end].starts_with('[')
297 && self
298 .blocks
299 .children(node.parent)
300 .take_while(|&(before, _)| before != index)
301 .all(|(_, child)| matches!(child.body, Body::Definition(_)));
302 let table = matches!(node.body, Body::TableCell { .. });
303 if self.extensions {
304 self.prepare_inline::<true>(index, task, table);
305 } else {
306 self.prepare_inline::<false>(index, task, table);
307 }
308 self.phase = Phase::Inline;
309 match node.body {
310 Body::Heading(level) => EventKind::Start(Tag::Heading(level)),
311 Body::Paragraph => EventKind::Start(Tag::Paragraph {
312 tight: self.blocks.tight_paragraph(&node),
313 }),
314 Body::TableCell { alignment, header } => {
315 EventKind::Start(Tag::TableCell { alignment, header })
316 }
317 _ => unreachable!("this branch handles inline block containers"),
318 }
319 }
320 Body::Quote => EventKind::Start(Tag::BlockQuote),
321 Body::Footnote(index) => EventKind::Start(Tag::FootnoteDefinition(Text::from(
322 self.definitions.footnote_key(index).to_string(),
323 ))),
324 Body::Opaque => {
325 self.advance();
326 EventKind::Opaque {
327 value: Text::Source(node.start..node.end),
328 block: true,
329 }
330 }
331 Body::Table => EventKind::Start(Tag::Table),
332 Body::TableHead => EventKind::Start(Tag::TableHead),
333 Body::TableBody => EventKind::Start(Tag::TableBody),
334 Body::TableRow => EventKind::Start(Tag::TableRow),
335 Body::List {
336 tight,
337 marker,
338 start,
339 } => EventKind::Start(Tag::List {
340 tight,
341 start: matches!(marker, b'.' | b')').then_some(start),
342 }),
343 Body::Item(_) => {
344 let block_end = self
345 .blocks
346 .children(index)
347 .filter(|(_, child)| {
348 !matches!(child.body, Body::Definition(_) | Body::Footnote(_))
349 })
350 .last()
351 .is_some_and(|(_, child)| !self.blocks.tight_paragraph(child));
352 EventKind::Start(Tag::Item { block_end })
353 }
354 Body::Rule => {
355 if node.next != 0 {
356 self.phase = Phase::Separator(node.end);
357 }
358 self.advance();
359 EventKind::Rule
360 }
361 Body::Definition(index) => {
362 let definition = &self.definitions.entries[index];
363 let kind = EventKind::Definition {
364 label: definition.label.clone(),
365 destination: definition.destination.clone(),
366 title: definition.title.clone(),
367 };
368 self.advance();
369 let end = terminal_ending_start(self.source.as_bytes(), node.end);
370 return Some(Ok(Event {
371 kind,
372 span: node.start..end,
373 }));
374 }
375 Body::Html(end) => {
376 let kind = EventKind::Html {
377 value: self.value(index),
378 block: true,
379 };
380 if node.next != 0 && end < node.end {
381 self.phase = Phase::Separator(node.end);
382 }
383 self.advance();
384 return Some(Ok(Event {
385 kind,
386 span: node.start..end,
387 }));
388 }
389 Body::Code(metadata) => {
390 let code = &self.blocks.codes[metadata];
391 let value = self.value(index);
392 let kind = EventKind::CodeBlock {
393 value,
394 language: if code.info.is_empty() {
395 None
396 } else {
397 Some(crate::value_builder::decode_string_with_options(
398 self.source,
399 code.info.clone(),
400 self.options,
401 ))
402 },
403 fence: code.fence,
404 math: code.math,
405 unclosed: code.unclosed,
406 };
407 if node.next != 0 && !code.unclosed {
408 self.phase = Phase::Separator(node.end);
409 }
410 self.advance();
411 kind
412 }
413 _ => unreachable!("root and source fragments are not semantic blocks"),
414 };
415 if matches!(
416 node.body,
417 Body::Quote
418 | Body::Footnote(_)
419 | Body::List { .. }
420 | Body::Item(_)
421 | Body::Table
422 | Body::TableHead
423 | Body::TableBody
424 | Body::TableRow
425 ) {
426 if node.child == 0 {
427 self.closing = true;
428 } else {
429 self.cursor = node.child;
430 }
431 }
432 Some(Ok(Event {
433 kind,
434 span: node.start..node.span_end(self.source),
435 }))
436 }
437}