1#[macro_use]
2use crate::*;
3
4#[cfg(not(feature = "no-std"))] use core::fmt;
5#[cfg(feature = "no-std")] use alloc::fmt;
6#[cfg(feature = "no-std")] use alloc::string::String;
7#[cfg(feature = "no-std")] use alloc::vec::Vec;
8use nom::{
9 IResult,
10 branch::alt,
11 sequence::{tuple as nom_tuple, pair},
12 combinator::{opt, eof, peek},
13 multi::{many1, many_till, many0, separated_list1,separated_list0},
14 bytes::complete::{take_until, take_while},
15 Err,
16 Err::Failure
17};
18
19use std::collections::HashMap;
20use colored::*;
21
22use crate::*;
23
24pub fn title(input: ParseString) -> ParseResult<Title> {
29 let (input, mut text) = many1(text)(input)?;
30 let (input, _) = new_line(input)?;
31 let (input, _) = many1(equal)(input)?;
32 let (input, _) = whitespace0(input)?;
33 let (input, byline) = opt(byline)(input)?;
34 let mut title = Token::merge_tokens(&mut text).unwrap();
35 title.kind = TokenKind::Title;
36 Ok((input, Title{text: title, byline}))
37}
38
39pub fn byline(input: ParseString) -> ParseResult<Paragraph> {
40 let (input, byline) = paragraph_newline(input)?;
41 let (input, _) = many1(equal)(input)?;
42 Ok((input, byline))
43}
44
45pub struct MarkdownTableHeader {
46 pub header: Vec<(Token, Token)>,
47}
48
49pub fn no_alignment(input: ParseString) -> ParseResult<ColumnAlignment> {
50 let (input, _) = many1(dash)(input)?;
51 Ok((input, ColumnAlignment::Left))
52}
53
54pub fn left_alignment(input: ParseString) -> ParseResult<ColumnAlignment> {
55 let (input, _) = colon(input)?;
56 let (input, _) = many1(dash)(input)?;
57 Ok((input, ColumnAlignment::Left))
58}
59
60pub fn right_alignment(input: ParseString) -> ParseResult<ColumnAlignment> {
61 let (input, _) = many1(dash)(input)?;
62 let (input, _) = colon(input)?;
63 Ok((input, ColumnAlignment::Right))
64}
65
66pub fn center_alignment(input: ParseString) -> ParseResult<ColumnAlignment> {
67 let (input, _) = colon(input)?;
68 let (input, _) = many1(dash)(input)?;
69 let (input, _) = colon(input)?;
70 Ok((input, ColumnAlignment::Center))
71}
72
73pub fn alignment_separator(input: ParseString) -> ParseResult<ColumnAlignment> {
74 let (input, _) = many0(space_tab)(input)?;
75 let (input, separator) = alt((center_alignment, left_alignment, right_alignment, no_alignment))(input)?;
76 let (input, _) = many0(space_tab)(input)?;
77 Ok((input, separator))
78}
79
80pub fn mechdown_table(input: ParseString) -> ParseResult<MarkdownTable> {
81 let (input, _) = whitespace0(input)?;
82 let (input, table) = alt((mechdown_table_with_header, mechdown_table_no_header))(input)?;
83 Ok((input, table))
84}
85
86pub fn mechdown_table_with_header(input: ParseString) -> ParseResult<MarkdownTable> {
87 let (input, (header,alignment)) = mechdown_table_header(input)?;
88 let (input, rows) = many1(mechdown_table_row)(input)?;
89 Ok((input, MarkdownTable{header, rows, alignment}))
90}
91
92pub fn mechdown_table_no_header(input: ParseString) -> ParseResult<MarkdownTable> {
93 let (input, rows) = many1(mechdown_table_row)(input)?;
94 let header = vec![];
95 let alignment = vec![];
96 Ok((input, MarkdownTable{header, rows, alignment}))
97}
98
99pub fn mechdown_table_header(input: ParseString) -> ParseResult<(Vec<Paragraph>,Vec<ColumnAlignment>)> {
100 let (input, _) = whitespace0(input)?;
101 let (input, header) = many1(tuple((bar, tuple((many0(space_tab), inline_paragraph)))))(input)?;
102 let (input, _) = bar(input)?;
103 let (input, _) = whitespace0(input)?;
104 let (input, alignment) = many1(tuple((bar, tuple((many0(space_tab), alignment_separator)))))(input)?;
105 let (input, _) = bar(input)?;
106 let (input, _) = whitespace0(input)?;
107 let column_names: Vec<Paragraph> = header.into_iter().map(|(_,(_,tkn))| tkn).collect();
108 let column_alignments = alignment.into_iter().map(|(_,(_,tkn))| tkn).collect();
109 Ok((input, (column_names,column_alignments)))
110}
111
112pub fn empty_paragraph(input: ParseString) -> ParseResult<Paragraph> {
113 Ok((input, Paragraph{elements: vec![], error_range: None}))
114}
115
116pub fn mechdown_table_row(input: ParseString) -> ParseResult<Vec<Paragraph>> {
118 let (input, _) = whitespace0(input)?;
119 let (input, _) = bar(input)?;
120 let (input, row) = many1(tuple((alt((tuple((many0(space_tab), inline_paragraph)),tuple((many1(space_tab), empty_paragraph)))),bar)))(input)?;
121 let (input, _) = whitespace0(input)?;
122 let row = row.into_iter().map(|((_,tkn),_)| tkn).collect();
123 Ok((input, row))
124}
125
126pub fn ul_subtitle(input: ParseString) -> ParseResult<Subtitle> {
128 let (input, _) = many1((alt((digit_token, alpha_token))))(input)?;
129 let (input, _) = period(input)?;
130 let (input, _) = many0(space_tab)(input)?;
131 let (input, text) = paragraph_newline(input)?;
132 let (input, _) = many1(dash)(input)?;
133 let (input, _) = many0(space_tab)(input)?;
134 let (input, _) = new_line(input)?;
135 let (input, _) = many0(space_tab)(input)?;
136 let (input, _) = whitespace0(input)?;
137 Ok((input, Subtitle{text, level: 2}))
138}
139
140pub fn subtitle(input: ParseString) -> ParseResult<Subtitle> {
142 let (input, _) = peek(is_not(alt((error_sigil, info_sigil))))(input)?;
143 let (input, _) = many0(space_tab)(input)?;
144 let (input, _) = left_parenthesis(input)?;
145 let (input, num) = separated_list1(period,alt((many1(alpha),many1(digit))))(input)?;
146 let (input, _) = right_parenthesis(input)?;
147 let (input, _) = many0(space_tab)(input)?;
148 let (input, text) = paragraph_newline(input)?;
149 let (input, _) = many0(space_tab)(input)?;
150 let (input, _) = whitespace0(input)?;
151 let level: u8 = if num.len() < 3 { 3 } else { num.len() as u8 + 1 };
152 Ok((input, Subtitle{text, level}))
153}
154
155pub fn strong(input: ParseString) -> ParseResult<ParagraphElement> {
157 let (input, _) = tuple((asterisk,asterisk))(input)?;
158 let (input, text) = paragraph_element(input)?;
159 let (input, _) = tuple((asterisk,asterisk))(input)?;
160 Ok((input, ParagraphElement::Strong(Box::new(text))))
161}
162
163pub fn emphasis(input: ParseString) -> ParseResult<ParagraphElement> {
165 let (input, _) = asterisk(input)?;
166 let (input, text) = paragraph_element(input)?;
167 let (input, _) = asterisk(input)?;
168 Ok((input, ParagraphElement::Emphasis(Box::new(text))))
169}
170
171pub fn strikethrough(input: ParseString) -> ParseResult<ParagraphElement> {
173 let (input, _) = tilde(input)?;
174 let (input, text) = paragraph_element(input)?;
175 let (input, _) = tilde(input)?;
176 Ok((input, ParagraphElement::Strikethrough(Box::new(text))))
177}
178
179pub fn underline(input: ParseString) -> ParseResult<ParagraphElement> {
181 let (input, _) = underscore(input)?;
182 let (input, text) = paragraph_element(input)?;
183 let (input, _) = underscore(input)?;
184 Ok((input, ParagraphElement::Underline(Box::new(text))))
185}
186
187pub fn highlight(input: ParseString) -> ParseResult<ParagraphElement> {
189 let (input, _) = highlight_sigil(input)?;
190 let (input, text) = paragraph_element(input)?;
191 let (input, _) = highlight_sigil(input)?;
192 Ok((input, ParagraphElement::Highlight(Box::new(text))))
193}
194
195pub fn inline_code(input: ParseString) -> ParseResult<ParagraphElement> {
197 let (input, _) = is_not(grave_codeblock_sigil)(input)?; let (input, _) = grave(input)?;
199 let (input, text) = many0(tuple((is_not(grave),text)))(input)?;
200 let (input, _) = grave(input)?;
201 let mut text = text.into_iter().map(|(_,tkn)| tkn).collect();
202 let mut text = match Token::merge_tokens(&mut text) {
204 Some(t) => t,
205 None => {
206 return Ok((input, ParagraphElement::InlineCode(Token::default())));
207 }
208 };
209 text.kind = TokenKind::Text;
210 Ok((input, ParagraphElement::InlineCode(text)))
211}
212
213pub fn inline_equation(input: ParseString) -> ParseResult<ParagraphElement> {
215 let (input, _) = equation_sigil(input)?;
216 let (input, txt) = many0(tuple((is_not(equation_sigil),alt((backslash,text)))))(input)?;
217 let (input, _) = equation_sigil(input)?;
218 let mut txt = txt.into_iter().map(|(_,tkn)| tkn).collect();
219 let mut eqn = Token::merge_tokens(&mut txt).unwrap();
220 eqn.kind = TokenKind::Text;
221 Ok((input, ParagraphElement::InlineEquation(eqn)))
222}
223
224pub fn hyperlink(input: ParseString) -> ParseResult<ParagraphElement> {
226 let (input, _) = left_bracket(input)?;
227 let (input, link_text) = inline_paragraph(input)?;
228 let (input, _) = right_bracket(input)?;
229 let (input, _) = left_parenthesis(input)?;
230 let (input, link) = many1(tuple((is_not(right_parenthesis),text)))(input)?;
231 let (input, _) = right_parenthesis(input)?;
232 let mut tokens = link.into_iter().map(|(_,tkn)| tkn).collect::<Vec<Token>>();
233 let link_merged = Token::merge_tokens(&mut tokens).unwrap();
234 Ok((input, ParagraphElement::Hyperlink((link_text, link_merged))))
235}
236
237pub fn raw_hyperlink(input: ParseString) -> ParseResult<ParagraphElement> {
239 let (input, _) = peek(http_prefix)(input)?;
240 let (input, address) = many1(tuple((is_not(space), text)))(input)?;
241 let mut tokens = address.into_iter().map(|(_,tkn)| tkn).collect::<Vec<Token>>();
242 let url_token = Token::merge_tokens(&mut tokens).unwrap();
243 let url_paragraph = Paragraph::from_tokens(vec![url_token.clone()]);
244 Ok((input, ParagraphElement::Hyperlink((url_paragraph, url_token))))
245}
246
247pub fn option_map(input: ParseString) -> ParseResult<OptionMap> {
249 let msg = "Expects right bracket '}' to terminate map.";
250 let (input, (_, r)) = range(left_brace)(input)?;
251 let (input, _) = whitespace0(input)?;
252 let (input, elements) = many1(option_mapping)(input)?;
253 let (input, _) = whitespace0(input)?;
254 let (input, _) = label!(right_brace, msg, r)(input)?;
255 Ok((input, OptionMap{elements}))
256}
257
258pub fn option_mapping(input: ParseString) -> ParseResult<(Identifier, MechString)> {
260 let msg1 = "Unexpected space before colon ':'";
261 let msg2 = "Expects a value";
262 let msg3 = "Expects whitespace or comma followed by whitespace";
263 let msg4 = "Expects whitespace";
264 let (input, _) = whitespace0(input)?;
265 let (input, key) = identifier(input)?;
266 let (input, _) = whitespace0(input)?;
267 let (input, _) = colon(input)?;
268 let (input, _) = whitespace0(input)?;
269 let (input, value) = string(input)?;
270 let (input, _) = whitespace0(input)?;
271 let (input, _) = opt(comma)(input)?;
272 let (input, _) = whitespace0(input)?;
273 Ok((input, (key, value)))
274}
275
276pub fn img(input: ParseString) -> ParseResult<Image> {
278 let (input, _) = img_prefix(input)?;
279 let (input, caption_text) = opt(inline_paragraph)(input)?;
280 let (input, _) = right_bracket(input)?;
281 let (input, _) = left_parenthesis(input)?;
282 let (input, src) = many1(tuple((is_not(right_parenthesis),text)))(input)?;
283 let (input, _) = right_parenthesis(input)?;
284 let (input, style) = opt(option_map)(input)?;
285 let merged_src = Token::merge_tokens(&mut src.into_iter().map(|(_,tkn)| tkn).collect::<Vec<Token>>()).unwrap();
286 Ok((input, Image{src: merged_src, caption: caption_text, style}))
287}
288
289pub fn figure_item(input: ParseString) -> ParseResult<FigureItem> {
290 let (input, image) = img(input)?;
291 let caption = image.caption.unwrap_or(Paragraph { elements: vec![], error_range: None });
292 Ok((input, FigureItem { src: image.src, caption }))
293}
294
295pub fn figures_row(input: ParseString) -> ParseResult<Vec<FigureItem>> {
297 let (input, _) = whitespace0(input)?;
298 let (input, _) = bar(input)?;
299 let (input, cells) = many1(tuple((many0(space_tab), figure_item, many0(space_tab), bar)))(input)?;
300 let (input, _) = whitespace0(input)?;
301 let row = cells.into_iter().map(|(_, item, _, _)| item).collect();
302 Ok((input, row))
303}
304
305pub fn figures(input: ParseString) -> ParseResult<FigureTable> {
307 let (input, rows) = many1(figures_row)(input)?;
308 Ok((input, FigureTable { rows }))
309}
310
311pub fn paragraph_text(input: ParseString) -> ParseResult<ParagraphElement> {
313 let (input, elements) = match many1(nom_tuple((is_not(alt((section_sigil, footnote_prefix, highlight_sigil, equation_sigil, img_prefix, http_prefix, left_brace, left_bracket, left_angle, right_bracket, tilde, asterisk, underscore, grave, define_operator, bar, mika_section_open, mika_section_close))),text)))(input) {
314 Ok((input, mut text)) => {
315 let mut text = text.into_iter().map(|(_,tkn)| tkn).collect();
316 let mut text = Token::merge_tokens(&mut text).unwrap();
317 text.kind = TokenKind::Text;
318 (input, ParagraphElement::Text(text))
319 },
320 Err(err) => {return Err(err);},
321 };
322 Ok((input, elements))
323}
324
325pub fn eval_inline_mech_code(input: ParseString) -> ParseResult<ParagraphElement> {
327 let (input, _) = left_brace(input)?;
328 let (input, _) = whitespace0(input)?;
329 let (input, expr) = expression(input)?;
330 let (input, _) = whitespace0(input)?;
331 let (input, _) = right_brace(input)?;
332 Ok((input, ParagraphElement::EvalInlineMechCode(expr)))
333}
334
335pub fn inline_mech_code(input: ParseString) -> ParseResult<ParagraphElement> {
337 let (input, _) = left_brace(input)?;
338 let (input, _) = left_brace(input)?;
339 let (input, _) = whitespace0(input)?;
340 let (input, expr) = mech_code_alt(input)?;
341 let (input, _) = whitespace0(input)?;
342 let (input, _) = right_brace(input)?;
343 let (input, _) = right_brace(input)?;
344 Ok((input, ParagraphElement::InlineMechCode(expr)))
345}
346
347pub fn footnote_reference(input: ParseString) -> ParseResult<ParagraphElement> {
349 let (input, _) = footnote_prefix(input)?;
350 let (input, text) = many1(tuple((is_not(right_bracket),text)))(input)?;
351 let (input, _) = right_bracket(input)?;
352 let mut tokens = text.into_iter().map(|(_,tkn)| tkn).collect::<Vec<Token>>();
353 let footnote_text = Token::merge_tokens(&mut tokens).unwrap();
354 Ok((input, ParagraphElement::FootnoteReference(footnote_text)))
355}
356
357pub fn reference(input: ParseString) -> ParseResult<ParagraphElement> {
359 let (input, _) = left_bracket(input)?;
360 let (input, mut txt) = many1(alphanumeric)(input)?;
361 let (input, _) = right_bracket(input)?;
362 let ref_text = Token::merge_tokens(&mut txt).unwrap();
363 Ok((input, ParagraphElement::Reference(ref_text)))
364}
365
366pub fn section_reference(input: ParseString) -> ParseResult<ParagraphElement> {
368 let (input, _) = section_sigil(input)?;
369 let (input, mut txt) = many1(alt((alphanumeric, period)))(input)?;
370 let section_text = Token::merge_tokens(&mut txt).unwrap();
371 Ok((input, ParagraphElement::SectionReference(section_text)))
372}
373
374pub fn paragraph_element(input: ParseString) -> ParseResult<ParagraphElement> {
376 alt((hyperlink, reference, section_reference, raw_hyperlink, highlight, footnote_reference, inline_mech_code, eval_inline_mech_code, inline_equation, paragraph_text, strong, highlight, emphasis, inline_code, strikethrough, underline))(input)
377}
378
379pub fn inline_paragraph(input: ParseString) -> ParseResult<Paragraph> {
381 let (input, _) = peek(paragraph_element)(input)?;
382 let (input, elements) = many1(
383 pair(
384 is_not(new_line),
385 paragraph_element
386 )
387 )(input)?;
388 let elements = elements.into_iter().map(|(_,elem)| elem).collect();
389 Ok((input, Paragraph{elements, error_range: None}))
390}
391
392pub fn paragraph(input: ParseString) -> ParseResult<Paragraph> {
394 let (input, _) = peek(paragraph_element)(input)?;
395 let (input, elements) = many1(
396 pair(
397 is_not(alt((null(new_line), null(mika_section_close), null(idea_sigil)))),
398 labelr!(paragraph_element,
399 |input| recover::<ParagraphElement, _>(input, skip_till_paragraph_element),
400 "Unexpected paragraph element")
401 )
402 )(input)?;
403 let elements = elements.into_iter().map(|(_,elem)| elem).collect();
404 Ok((input, Paragraph{elements, error_range: None}))
405}
406
407pub fn paragraph_newline(input: ParseString) -> ParseResult<Paragraph> {
409 let (input, elements) = paragraph(input)?;
410 let (input, _) = new_line(input)?;
411 Ok((input, elements))
412}
413
414pub fn ordered_list_item(input: ParseString) -> ParseResult<(Number,Paragraph)> {
416 let (input, number) = number(input)?;
417 let (input, _) = period(input)?;
418 let (input, list_item) = labelr!(paragraph_newline, |input| recover::<Paragraph, _>(input, skip_till_eol), "Expects paragraph as list item")(input)?;
419 Ok((input, (number,list_item)))
420}
421
422pub fn checked_item(input: ParseString) -> ParseResult<(bool,Paragraph)> {
424 let (input, _) = dash(input)?;
425 let (input, _) = left_bracket(input)?;
426 let (input, _) = alt((tag("x"),tag("✓"),tag("✗")))(input)?;
427 let (input, _) = right_bracket(input)?;
428 let (input, list_item) = labelr!(paragraph_newline, |input| recover::<Paragraph, _>(input, skip_till_eol), "Expects paragraph as list item")(input)?;
429 Ok((input, (true,list_item)))
430}
431
432pub fn unchecked_item(input: ParseString) -> ParseResult<(bool,Paragraph)> {
434 let (input, _) = dash(input)?;
435 let (input, _) = left_bracket(input)?;
436 let (input, _) = whitespace0(input)?;
437 let (input, _) = right_bracket(input)?;
438 let (input, list_item) = labelr!(paragraph_newline, |input| recover::<Paragraph, _>(input, skip_till_eol), "Expects paragraph as list item")(input)?;
439 Ok((input, (false,list_item)))
440}
441
442pub fn check_list_item(input: ParseString) -> ParseResult<(bool,Paragraph)> {
444 let (input, item) = alt((checked_item, unchecked_item))(input)?;
445 Ok((input, item))
446}
447
448pub fn check_list(mut input: ParseString, level: usize) -> ParseResult<MDList> {
449 let mut items = vec![];
450 loop {
451 let mut indent = 0;
453 let mut current = input.peek(indent);
454 while current == Some(" ") || current == Some("\t") {
455 indent += 1;
456 current = input.peek(indent);
457 }
458 if indent < level {
460 break;
461 }
462 let (next_input, _) = many0(space_tab)(input.clone())?;
464 let (next_input, list_item) = match check_list_item(next_input.clone()) {
466 Ok((next_input, list_item)) => (next_input, list_item),
467 Err(err) => {
468 if !items.is_empty() {
469 break;
470 } else {
471 return Err(err);
472 }
473 }
474 };
475 let mut lookahead_indent = 0;
477 let mut current = next_input.peek(lookahead_indent);
478 while current == Some(" ") || current == Some("\t") {
479 lookahead_indent += 1;
480 current = next_input.peek(lookahead_indent);
481 }
482 input = next_input;
483 if lookahead_indent < level {
484 items.push((list_item, None));
486 break;
487 } else if lookahead_indent == level {
488 items.push((list_item, None));
490 continue;
491 } else {
492 let (next_input, sublist_md) = sublist(input.clone(), lookahead_indent)?;
494 items.push((list_item, Some(sublist_md)));
495 input = next_input;
496 }
497 }
498 Ok((input, MDList::Check(items)))
499}
500
501
502pub fn unordered_list(mut input: ParseString, level: usize) -> ParseResult<MDList> {
504 let mut items = vec![];
505 loop {
506 let mut indent = 0;
507 let mut current = input.peek(indent);
508 while current == Some(" ") || current == Some("\t") {
509 indent += 1;
510 current = input.peek(indent);
511 }
512 if indent < level {
514 return Ok((input, MDList::Unordered(items)));
515 }
516 let (next_input, _) = many0(space_tab)(input.clone())?;
517 let (next_input, list_item) = match unordered_list_item(next_input.clone()) {
519 Ok((next_input, list_item)) => (next_input, list_item),
520 Err(err) => {
521 if !items.is_empty() {
522 return Ok((input, MDList::Unordered(items)));
523 } else {
524 return Err(err);
525 }
526 }
527 };
528 let mut lookahead_indent = 0;
530 let mut current = next_input.peek(lookahead_indent);
531 while current == Some(" ") || current == Some("\t") {
532 lookahead_indent += 1;
533 current = next_input.peek(lookahead_indent);
534 }
535 input = next_input;
536 if lookahead_indent < level {
537 items.push((list_item, None));
539 return Ok((input, MDList::Unordered(items)));
540 } else if lookahead_indent == level {
541 items.push((list_item, None));
543 continue;
544 } else {
545 let (next_input, sub) = sublist(input.clone(), lookahead_indent)?;
547 items.push((list_item, Some(sub)));
548 input = next_input;
549 }
550 }
551}
552
553pub fn ordered_list(mut input: ParseString, level: usize) -> ParseResult<MDList> {
555 let mut items = vec![];
556 loop {
557 let mut indent = 0;
558 let mut current = input.peek(indent);
559 while current == Some(" ") || current == Some("\t") {
560 indent += 1;
561 current = input.peek(indent);
562 }
563 if indent < level {
565 let start = items.first()
566 .map(|item: &((Number, Paragraph), Option<MDList>)| item.0.0.clone())
567 .unwrap_or(Number::from_integer(1));
568 return Ok((input, MDList::Ordered(OrderedList { start, items })));
569 }
570 let (next_input, _) = many0(space_tab)(input.clone())?;
572 let (next_input, (list_item, _)) = match tuple((ordered_list_item, is_not(tuple((dash, dash)))))(next_input.clone()) {
574 Ok((next_input, res)) => (next_input, res),
575 Err(err) => {
576 if !items.is_empty() {
577 let start = items.first()
578 .map(|((number, _), _)| number.clone())
579 .unwrap_or(Number::from_integer(1));
580 return Ok((input, MDList::Ordered(OrderedList { start, items })));
581 } else {
582 return Err(err);
583 }
584 }
585 };
586
587 let mut lookahead_indent = 0;
589 let mut current = next_input.peek(lookahead_indent);
590 while current == Some(" ") || current == Some("\t") {
591 lookahead_indent += 1;
592 current = next_input.peek(lookahead_indent);
593 }
594
595 input = next_input;
596
597 if lookahead_indent < level {
598 items.push((list_item, None));
599 let start = items.first()
600 .map(|((number, _), _)| number.clone())
601 .unwrap_or(Number::from_integer(1));
602 return Ok((input, MDList::Ordered(OrderedList { start, items })));
603 } else if lookahead_indent == level {
604 items.push((list_item, None));
605 continue;
606 } else {
607 let (next_input, sub) = sublist(input.clone(), lookahead_indent)?;
609 items.push((list_item, Some(sub)));
610 input = next_input;
611 }
612 }
613}
614
615
616
617pub fn sublist(input: ParseString, level: usize) -> ParseResult<MDList> {
618 let (input, list) = match ordered_list(input.clone(), level) {
619 Ok((input, list)) => (input, list),
620 _ => match check_list(input.clone(), level) {
621 Ok((input, list)) => (input, list),
622 _ => match unordered_list(input.clone(), level) {
623 Ok((input, list)) => (input, list),
624 Err(err) => { return Err(err); }
625 }
626 }
627 };
628 Ok((input, list))
629}
630
631pub fn mechdown_list(input: ParseString) -> ParseResult<MDList> {
633 let (input, list) = match ordered_list(input.clone(), 0) {
634 Ok((input, list)) => (input, list),
635 _ => match check_list(input.clone(), 0) {
636 Ok((input, list)) => (input, list),
637 _ => match unordered_list(input.clone(), 0) {
638 Ok((input, list)) => (input, list),
639 Err(err) => { return Err(err); }
640 }
641 }
642 };
643 Ok((input, list))
644}
645
646pub fn unordered_list_item(input: ParseString) -> ParseResult<(Option<Token>,Paragraph)> {
648 let msg1 = "Expects space after dash";
649 let msg2 = "Expects paragraph as list item";
650 let (input, _) = dash(input)?;
651 let (input, bullet) = opt(tuple((left_parenthesis, emoji, right_parenthesis)))(input)?;
652 let (input, _) = labelr!(null(many1(space)), skip_nil, msg1)(input)?;
653 let (input, list_item) = labelr!(paragraph_newline, |input| recover::<Paragraph, _>(input, skip_till_eol), msg2)(input)?;
654 let (input, _) = many0(new_line)(input)?;
655 let bullet = match bullet {
656 Some((_,b,_)) => Some(b),
657 None => None,
658 };
659 Ok((input, (bullet, list_item)))
660}
661
662pub fn codeblock_sigil(input: ParseString) -> ParseResult<fn(ParseString) -> ParseResult<Token>> {
664 let (input, sgl_tkn) = alt((grave_codeblock_sigil, tilde_codeblock_sigil))(input)?;
665 let sgl_cmb = match sgl_tkn.kind {
666 TokenKind::GraveCodeBlockSigil => grave_codeblock_sigil,
667 TokenKind::TildeCodeBlockSigil => tilde_codeblock_sigil,
668 _ => unreachable!(),
669 };
670 Ok((input, sgl_cmb))
671}
672
673pub fn code_block(input: ParseString) -> ParseResult<SectionElement> {
675 let msg1 = "Expects 3 graves to start a code block";
676 let msg2 = "Expects new_line";
677 let msg3 = "Expects 3 graves followed by new_line to terminate a code block";
678 let (input, (end_sgl,r)) = range(codeblock_sigil)(input)?;
679 let (input, _) = many0(space_tab)(input)?;
680 let (input, code_id) = many0(tuple((is_not(left_brace),text)))(input)?;
681 let code_id = code_id.into_iter().map(|(_,tkn)| tkn).collect::<Vec<Token>>();
682 let (input, options) = opt(option_map)(input)?;
683 let (input, _) = many0(space_tab)(input)?;
684 let (input, _) = label!(new_line, msg2)(input)?;
685 let (input, (text,src_range)) = range(many0(nom_tuple((
686 is_not(end_sgl),
687 any,
688 ))))(input)?;
689 let (input, _) = end_sgl(input)?;
690 let (input, _) = whitespace0(input)?;
691 let block_src: Vec<char> = text.into_iter().flat_map(|(_, s)| s.chars().collect::<Vec<char>>()).collect();
692 let code_token = Token::new(TokenKind::CodeBlock, src_range, block_src.clone());
693
694 let code_id = code_id.iter().flat_map(|tkn| tkn.chars.clone().into_iter().collect::<Vec<char>>()).collect::<String>();
695 match code_id.as_str() {
696 "ebnf" => {
697 let ebnf_text = block_src.iter().collect::<String>();
698 match parse_grammar(&ebnf_text) {
699 Ok(grammar_tree) => {return Ok((input, SectionElement::Grammar(grammar_tree)));},
700 Err(err) => {
701 println!("Error parsing EBNF grammar: {:?}", err);
702 todo!();
703 }
704 }
705 }
706 tag => {
707 if tag.starts_with("mech") || tag.starts_with("mec") || tag.starts_with("🤖") {
709
710 let rest = tag.trim_start_matches("mech").trim_start_matches("mec").trim_start_matches("🤖").trim_start_matches(":");
712
713 let config = if rest == "" {BlockConfig { namespace_str: "".to_string(), namespace: 0, disabled: false, hidden: false}}
714 else if rest == "disabled" { BlockConfig { namespace_str: "".to_string(), namespace: 0, disabled: true, hidden: false} }
715 else if rest == "hidden" { BlockConfig { namespace_str: "".to_string(), namespace: 0, disabled: false, hidden: true} }
716 else { BlockConfig { namespace_str: rest.to_string(), namespace: hash_str(rest), disabled: false, hidden: false} };
717
718 let mech_src = block_src.iter().collect::<String>();
719 let graphemes = graphemes::init_source(&mech_src);
720 let parse_string = ParseString::new(&graphemes);
721
722 match mech_code(parse_string) {
723 Ok((_, mech_tree)) => {
724 return Ok((input, SectionElement::FencedMechCode(FencedMechCode{code: mech_tree, config, options})));
726 },
727 Err(err) => {
728 return Err(nom::Err::Error(ParseError {
729 cause_range: SourceRange::default(),
730 remaining_input: input,
731 error_detail: ParseErrorDetail {
732 message: "Generic error parsing Mech code block",
733 annotation_rngs: Vec::new(),
734 },
735 }));
736 }
737 };
738 } else if tag.starts_with("equation") || tag.starts_with("eq") || tag.starts_with("math") || tag.starts_with("latex") || tag.starts_with("tex") {
739 return Ok((input, SectionElement::Equation(code_token)));
740 } else if tag.starts_with("diagram") || tag.starts_with("chart") || tag.starts_with("mermaid") {
741 return Ok((input, SectionElement::Diagram(code_token)));
742 } else {
743 }
745 }
746 }
747 Ok((input, SectionElement::CodeBlock(code_token)))
748}
749
750pub fn thematic_break(input: ParseString) -> ParseResult<SectionElement> {
751 let (input, _) = many1(asterisk)(input)?;
752 let (input, _) = many0(space_tab)(input)?;
753 let (input, _) = new_line(input)?;
754 Ok((input, SectionElement::ThematicBreak))
755}
756
757pub fn footnote(input: ParseString) -> ParseResult<Footnote> {
759 let (input, _) = footnote_prefix(input)?;
760 let (input, text) = many1(tuple((is_not(right_bracket),text)))(input)?;
761 let (input, _) = right_bracket(input)?;
762 let (input, _) = colon(input)?;
763 let (input, _) = whitespace0(input)?;
764 let (input, paragraph) = many1(paragraph_newline)(input)?;
765 let mut tokens = text.into_iter().map(|(_,tkn)| tkn).collect::<Vec<Token>>();
766 let footnote_text = Token::merge_tokens(&mut tokens).unwrap();
767 let footnote = (footnote_text, paragraph);
768 Ok((input, footnote))
769}
770
771pub fn prompt(input: ParseString) -> ParseResult<SectionElement> {
773 let (input, _) = prompt_sigil(input)?;
774 let (input, _) = many0(space_tab)(input)?;
775 let (input, element) = section_element(input)?;
776 Ok((input, SectionElement::Prompt(Box::new(element))))
777}
778
779pub fn blank_line(input: ParseString) -> ParseResult<Vec<Token>> {
780 let (input, mut st) = many0(space_tab)(input)?;
781 let (input, n) = new_line(input)?;
782 st.push(n);
783 Ok((input, st))
784}
785
786pub fn question_block(input: ParseString) -> ParseResult<SectionElement> {
788 let (input, _) = question_sigil(input)?;
789 let (input, _) = many0(space_tab)(input)?;
790 let (input, paragraphs) = many1(paragraph_newline)(input)?;
791 Ok((input, SectionElement::QuestionBlock(paragraphs)))
792}
793
794pub fn info_block(input: ParseString) -> ParseResult<SectionElement> {
796 let (input, _) = info_sigil(input)?;
797 let (input, _) = many0(space_tab)(input)?;
798 let (input, paragraphs) = many1(paragraph_newline)(input)?;
799 Ok((input, SectionElement::InfoBlock(paragraphs)))
800}
801
802pub fn quote_block(input: ParseString) -> ParseResult<SectionElement> {
804 let (input, _) = peek(is_not(float_sigil))(input)?;
805 let (input, _) = peek(is_not(prompt_sigil))(input)?;
806 let (input, _) = quote_sigil(input)?;
807 let (input, _) = many0(space_tab)(input)?;
808 let (input, paragraphs) = many1(paragraph_newline)(input)?;
809 Ok((input, SectionElement::QuoteBlock(paragraphs)))
810}
811
812pub fn warning_block(input: ParseString) -> ParseResult<SectionElement> {
814 let (input, _) = peek(is_not(float_sigil))(input)?;
815 let (input, _) = warning_sigil(input)?;
816 let (input, _) = many0(space_tab)(input)?;
817 let (input, paragraphs) = many1(paragraph_newline)(input)?;
818 Ok((input, SectionElement::WarningBlock(paragraphs)))
819}
820
821pub fn success_block(input: ParseString) -> ParseResult<SectionElement> {
823 let (input, _) = peek(is_not(float_sigil))(input)?;
824 let (input, _) = alt((success_sigil, success_check_sigil))(input)?;
825 let (input, _) = many0(space_tab)(input)?;
826 let (input, paragraphs) = many1(paragraph_newline)(input)?;
827 Ok((input, SectionElement::SuccessBlock(paragraphs)))
828}
829
830pub fn error_block(input: ParseString) -> ParseResult<SectionElement> {
832 let (input, _) = peek(is_not(float_sigil))(input)?;
833 let (input, _) = alt((error_sigil, error_alt_sigil))(input)?;
834 let (input, _) = many0(space_tab)(input)?;
835 let (input, paragraphs) = many1(paragraph_newline)(input)?;
836 Ok((input, SectionElement::ErrorBlock(paragraphs)))
837}
838
839pub fn idea_block(input: ParseString) -> ParseResult<SectionElement> {
841 let (input, _) = idea_sigil(input)?;
842 let (input, _) = many0(space_tab)(input)?;
843 let (input, paragraphs) = many1(paragraph_newline)(input)?;
844 Ok((input, SectionElement::IdeaBlock(paragraphs)))
845}
846
847pub fn abstract_el(input: ParseString) -> ParseResult<SectionElement> {
849 let (input, _) = abstract_sigil(input)?;
850 let (input, _) = many0(space_tab)(input)?;
851 let (input, paragraphs) = many1(paragraph_newline)(input)?;
852 Ok((input, SectionElement::Abstract(paragraphs)))
853}
854
855pub fn equation(input: ParseString) -> ParseResult<Token> {
857 let (input, _) = equation_sigil(input)?;
858 let (input, mut txt) = many1(alt((backslash,text)))(input)?;
859 let mut eqn = Token::merge_tokens(&mut txt).unwrap();
860 Ok((input, eqn))
861}
862
863pub fn citation(input: ParseString) -> ParseResult<Citation> {
865 let (input, _) = left_bracket(input)?;
866 let (input, mut id) = many1(alphanumeric)(input)?;
867 let (input, _) = right_bracket(input)?;
868 let (input, _) = colon(input)?;
869 let (input, _) = whitespace0(input)?;
870 let (input, txt) = paragraph(input)?;
871 let (input, _) = whitespace0(input)?;
872 let id = Token::merge_tokens(&mut id).unwrap();
873 Ok((input, Citation{id, text: txt}))
874}
875
876pub fn float_sigil(input: ParseString) -> ParseResult<FloatDirection> {
878 let (input, d) = alt((float_left, float_right))(input)?;
879 let d = match d.kind {
880 TokenKind::FloatLeft => FloatDirection::Left,
881 TokenKind::FloatRight => FloatDirection::Right,
882 _ => unreachable!(),
883 };
884 Ok((input, d))
885}
886
887pub fn float(input: ParseString) -> ParseResult<(Box<SectionElement>,FloatDirection)> {
889 let (input, direction) = float_sigil(input)?;
890 let (input, _) = many0(space_tab)(input)?;
891 let (input, el) = section_element(input)?;
892 Ok((input, (Box::new(el), direction)))
893}
894
895pub fn not_mech_code(input: ParseString) -> ParseResult<()> {
897 let (input, _) = alt((null(question_block),
898 null(info_block),
899 null(success_block),
900 null(warning_block),
901 null(error_block),
902 null(idea_block),
903 null(img),
904 null(mika_section_close),
905 null(float)))(input)?;
906 Ok((input, ()))
907}
908
909pub fn section_element(input: ParseString) -> ParseResult<SectionElement> {
911 let parsers: Vec<(&'static str, Box<dyn Fn(ParseString) -> ParseResult<SectionElement>>)> = vec![
912 ("list", Box::new(|i| mechdown_list(i).map(|(i, lst)| (i, SectionElement::List(lst))))),
913 ("prompt", Box::new(prompt)),
914 ("footnote", Box::new(|i| footnote(i).map(|(i, f)| (i, SectionElement::Footnote(f))))),
915 ("citation", Box::new(|i| citation(i).map(|(i, c)| (i, SectionElement::Citation(c))))),
916 ("abstract", Box::new(abstract_el)),
917 ("img", Box::new(|i| img(i).map(|(i, img)| (i, SectionElement::Image(img))))),
918 ("figures", Box::new(|i| figures(i).map(|(i, f)| (i, SectionElement::FigureTable(f))))),
919 ("equation", Box::new(|i| equation(i).map(|(i, e)| (i, SectionElement::Equation(e))))),
920 ("table", Box::new(|i| mechdown_table(i).map(|(i, t)| (i, SectionElement::Table(t))))),
921 ("float", Box::new(|i| float(i).map(|(i, f)| (i, SectionElement::Float(f))))),
922 ("code_block", Box::new(code_block)),
924 ("thematic_break", Box::new(|i| thematic_break(i).map(|(i, _)| (i, SectionElement::ThematicBreak)))),
925 ("subtitle", Box::new(|i| subtitle(i).map(|(i, s)| (i, SectionElement::Subtitle(s))))),
926 ("question_block", Box::new(question_block)),
927 ("info_block", Box::new(info_block)),
928 ("success_block", Box::new(success_block)),
929 ("warning_block", Box::new(warning_block)),
930 ("error_block", Box::new(error_block)),
931 ("idea_block", Box::new(idea_block)),
932 ("paragraph", Box::new(|i| paragraph(i).map(|(i, p)| (i, SectionElement::Paragraph(p))))),
933 ];
934
935 alt_best(input, &parsers)
936
937}
938
939pub fn section(input: ParseString) -> ParseResult<Section> {
941 let (input, subtitle) = opt(ul_subtitle)(input)?;
942
943 let mut elements = vec![];
944
945 let mut new_input = input.clone();
946
947 loop {
948 if new_input.cursor >= new_input.graphemes.len() {
950 break;
952 }
953
954 if ul_subtitle(new_input.clone()).is_ok() {
956 break;
958 }
959
960 #[cfg(feature = "mika")]
961 if mika_section_close(new_input.clone()).is_ok() {
962 break;
963 }
964
965 #[cfg(feature = "mika")]
975 match mika(new_input.clone()) {
976 Ok((input, mika)) => {
977 elements.push(SectionElement::Mika(mika));
978 new_input = input;
979 continue;
980 }
981 Err(e) => {
982 }
985 }
986
987 match mech_code(new_input.clone()) {
989 Ok((input, mech_tree)) => {
990 elements.push(SectionElement::MechCode(mech_tree));
991 new_input = input;
992 continue;
993 }
994 Err(e) => {
995 }
998 }
999
1000 match section_element(new_input.clone()) {
1001 Ok((input, element)) => {
1002
1003 elements.push(element);
1004
1005 let (input, _) = many0(blank_line)(input.clone())?;
1007 new_input = input;
1008 }
1009 Err(err) => {
1010 return Err(err);
1012 }
1013 }
1014 }
1015 Ok((new_input, Section { subtitle, elements }))
1016}
1017
1018pub fn body(input: ParseString) -> ParseResult<Body> {
1020 let (mut input, _) = whitespace0(input)?;
1021 let mut sections = vec![];
1022 let mut new_input = input.clone();
1023 loop {
1024 if new_input.cursor >= new_input.graphemes.len() {
1025 break;
1026 }
1027 match section(new_input.clone()) {
1029 Ok((input, sect)) => {
1030 sections.push(sect);
1032 new_input = input;
1033 }
1034 Err(err) => {
1035 return Err(err);
1036 }
1037 }
1038 }
1039 Ok((new_input, Body { sections }))
1040}
1041
1042#[cfg(test)]
1043mod tests {
1044 use super::*;
1045
1046 #[test]
1047 fn parses_figures_block_with_markdown_image_syntax() {
1048 let src = "|  |  |\n|  |\n";
1049 let gs = graphemes::init_source(src);
1050 let input = ParseString::new(&gs);
1051 let (_, parsed) = figures(input).expect("figures block should parse");
1052 assert_eq!(parsed.rows.len(), 2);
1053 assert_eq!(parsed.rows[0].len(), 2);
1054 assert_eq!(parsed.rows[1].len(), 1);
1055 assert_eq!(parsed.rows[0][0].caption.to_string(), "caption a");
1056 assert_eq!(parsed.rows[0][0].src.to_string(), "img1.jpg");
1057 }
1058}