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