mech_syntax/
mechdown.rs

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,
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
24// ### Mechdown
25
26// title := text+, new_line, equal+, (space|tab)*, whitespace* ;
27pub fn title(input: ParseString) -> ParseResult<Title> {
28  let (input, mut text) = many1(text)(input)?;
29  let (input, _) = new_line(input)?;
30  let (input, _) = many1(equal)(input)?;
31  let (input, _) = many0(space_tab)(input)?;
32  let (input, _) = new_line(input)?;
33  let (input, _) = many0(space_tab)(input)?;
34  let (input, _) = whitespace0(input)?;
35  let mut title = Token::merge_tokens(&mut text).unwrap();
36  title.kind = TokenKind::Title;
37  Ok((input, Title{text: title}))
38}
39
40pub struct MarkdownTableHeader {
41  pub header: Vec<(Token, Token)>,
42}
43
44pub fn no_alignment(input: ParseString) -> ParseResult<ColumnAlignment> {
45  let (input, _) = many1(dash)(input)?;
46  Ok((input, ColumnAlignment::Left))
47}
48
49pub fn left_alignment(input: ParseString) -> ParseResult<ColumnAlignment> {
50  let (input, _) = colon(input)?;
51  let (input, _) = many1(dash)(input)?;
52  Ok((input, ColumnAlignment::Left))
53}
54
55pub fn right_alignment(input: ParseString) -> ParseResult<ColumnAlignment> {
56  let (input, _) = many1(dash)(input)?;
57  let (input, _) = colon(input)?;
58  Ok((input, ColumnAlignment::Right))
59}
60
61pub fn center_alignment(input: ParseString) -> ParseResult<ColumnAlignment> {
62  let (input, _) = colon(input)?;
63  let (input, _) = many1(dash)(input)?;
64  let (input, _) = colon(input)?;
65  Ok((input, ColumnAlignment::Center))
66}
67
68pub fn alignment_separator(input: ParseString) -> ParseResult<ColumnAlignment> {
69  let (input, _) = many0(space_tab)(input)?;
70  let (input, separator) = alt((center_alignment, left_alignment, right_alignment, no_alignment))(input)?;
71  let (input, _) = many0(space_tab)(input)?;
72  Ok((input, separator))
73}
74
75pub fn markdown_table(input: ParseString) -> ParseResult<MarkdownTable> {
76  let (input, _) = whitespace0(input)?;
77  let (input, table) = alt((markdown_table_with_header, markdown_table_no_header))(input)?;
78  Ok((input, table))
79}
80
81pub fn markdown_table_with_header(input: ParseString) -> ParseResult<MarkdownTable> {
82  let (input, (header,alignment)) = markdown_table_header(input)?;
83  let (input, rows) = many1(markdown_table_row)(input)?;
84  Ok((input, MarkdownTable{header, rows, alignment}))
85}
86
87pub fn markdown_table_no_header(input: ParseString) -> ParseResult<MarkdownTable> {
88  let (input, rows) = many1(markdown_table_row)(input)?;
89  let header = vec![];
90  let alignment = vec![];
91  Ok((input, MarkdownTable{header, rows, alignment}))
92}
93
94pub fn markdown_table_header(input: ParseString) -> ParseResult<(Vec<Paragraph>,Vec<ColumnAlignment>)> {
95  let (input, _) = whitespace0(input)?;
96  let (input, header) = many1(tuple((bar, paragraph)))(input)?;
97  let (input, _) = bar(input)?;
98  let (input, _) = new_line(input)?;
99  let (input, _) = whitespace0(input)?;
100  let (input, alignment) = many1(tuple((bar, alignment_separator)))(input)?;
101  let (input, _) = bar(input)?;
102  let (input, _) = new_line(input)?;
103  let column_names: Vec<Paragraph> = header.into_iter().map(|(_,tkn)| tkn).collect();
104  let column_alignments = alignment.into_iter().map(|(_,tkn)| tkn).collect();
105  Ok((input, (column_names,column_alignments)))
106}
107
108// markdown_table_row := +(bar, paragraph), bar, *whitespace ;
109pub fn markdown_table_row(input: ParseString) -> ParseResult<Vec<Paragraph>> {
110  let (input, _) = whitespace0(input)?;
111  let (input, row) = many1(tuple((bar, paragraph)))(input)?;
112  let (input, _) = bar(input)?;
113  let (input, _) = whitespace0(input)?;
114  let row = row.into_iter().map(|(_,tkn)| tkn).collect();
115  Ok((input, row))
116}
117
118// subtitle := digit_token+, period, space*, text+, new_line, dash+, (space|tab)*, new_line, (space|tab)*, whitespace* ;
119pub fn ul_subtitle(input: ParseString) -> ParseResult<Subtitle> {
120  let (input, _) = many1(digit_token)(input)?;
121  let (input, _) = period(input)?;
122  let (input, _) = many0(space)(input)?;
123  let (input, text) = paragraph(input)?;
124  let (input, _) = new_line(input)?;
125  let (input, _) = many1(dash)(input)?;
126  let (input, _) = many0(space_tab)(input)?;
127  let (input, _) = new_line(input)?;
128  let (input, _) = many0(space_tab)(input)?;
129  let (input, _) = whitespace0(input)?;
130  Ok((input, Subtitle{text, level: 2}))
131}
132
133// alpha_subtitle := (space|tab)*, "(", alpha, ")", (space|tab)+, text+, (space|tab)*, whitespace* ;
134pub fn subtitle(input: ParseString) -> ParseResult<Subtitle> {
135  let (input, _) = many0(space_tab)(input)?;
136  let (input, _) = left_parenthesis(input)?;
137  let (input, num) = separated_list1(period,alt((many1(alpha),many1(digit))))(input)?;
138  let (input, _) = right_parenthesis(input)?;
139  let (input, _) = many0(space_tab)(input)?;
140  let (input, text) = paragraph(input)?;
141  let (input, _) = many0(space_tab)(input)?;
142  let (input, _) = whitespace0(input)?;
143  let level: u8 = if num.len() < 3 { 3 } else { num.len() as u8 + 1 };
144  Ok((input, Subtitle{text, level}))
145}
146
147// strong := (asterisk, asterisk), +paragraph_element, (asterisk, asterisk) ;
148pub fn strong(input: ParseString) -> ParseResult<ParagraphElement> {
149  let (input, _) = tuple((asterisk,asterisk))(input)?;
150  let (input, text) = paragraph_element(input)?;
151  let (input, _) = tuple((asterisk,asterisk))(input)?;
152  Ok((input, ParagraphElement::Strong(Box::new(text))))
153}
154
155/// emphasis := asterisk, +paragraph_element, asterisk ;
156pub fn emphasis(input: ParseString) -> ParseResult<ParagraphElement> {
157  let (input, _) = asterisk(input)?;
158  let (input, text) = paragraph_element(input)?;
159  let (input, _) = asterisk(input)?;
160  Ok((input, ParagraphElement::Emphasis(Box::new(text))))
161}
162
163// strikethrough := tilde, +paragraph_element, tilde ;
164pub fn strikethrough(input: ParseString) -> ParseResult<ParagraphElement> {
165  let (input, _) = tilde(input)?;
166  let (input, text) = paragraph_element(input)?;
167  let (input, _) = tilde(input)?;
168  Ok((input, ParagraphElement::Strikethrough(Box::new(text))))
169}
170
171/// underline := underscore, +paragraph_element, underscore ;
172pub fn underline(input: ParseString) -> ParseResult<ParagraphElement> {
173  let (input, _) = underscore(input)?;
174  let (input, text) = paragraph_element(input)?;
175  let (input, _) = underscore(input)?;
176  Ok((input, ParagraphElement::Underline(Box::new(text))))
177}
178
179/// highlight := "!!", +paragraph_element, "!!" ;
180pub fn highlight(input: ParseString) -> ParseResult<ParagraphElement> {
181  let (input, _) = highlight_sigil(input)?;
182  let (input, text) = paragraph_element(input)?;
183  let (input, _) = highlight_sigil(input)?;
184  Ok((input, ParagraphElement::Highlight(Box::new(text))))
185}
186
187// inline_code := grave, +text, grave ;
188pub fn inline_code(input: ParseString) -> ParseResult<ParagraphElement> {
189  let (input, _) = grave(input)?;
190  let (input, text) = many0(tuple((is_not(grave),text)))(input)?;
191  let (input, _) = grave(input)?;
192  let mut text = text.into_iter().map(|(_,tkn)| tkn).collect();
193  let mut text = Token::merge_tokens(&mut text).unwrap();
194  text.kind = TokenKind::Text;
195  Ok((input, ParagraphElement::InlineCode(text)))
196}
197
198// inline_code := grave, +text, grave ;
199pub fn inline_equation(input: ParseString) -> ParseResult<ParagraphElement> {
200  let (input, _) = equation_sigil(input)?;
201  let (input, txt) = many0(tuple((is_not(equation_sigil),alt((backslash,text)))))(input)?;
202  let (input, _) = equation_sigil(input)?;
203  let mut txt = txt.into_iter().map(|(_,tkn)| tkn).collect();
204  let mut eqn = Token::merge_tokens(&mut txt).unwrap();
205  eqn.kind = TokenKind::Text;
206  Ok((input, ParagraphElement::InlineEquation(eqn)))
207}
208
209// hyperlink := "[", +text, "]", "(", +text, ")" ;
210pub fn hyperlink(input: ParseString) -> ParseResult<ParagraphElement> {
211  let (input, _) = left_bracket(input)?;
212  let (input, link_text) = many1(tuple((is_not(right_bracket),text)))(input)?;
213  let (input, _) = right_bracket(input)?;
214  let (input, _) = left_parenthesis(input)?;
215  let (input, link) = many1(tuple((is_not(right_parenthesis),text)))(input)?;
216  let (input, _) = right_parenthesis(input)?;
217  let mut tokens = link.into_iter().map(|(_,tkn)| tkn).collect::<Vec<Token>>();
218  let link_merged = Token::merge_tokens(&mut tokens).unwrap();
219  let mut tokens = link_text.into_iter().map(|(_,tkn)| tkn).collect::<Vec<Token>>();
220  let text_merged = Token::merge_tokens(&mut tokens).unwrap();
221  Ok((input, ParagraphElement::Hyperlink((text_merged, link_merged))))
222}
223
224// raw-hyperlink := ("https" | "http" |)
225pub fn raw_hyperlink(input: ParseString) -> ParseResult<ParagraphElement> {
226  let (input, _) = peek(http_prefix)(input)?;
227  let (input, address) = many1(tuple((is_not(space), text)))(input)?;
228  let mut tokens = address.into_iter().map(|(_,tkn)| tkn).collect::<Vec<Token>>();
229  let url = Token::merge_tokens(&mut tokens).unwrap();
230  Ok((input, ParagraphElement::Hyperlink((url.clone(), url))))
231}
232
233// img := "![", paragraph, "]", "(", +text, ")" ;
234pub fn img(input: ParseString) -> ParseResult<Image> {
235  let (input, _) = img_prefix(input)?;
236  let (input, caption_text) = paragraph(input)?;
237  let (input, _) = right_bracket(input)?;
238  let (input, _) = left_parenthesis(input)?;
239  let (input, src) = many1(tuple((is_not(right_parenthesis),text)))(input)?;
240  let (input, _) = right_parenthesis(input)?;
241  let merged_src = Token::merge_tokens(&mut src.into_iter().map(|(_,tkn)| tkn).collect::<Vec<Token>>()).unwrap();
242  Ok((input, Image{src: merged_src, caption: Some(caption_text)} ))
243}
244
245// paragraph_text := ¬(img_prefix | http_prefix | left_bracket | tilde | asterisk | underscore | grave | define_operator | bar), +text ;
246pub fn paragraph_text(input: ParseString) -> ParseResult<ParagraphElement> {
247  let (input, elements) = match many1(nom_tuple((is_not(alt((footnote_prefix, highlight_sigil, equation_sigil, img_prefix, http_prefix, left_brace, left_bracket, right_bracket, tilde, asterisk, underscore, grave, define_operator, bar))),text)))(input) {
248    Ok((input, mut text)) => {
249      let mut text = text.into_iter().map(|(_,tkn)| tkn).collect();
250      let mut text = Token::merge_tokens(&mut text).unwrap();
251      text.kind = TokenKind::Text;
252      (input, ParagraphElement::Text(text))
253    }, 
254    Err(err) => {return Err(err);},
255  };
256  Ok((input, elements))
257}
258
259// eval-inline-mech-cdoe := "{", expression, "}" ;`
260pub fn eval_inline_mech_code(input: ParseString) -> ParseResult<ParagraphElement> {
261  let (input, _) = left_brace(input)?;
262  let (input, expr) = expression(input)?;
263  let (input, _) = right_brace(input)?;
264  Ok((input, ParagraphElement::EvalInlineMechCode(expr)))
265}
266
267// inline-mech-cdoe := "{{", expression, "}}" ;`
268pub fn inline_mech_code(input: ParseString) -> ParseResult<ParagraphElement> {
269  let (input, _) = left_brace(input)?;
270  let (input, _) = left_brace(input)?;
271  let (input, (expr,_)) = mech_code(input)?;
272  let (input, _) = right_brace(input)?;
273  let (input, _) = right_brace(input)?;
274  Ok((input, ParagraphElement::InlineMechCode(expr)))
275}
276
277// footnote-reference := "[^", +text, "]" ;
278pub fn footnote_reference(input: ParseString) -> ParseResult<ParagraphElement> {
279  let (input, _) = footnote_prefix(input)?;
280  let (input, text) = many1(tuple((is_not(right_bracket),text)))(input)?;
281  let (input, _) = right_bracket(input)?;
282  let mut tokens = text.into_iter().map(|(_,tkn)| tkn).collect::<Vec<Token>>();
283  let footnote_text = Token::merge_tokens(&mut tokens).unwrap();
284  Ok((input, ParagraphElement::FootnoteReference(footnote_text)))
285}
286
287// reference := "[", +alphanumeric, "]" ;
288pub fn reference(input: ParseString) -> ParseResult<ParagraphElement> {
289  let (input, _) = left_bracket(input)?;
290  let (input, mut txt) = many1(alphanumeric)(input)?;
291  let (input, _) = right_bracket(input)?;
292  let ref_text = Token::merge_tokens(&mut txt).unwrap();
293  Ok((input, ParagraphElement::Reference(ref_text)))
294}
295
296// paragraph-element := hyperlink | raw-hyperlink | footnote-reference | paragraph-text | strong | highlight | emphasis | inline-code | strikethrough | underline ;
297pub fn paragraph_element(input: ParseString) -> ParseResult<ParagraphElement> {
298  alt((hyperlink, 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)
299}
300
301// paragraph := +paragraph_element ;
302pub fn paragraph(input: ParseString) -> ParseResult<Paragraph> {
303  let (input, elements) = many1(paragraph_element)(input)?;
304  Ok((input, Paragraph{elements}))
305}
306
307// indented-ordered-list-item := ws, number, ".", +text, new_line*; 
308pub fn ordered_list_item(input: ParseString) -> ParseResult<(Number,Paragraph)> {
309  let (input, number) = number(input)?;
310  let (input, _) = period(input)?;
311  let (input, list_item) = paragraph(input)?;
312  let (input, _) = new_line(input)?;
313  Ok((input, (number,list_item)))
314}
315
316// checked-item := "-", ("[", "x", "]"), paragraph ;
317pub fn checked_item(input: ParseString) -> ParseResult<(bool,Paragraph)> {
318  let (input, _) = dash(input)?;
319  let (input, _) = left_bracket(input)?;
320  let (input, _) = alt((tag("x"),tag("✓"),tag("✗")))(input)?;
321  let (input, _) = right_bracket(input)?;
322  let (input, list_item) = paragraph(input)?;
323  let (input, _) = new_line(input)?;
324  Ok((input, (true,list_item)))
325}
326
327// unchecked-item := "-", ("[", whitespace0, "]"), paragraph ;
328pub fn unchecked_item(input: ParseString) -> ParseResult<(bool,Paragraph)> {
329  let (input, _) = dash(input)?;
330  let (input, _) = left_bracket(input)?;
331  let (input, _) = whitespace0(input)?;
332  let (input, _) = right_bracket(input)?;
333  let (input, list_item) = paragraph(input)?;
334  let (input, _) = new_line(input)?;
335  Ok((input, (false,list_item)))
336}
337
338// check-list-item := checked-item | unchecked-item ;
339pub fn check_list_item(input: ParseString) -> ParseResult<(bool,Paragraph)> {
340  let (input, item) = alt((checked_item, unchecked_item))(input)?;
341  Ok((input, item))
342}
343
344pub fn check_list(mut input: ParseString, level: usize) -> ParseResult<MDList> {
345  let mut items = vec![];
346  loop {
347    // Calculate current line indent
348    let mut indent = 0;
349    let mut current = input.peek(indent);
350    while current == Some(" ") || current == Some("\t") {
351      indent += 1;
352      current = input.peek(indent);
353    }
354    // If indent is less than current level, we are done parsing this list level
355    if indent < level {
356      break;
357    }
358    // Consume whitespace
359    let (next_input, _) = many0(space_tab)(input.clone())?;
360    // Try parsing a checklist item
361    let (next_input, list_item) = match check_list_item(next_input.clone()) {
362      Ok((next_input, list_item)) => (next_input, list_item),
363      Err(err) => {
364        if !items.is_empty() {
365          break;
366        } else {
367          return Err(err);
368        }
369      }
370    };
371    // Look ahead to next line's indent
372    let mut lookahead_indent = 0;
373    let mut current = next_input.peek(lookahead_indent);
374    while current == Some(" ") || current == Some("\t") {
375      lookahead_indent += 1;
376      current = next_input.peek(lookahead_indent);
377    }
378    input = next_input;
379    if lookahead_indent < level {
380      // End of this list level
381      items.push((list_item, None));
382      break;
383    } else if lookahead_indent == level {
384      // Same level, continue
385      items.push((list_item, None));
386      continue;
387    } else {
388      // Nested sublist: parse recursively
389      let (next_input, sublist_md) = sublist(input.clone(), lookahead_indent)?;
390      items.push((list_item, Some(sublist_md)));
391      input = next_input;
392    }
393  }
394  Ok((input, MDList::Check(items)))
395}
396
397
398// unordered_list := +list_item, ?new_line, *whitespace ;
399pub fn unordered_list(mut input: ParseString, level: usize) -> ParseResult<MDList> {
400  let mut items = vec![];
401  loop {
402    let mut indent = 0;
403    let mut current = input.peek(indent);
404    while current == Some(" ") || current == Some("\t") {
405      indent += 1;
406      current = input.peek(indent);
407    }
408    // If indentation is less than the current level, return to parent list
409    if indent < level {
410      return Ok((input, MDList::Unordered(items)));
411    }
412    let (next_input, _) = many0(space_tab)(input.clone())?;
413    // Try to parse a list item
414    let (next_input, list_item) = match unordered_list_item(next_input.clone()) {
415      Ok((next_input, list_item)) => (next_input, list_item),
416      Err(err) => {
417        if !items.is_empty() {
418          return Ok((input, MDList::Unordered(items)));
419        } else {
420          return Err(err);
421        }
422      }
423    };
424    // Look ahead at the next line to determine indent
425    let mut lookahead_indent = 0;
426    let mut current = next_input.peek(lookahead_indent);
427    while current == Some(" ") || current == Some("\t") {
428      lookahead_indent += 1;
429      current = next_input.peek(lookahead_indent);
430    }
431    input = next_input;
432    if lookahead_indent < level {
433      // This is the last item at the current list level
434      items.push((list_item, None));
435      return Ok((input, MDList::Unordered(items)));
436    } else if lookahead_indent == level {
437      // Continue at the same level
438      items.push((list_item, None));
439      continue;
440    } else {
441      // Nested list detected
442      let (next_input, sub) = sublist(input.clone(), lookahead_indent)?;
443      items.push((list_item, Some(sub)));
444      input = next_input;
445    }
446  }
447}
448
449// ordered-list := +ordered-list-item, ?new-line, *whitespace ;
450pub fn ordered_list(mut input: ParseString, level: usize) -> ParseResult<MDList> {
451  let mut items = vec![];
452  loop {
453    let mut indent = 0;
454    let mut current = input.peek(indent);
455    while current == Some(" ") || current == Some("\t") {
456      indent += 1;
457      current = input.peek(indent);
458    }
459    // If indent drops below current level, return to parent
460    if indent < level {
461      let start = items.first()
462        .map(|item: &((Number, Paragraph), Option<MDList>)| item.0.0.clone())
463        .unwrap_or(Number::from_integer(1));
464      return Ok((input, MDList::Ordered(OrderedList { start, items })));
465    }
466    // Consume whitespace
467    let (next_input, _) = many0(space_tab)(input.clone())?;
468    // Try to parse an ordered list item
469    let (next_input, (list_item, _)) = match tuple((ordered_list_item, is_not(tuple((dash, dash)))))(next_input.clone()) {
470      Ok((next_input, res)) => (next_input, res),
471      Err(err) => {
472        if !items.is_empty() {
473          let start = items.first()
474            .map(|((number, _), _)| number.clone())
475            .unwrap_or(Number::from_integer(1));
476          return Ok((input, MDList::Ordered(OrderedList { start, items })));
477        } else {
478          return Err(err);
479        }
480      }
481    };
482
483    // Determine indentation of the next line
484    let mut lookahead_indent = 0;
485    let mut current = next_input.peek(lookahead_indent);
486    while current == Some(" ") || current == Some("\t") {
487      lookahead_indent += 1;
488      current = next_input.peek(lookahead_indent);
489    }
490
491    input = next_input;
492
493    if lookahead_indent < level {
494      items.push((list_item, None));
495      let start = items.first()
496        .map(|((number, _), _)| number.clone())
497        .unwrap_or(Number::from_integer(1));
498      return Ok((input, MDList::Ordered(OrderedList { start, items })));
499    } else if lookahead_indent == level {
500      items.push((list_item, None));
501      continue;
502    } else {
503      // Nested sublist
504      let (next_input, sub) = sublist(input.clone(), lookahead_indent)?;
505      items.push((list_item, Some(sub)));
506      input = next_input;
507    }
508  }
509}
510
511
512
513pub fn sublist(input: ParseString, level: usize) -> ParseResult<MDList> {
514  let (input, list) = match ordered_list(input.clone(), level) {
515    Ok((input, list)) => (input, list),
516    _ => match check_list(input.clone(), level) {
517      Ok((input, list)) => (input, list),
518      _ => match unordered_list(input.clone(), level) {
519        Ok((input, list)) => (input, list),
520        Err(err) => { return Err(err); }
521      }
522    }
523  };
524  Ok((input, list))
525}
526
527// mechdown-list := ordered-list | unordered-list ;
528pub fn mechdown_list(input: ParseString) -> ParseResult<MDList> {
529  let (input, list) = match ordered_list(input.clone(), 0) {
530    Ok((input, list)) => (input, list),
531    _ => match check_list(input.clone(), 0) {
532      Ok((input, list)) => (input, list),
533      _ => match unordered_list(input.clone(), 0) {
534        Ok((input, list)) => (input, list),
535        Err(err) => { return Err(err); }
536      }
537    }
538  };
539  Ok((input, list))
540}
541
542// list_item := dash, <space+>, <paragraph>, new_line* ;
543pub fn unordered_list_item(input: ParseString) -> ParseResult<(Option<Token>,Paragraph)> {
544  let msg1 = "Expects space after dash";
545  let msg2 = "Expects paragraph as list item";
546  let (input, _) = dash(input)?;
547  let (input, bullet) = opt(tuple((left_parenthesis, emoji, right_parenthesis)))(input)?;
548  let (input, _) = labelr!(null(many1(space)), skip_nil, msg1)(input)?;
549  let (input, list_item) = label!(paragraph, msg2)(input)?;
550  let (input, _) = many0(new_line)(input)?;
551  let bullet = match bullet {
552    Some((_,b,_)) => Some(b),
553    None => None,
554  };
555  Ok((input,  (bullet, list_item)))
556}
557
558
559pub fn skip_till_eol(input: ParseString) -> ParseResult<()> {
560
561  Ok((input, ()))
562}
563
564// code_block := grave, <grave>, <grave>, <new_line>, any, <grave{3}, new_line, whitespace*> ;
565pub fn code_block(input: ParseString) -> ParseResult<SectionElement> {
566  let msg1 = "Expects 3 graves to start a code block";
567  let msg2 = "Expects new_line";
568  let msg3 = "Expects 3 graves followed by new_line to terminate a code block";
569  let (input, (_, r)) = range(nom_tuple((
570    grave,
571    label!(grave, msg1),
572    label!(grave, msg1),
573  )))(input)?;
574  let (input, code_id) = many0(text)(input)?;
575  let (input, _) = label!(new_line, msg2)(input)?;
576  let (input, (text,src_range)) = range(many0(nom_tuple((
577    is_not(nom_tuple((grave, grave, grave))),
578    any,
579  ))))(input)?;
580  let (input, _) = nom_tuple((grave, grave, grave))(input)?;
581  let (input, _) = many0(space_tab)(input)?;
582  let (input, _) = new_line(input)?;
583  let block_src: Vec<char> = text.into_iter().flat_map(|(_, s)| s.chars().collect::<Vec<char>>()).collect();
584  let code_token = Token::new(TokenKind::CodeBlock, src_range, block_src.clone());
585
586  let code_id = code_id.iter().flat_map(|tkn| tkn.chars.clone().into_iter().collect::<Vec<char>>()).collect::<String>();
587  match code_id.as_str() {
588    "ebnf" => {
589      let ebnf_text = block_src.iter().collect::<String>();
590      match parse_grammar(&ebnf_text) {
591        Ok(grammar_tree) => {return Ok((input, SectionElement::Grammar(grammar_tree)));},
592        Err(err) => {
593          println!("Error parsing EBNF grammar: {:?}", err);
594          todo!();
595        }
596      }
597    }
598    tag => {
599      // if x begins with mec, mech, or 🤖
600      if tag.starts_with("mech") || tag.starts_with("mec") || tag.starts_with("🤖") {
601
602        // get rid of the prefix and then treat the rest of the string after : as an identifier
603        let rest = tag.trim_start_matches("mech").trim_start_matches("mec").trim_start_matches("🤖").trim_start_matches(":");
604        
605        let config = if rest == "" {BlockConfig { namespace: 0, disabled: false}}
606        else if rest == "disabled" { BlockConfig { namespace: hash_str(rest), disabled: true }} 
607        else { BlockConfig { namespace: hash_str(rest), disabled: false} };
608
609        let mech_src = block_src.iter().collect::<String>();
610        let graphemes = graphemes::init_source(&mech_src);
611        let parse_string = ParseString::new(&graphemes);
612
613        match many1(mech_code)(parse_string) {
614          Ok((_, mech_tree)) => {
615            // TODO what if not all the input is parsed? Is that handled?
616            return Ok((input, SectionElement::FencedMechCode((mech_tree,config))));
617          },
618          Err(err) => {
619            println!("Error parsing Mech code: {:?}", err);
620            todo!();
621          }
622        };
623      } else if tag.starts_with("equation") || tag.starts_with("eq") || tag.starts_with("math") || tag.starts_with("latex") || tag.starts_with("tex") {
624          return Ok((input, SectionElement::Equation(code_token)));
625      } else if tag.starts_with("diagram") || tag.starts_with("chart") || tag.starts_with("mermaid") {
626          return Ok((input, SectionElement::Diagram(code_token)));          
627      } else {
628        // Some other code block, just keep moving although we might want to do something with it later
629      }
630    }
631  } 
632  Ok((input, SectionElement::CodeBlock(code_token)))
633}
634
635pub fn block_quote(input: ParseString) -> ParseResult<Paragraph> {
636  let (input, _) = quote_sigil(input)?;
637  let (input, _) = many0(space_tab)(input)?;
638  let (input, text) = paragraph(input)?;
639  let (input, _) = many0(space_tab)(input)?;
640  Ok((input, text))
641}
642
643pub fn thematic_break(input: ParseString) -> ParseResult<SectionElement> {
644  let (input, _) = many1(asterisk)(input)?;
645  let (input, _) = many0(space_tab)(input)?;
646  let (input, _) = new_line(input)?;
647  Ok((input, SectionElement::ThematicBreak))
648}
649
650// footnote := "[^", +text, "]", ":", ws0, paragraph ;
651pub fn footnote(input: ParseString) -> ParseResult<Footnote> {
652  let (input, _) = footnote_prefix(input)?;
653  let (input, text) = many1(tuple((is_not(right_bracket),text)))(input)?;
654  let (input, _) = right_bracket(input)?;
655  let (input, _) = colon(input)?;
656  let (input, _) = whitespace0(input)?;
657  let (input, paragraph) = paragraph(input)?;
658  let mut tokens = text.into_iter().map(|(_,tkn)| tkn).collect::<Vec<Token>>();
659  let footnote_text = Token::merge_tokens(&mut tokens).unwrap();
660  let footnote = (footnote_text, paragraph);
661  Ok((input, footnote))
662}
663
664pub fn blank_line(input: ParseString) -> ParseResult<Vec<Token>> {
665  let (input, mut st) = many0(space_tab)(input)?;
666  let (input, n) = new_line(input)?;
667  st.push(n);
668  Ok((input, st))
669}
670
671pub fn abstract_el(input: ParseString) -> ParseResult<Paragraph> {
672  let (input, _) = abstract_sigil(input)?;
673  let (input, _) = many0(space_tab)(input)?;
674  let (input, text) = paragraph(input)?;
675  Ok((input, text))
676}
677
678// equation := "$$" , +text ;
679pub fn equation(input: ParseString) -> ParseResult<Token> {
680  let (input, _) = equation_sigil(input)?;
681  let (input, mut txt) = many1(alt((backslash,text)))(input)?;
682  let mut eqn = Token::merge_tokens(&mut txt).unwrap();
683  Ok((input, eqn))
684}
685
686// citation := "[", (identifier | number), "]", ":", ws0, paragraph, ws0, ?("(", +text, ")") ;
687pub fn citation(input: ParseString) -> ParseResult<Citation> {
688  let (input, _) = left_bracket(input)?;
689  let (input, mut id) = many1(alphanumeric)(input)?;
690  let (input, _) = right_bracket(input)?;
691  let (input, _) = colon(input)?;
692  let (input, _) = whitespace0(input)?;
693  let (input, txt) = paragraph(input)?;
694  let (input, _) = whitespace0(input)?;
695  let id = Token::merge_tokens(&mut id).unwrap();
696  Ok((input, Citation{id, text: txt}))
697}
698
699// float-sigil := ">>" | "<<" ;
700pub fn float_sigil(input: ParseString) -> ParseResult<FloatDirection> {
701  let (input, d) = alt((float_left, float_right))(input)?;
702  let d = match d.kind {
703    TokenKind::FloatLeft => FloatDirection::Left,
704    TokenKind::FloatRight => FloatDirection::Right,
705    _ => unreachable!(),
706  };
707  Ok((input, d))
708}
709
710// float := float-sigil, section-element ;
711pub fn float(input: ParseString) -> ParseResult<(Box<SectionElement>,FloatDirection)> {
712  let (input, direction) = float_sigil(input)?;
713  let (input, _) = many0(space_tab)(input)?;
714  let (input, el) = section_element(input)?;
715  Ok((input, (Box::new(el), direction)))
716}
717
718// sectio-_element := mech-code | list | footnote | citation | abstract | img | equation | markdown-table | float | block-quote | code-block | thematic-break | subtitle | paragraph ;
719pub fn section_element(input: ParseString) -> ParseResult<SectionElement> {
720  let (input, section_element) = match many1(mech_code)(input.clone()) {
721    Ok((input, code)) => (input, SectionElement::MechCode(code)),
722    _ =>match mechdown_list(input.clone()) {
723      Ok((input, lst)) => (input, SectionElement::List(lst)),
724      _ => match footnote(input.clone()) {
725        Ok((input, ftnote)) => (input, SectionElement::Footnote(ftnote)),
726        _ => match citation(input.clone()) {
727          Ok((input, citation)) => (input, SectionElement::Citation(citation)),
728          _ => match abstract_el(input.clone()) {
729            Ok((input, abstrct)) => (input, SectionElement::Abstract(abstrct)),
730            _ => match img(input.clone()) {
731              Ok((input, img)) => (input, SectionElement::Image(img)),
732              _ => match equation(input.clone()) {
733                Ok((input, eqn)) => (input, SectionElement::Equation(eqn)),
734                _ => match markdown_table(input.clone()) {
735                  Ok((input, table)) => (input, SectionElement::Table(table)),
736                  _ => match float(input.clone()) {
737                    Ok((input, flt)) => (input, SectionElement::Float(flt)),
738                    _ => match block_quote(input.clone()) {   
739                      Ok((input, quote)) => (input, SectionElement::BlockQuote(quote)),
740                      _ => match code_block(input.clone()) {
741                        Ok((input, m)) => (input,m),
742                        _ => match thematic_break(input.clone()) {
743                          Ok((input, _)) => (input, SectionElement::ThematicBreak),
744                          _ => match subtitle(input.clone()) {
745                            Ok((input, subtitle)) => (input, SectionElement::Subtitle(subtitle)),
746                            _ => match paragraph(input) {
747                              Ok((input, p)) => (input, SectionElement::Paragraph(p)),
748                              Err(err) => { return Err(err); }
749                            }
750                          }
751                        }
752                      }
753                    }
754                  }
755                }
756              }
757            }
758          }
759        }
760      }
761    }
762  };
763  let (input, _) = many0(blank_line)(input)?;
764  Ok((input, section_element))
765}
766
767// section := ul_subtitle, section_element* ;
768pub fn section(input: ParseString) -> ParseResult<Section> {
769  let msg = "Expects user function, block, mech code block, code block, statement, paragraph, or unordered list";
770  let (input, subtitle) = ul_subtitle(input)?;
771  let (input, elements) = many0(tuple((is_not(ul_subtitle),section_element)))(input)?;
772  let elements = elements.into_iter().map(|(_,e)| e).collect();
773  Ok((input, Section{subtitle: Some(subtitle), elements}))
774}
775
776// section_elements := section_element+ ;
777pub fn section_elements(input: ParseString) -> ParseResult<Section> {
778  let msg = "Expects user function, block, mech code block, code block, statement, paragraph, or unordered list";
779  let (input, elements) = many1(tuple((is_not(ul_subtitle),section_element)))(input)?;
780  let elements = elements.into_iter().map(|(_,e)| e).collect();
781  Ok((input, Section{subtitle: None, elements}))
782}
783
784// body := whitespace0, (section | section_elements)+, whitespace0 ;
785pub fn body(input: ParseString) -> ParseResult<Body> {
786  let (input, _) = whitespace0(input)?;
787  let (input, sections) = many0(alt((section,section_elements)))(input)?;
788  let (input, _) = whitespace0(input)?;
789  Ok((input, Body{sections}))
790}