Skip to main content

mech_syntax/
formatter.rs

1use mech_core::*;
2use mech_core::nodes::{Kind, Matrix};
3use std::collections::{HashMap, HashSet};
4use colored::Colorize;
5use std::io::{Read, Write, Cursor};
6use crate::*;
7
8#[derive(Default)]
9struct TitleSlots {
10  author: String,
11  date: String,
12  hero: String,
13  kicker: String,
14  summary: String,
15  next: String,
16  previous: String,
17}
18
19#[derive(Debug, Clone)]
20struct InvalidCitationLinkCountError;
21
22impl MechErrorKind for InvalidCitationLinkCountError {
23  fn name(&self) -> &str {
24    "InvalidCitationLinkCount"
25  }
26
27  fn message(&self) -> String {
28    "Citations may contain at most one link.".to_string()
29  }
30}
31
32#[derive(Debug, Clone, PartialEq)]
33pub struct Formatter{
34  identifiers: HashMap<u64, String>,
35  rows: usize,
36  cols: usize,
37  indent: usize,
38  pub html: bool,
39  nested: bool,
40  toc: bool,
41  figure_num: usize,
42  h2_num: usize,
43  h3_num: usize,
44  h4_num: usize,
45  h5_num: usize,
46  h6_num: usize,
47  citation_num: usize,
48  citation_map: HashMap<u64, usize>,
49  citations: Vec<String>,
50  footnote_num: usize,
51  footnote_map: HashMap<u64, usize>,
52  footnotes: Vec<String>,
53  interpreter_id: u64,
54  inline_eval_counters: HashMap<u64, u64>,
55}
56
57impl Formatter {
58  fn citation_paragraph_with_optional_link(&mut self, paragraph: &Paragraph) -> MResult<(String, Option<String>)> {
59    let mut link: Option<String> = None;
60    let mut rendered = String::new();
61    for element in &paragraph.elements {
62      match element {
63        ParagraphElement::Hyperlink((text, url)) => {
64          if link.is_some() {
65            return Err(MechError::new(InvalidCitationLinkCountError, None).with_compiler_loc());
66          }
67          link = Some(url.to_string());
68          rendered.push_str(&self.inline_paragraph(text));
69        }
70        _ => rendered.push_str(&self.paragraph_element(element)),
71      }
72    }
73    Ok((rendered, link))
74  }
75
76  fn mika_interpreter_id(parent_id: u64, node: &(Mika, Option<MikaSection>)) -> u64 {
77    hash_str(&format!("mika:{}:{:?}", parent_id, (&node.0, &node.1)))
78  }
79
80  fn inline_eval_id(&mut self) -> u64 {
81    let next_ix = {
82      let counter = self.inline_eval_counters.entry(self.interpreter_id).or_insert(0);
83      let current = *counter;
84      *counter += 1;
85      current
86    };
87    hash_str(&format!("inline-eval:{}:{}", self.interpreter_id, next_ix))
88  }
89
90  pub fn new() -> Formatter {
91    Formatter {
92      identifiers: HashMap::new(),
93      rows: 0,
94      cols: 0,
95      indent: 0,
96      h2_num: 0,
97      h3_num: 0,
98      h4_num: 0,
99      h5_num: 0,
100      h6_num: 0,
101      citation_num: 0,
102      citation_map: HashMap::new(),
103      citations: Vec::new(),
104      footnote_num: 0,
105      footnote_map: HashMap::new(),
106      footnotes: Vec::new(),
107      figure_num: 0,
108      html: false,
109      nested: false,
110      toc: false,
111      interpreter_id: 0,
112      inline_eval_counters: HashMap::new(),
113    }
114  }
115
116  pub fn format(&mut self, tree: &Program) -> String {
117    self.html = false;
118    self.inline_eval_counters.clear();
119    self.program(tree)
120  }
121
122  /*pub fn format_grammar(&mut self, tree: &Gramamr) -> String {
123    self.html = false;
124    self.grammar(tree)
125  }*/
126
127  pub fn reset_numbering(&mut self) {
128    self.h2_num = 0;
129    self.h3_num = 0;
130    self.h4_num = 0;
131    self.h5_num = 0;
132    self.h6_num = 0;
133    self.figure_num = 0;
134  }
135
136  fn backmatter_heading(&self, text: &str) -> String {
137    let id = hash_str(text);
138    format!(
139      "<h3 id=\"{}\" class=\"mech-program-subtitle mech-backmatter-heading\">{}</h3>",
140      id, text
141    )
142  }
143
144  pub fn works_cited(&mut self) -> String {
145    if self.citations.is_empty() {
146      return "".to_string();
147    }
148    let mut src = format!(r#"<section id="67320967384727436" class="mech-works-cited">"#);
149    src.push_str(&self.backmatter_heading("Works Cited"));
150    for citation in &self.citations {
151      src.push_str(citation);
152    }
153    src.push_str("</section>\n");
154    src
155  }
156
157
158  pub fn footnotes(&mut self) -> String {
159    if self.footnotes.is_empty() {
160      return "".to_string();
161    }
162
163    let mut src = format!(r#"<section id="{}" class="mech-footnotes">"#, hash_str("footnotes"));
164    src.push_str(&self.backmatter_heading("Footnotes"));
165    for footnote in &self.footnotes {
166      src.push_str(footnote);
167    }
168    src.push_str("</section>\n");
169    src
170  }
171
172  pub fn format_html(&mut self, tree: &Program, style: String, shim: String) -> String {
173    self.html = true;
174    self.inline_eval_counters.clear();
175
176    let title_slots = self.title_slots(&tree.title);
177    let (formatted_abstract, formatted_intro, formatted_contents, formatted_cited, formatted_footnotes) = self.document_slots(tree);
178    let formatted_src = formatted_contents.clone();
179    self.reset_numbering();
180    let toc = tree.table_of_contents();
181    let formatted_toc = self.table_of_contents(&toc);
182
183    let title = match toc.title {
184      Some(title) => title.to_string(),
185      None => "Mech Program".to_string(),
186    };
187
188    #[cfg(feature = "serde")]
189    let encoded_tree = match compress_and_encode(&tree) {
190        Ok(encoded) => encoded,
191        Err(e) => todo!(),
192    };
193    #[cfg(not(feature = "serde"))]
194    let encoded_tree = String::new();
195    let repl_html = "<div class=\"console-scroll mech-repl hidden\" id=\"mech-output\"></div>";
196
197    let mut rendered = shim.replace("{{STYLESHEET}}", &style)
198        .replace("{{TOC}}", &formatted_toc)
199        .replace("{{AUTHOR}}", &title_slots.author)
200        .replace("{{DATE}}", &title_slots.date)
201        .replace("{{KICKER}}", &title_slots.kicker)
202        .replace("{{NEXT}}", &title_slots.next)
203        .replace("{{PREVIOUS}}", &title_slots.previous)
204        .replace("{{HERO}}", &title_slots.hero)
205        .replace("{{SUMMARY}}", &title_slots.summary)
206        .replace("{{ABSTRACT}}", &formatted_abstract)
207        .replace("{{INTRO}}", &formatted_intro)
208        .replace("{{CITED}}", &formatted_cited)
209        .replace("{{FOOTNOTES}}", &formatted_footnotes)
210        .replace("{{CODE}}", &encoded_tree)
211        .replace("{{REPL}}", repl_html)
212        .replace("{{TITLE}}", &title);
213
214    for (ix, section_html) in self.section_slots(tree).iter().enumerate() {
215      rendered = rendered.replace(&format!("{{{{SECTION{}}}}}", ix + 1), section_html);
216    }
217
218    rendered
219      .replace("{{CONTENT}}", &formatted_src)
220      .replace("{{CONTENTS}}", &formatted_contents)
221  }
222
223  fn title_slots(&mut self, title: &Option<Title>) -> TitleSlots {
224    match title {
225      Some(title) => {
226        TitleSlots {
227          author: title.author.as_ref().map(|p| self.inline_para_el(p, "mech-author")).unwrap_or_default(),
228          date: title.date.as_ref().map(|p| self.inline_para_el(p, "mech-date")).unwrap_or_default(),
229          hero: title.hero.as_ref().map(|h| self.hero_el(h)).unwrap_or_default(),
230          kicker: title.kicker.as_ref().map(|p| self.inline_para_el(p, "hero-kicker")).unwrap_or_default(),
231          summary: title.summary.as_ref().map(|p| self.synopsis_el(p)).unwrap_or_default(),
232          next: title.next.as_ref().map(|p| self.inline_para_el(p, "mech-next")).unwrap_or_default(),
233          previous: title.previous.as_ref().map(|p| self.inline_para_el(p, "mech-previous")).unwrap_or_default(),
234        }
235      }
236      None => TitleSlots::default(),
237    }
238  }
239
240  fn document_slots(&self, tree: &Program) -> (String, String, String, String, String) {
241    let first_section_ix = tree.body.sections.iter()
242      .position(|s| s.subtitle.is_some())
243      .unwrap_or(tree.body.sections.len());
244    let intro_sections = &tree.body.sections[..first_section_ix];
245    let content_sections = &tree.body.sections[first_section_ix..];
246
247    let mut abstract_formatter = Formatter::new();
248    abstract_formatter.html = true;
249    let mut intro_formatter = Formatter::new();
250    intro_formatter.html = true;
251    let mut contents_formatter = Formatter::new();
252    contents_formatter.html = true;
253
254    let mut abstract_src = String::new();
255    let mut intro_src = String::new();
256    let mut contents_src = String::new();
257
258    for section in intro_sections {
259      for el in &section.elements {
260        match el {
261          SectionElement::Abstract(paragraphs) => {
262            abstract_src.push_str(&abstract_formatter.abstract_el(paragraphs));
263          }
264          _ => {
265            intro_src.push_str(&intro_formatter.section_element(el));
266          }
267        }
268      }
269    }
270
271    for section in content_sections {
272      contents_src.push_str(&contents_formatter.section(section));
273    }
274
275    if !intro_src.is_empty() {
276      intro_src = format!("<section class=\"mech-intro\">{}</section>", intro_src);
277    }
278
279    let cited_src = contents_formatter.works_cited();
280    let footnotes_src = contents_formatter.footnotes();
281
282    (abstract_src, intro_src, contents_src, cited_src, footnotes_src)
283  }
284
285  fn section_slots(&self, tree: &Program) -> Vec<String> {
286    let first_section_ix = tree.body.sections.iter()
287      .position(|s| s.subtitle.is_some())
288      .unwrap_or(tree.body.sections.len());
289    let content_sections = &tree.body.sections[first_section_ix..];
290    let mut section_formatter = Formatter::new();
291    section_formatter.html = true;
292    content_sections.iter().map(|section| section_formatter.section(section)).collect()
293  }
294
295  pub fn table_of_contents(&mut self, toc: &TableOfContents) -> String {
296    let mut h2_num = 0usize;
297    let mut toc_items = String::new();
298    for section in &toc.sections {
299      let subtitle = match &section.subtitle {
300        Some(s) => s,
301        None => continue,
302      };
303      h2_num += 1;
304      let mut h3_num = 0usize;
305      let mut h4_num = 0usize;
306      let mut h5_num = 0usize;
307      let mut h6_num = 0usize;
308      let section_link_id = hash_str(&format!("{}.{}.{}.{}.{}", h2_num, h3_num, h4_num, h5_num, h6_num));
309      let mut nested = String::new();
310      let mut stack_depth = 0usize;
311
312      for element in &section.elements {
313        let subtitle = match element {
314          SectionElement::Subtitle(s) => s,
315          _ => continue,
316        };
317        match subtitle.level {
318          3 => { h3_num += 1; h4_num = 0; h5_num = 0; h6_num = 0; }
319          4 => { h4_num += 1; h5_num = 0; h6_num = 0; }
320          5 => { h5_num += 1; h6_num = 0; }
321          6 => { h6_num += 1; }
322          _ => {}
323        }
324        if subtitle.level < 3 || subtitle.level > 4 {
325          continue;
326        }
327        let target_depth = subtitle.level.saturating_sub(2) as usize;
328        while stack_depth < target_depth {
329          nested.push_str("<ul class=\"toc-sub\">");
330          stack_depth += 1;
331        }
332        while stack_depth > target_depth {
333          nested.push_str("</li></ul>");
334          stack_depth -= 1;
335        }
336        if target_depth > 0 {
337          nested.push_str("</li>");
338        }
339        let link_id = hash_str(&format!("{}.{}.{}.{}.{}", h2_num, h3_num, h4_num, h5_num, h6_num));
340        nested.push_str(&format!(
341          "<li><a href=\"#{}\">{}</a>",
342          link_id,
343          subtitle.to_string()
344        ));
345      }
346      while stack_depth > 0 {
347        nested.push_str("</li></ul>");
348        stack_depth -= 1;
349      }
350      if !nested.is_empty() {
351        nested.push_str("</li>");
352      }
353
354      toc_items.push_str(&format!(
355        "<li><a href=\"#{}\">{}</a>{}</li>",
356        section_link_id,
357        subtitle.to_string(),
358        nested
359      ));
360    }
361    format!("<aside class=\"toc mech-toc\"><div class=\"toc-title\">Contents</div><ul>{}</ul></aside>", toc_items)
362  }
363
364  pub fn sections(&mut self, sections: &Vec<Section>) -> String {
365    let mut src = "".to_string();
366    let section_count = sections.len();
367    for (i, section) in sections.iter().enumerate() {
368      let s = self.section(section);
369      src = format!("{}{}", src, s);
370    }
371    format!("<section class=\"mech-toc-sections\">{}</section>",src)
372  }
373
374  pub fn program(&mut self, node: &Program) -> String {
375    let title = match &node.title {
376      Some(title) => self.title(&title),
377      None => "".to_string(),
378    };
379    let body = self.body(&node.body);
380    let formatted_works_cited = self.works_cited();
381    if self.html {
382      format!("<div class=\"mech-content\"><div class=\"mech-program\">{}{}{}</div></div>",title,body,formatted_works_cited)
383    } else {
384      format!("{}{}{}",title,body,formatted_works_cited)
385    }
386  }
387
388  pub fn title(&mut self, node: &Title) -> String {
389    let title = node.text.to_string();
390
391    if self.html {
392      format!("<h1 class=\"mech-program-title\">{}</h1>", title)
393    } else {
394      format!(
395        "{}\n===============================================================================\n",
396        title
397      )
398    }
399  }
400
401  pub fn subtitle(&mut self, node: &Subtitle) -> String {
402    let level = node.level;
403    if level == 2 {
404      self.h2_num  += 1;
405      self.h3_num = 0;
406      self.h4_num = 0;
407      self.h5_num = 0;
408      self.h6_num = 0;
409      self.figure_num = 0;
410    } else if level == 3 {
411      self.h3_num += 1;
412      self.h4_num = 0;
413      self.h5_num = 0;
414      self.h6_num = 0;
415    } else if level == 4 {
416      self.h4_num += 1;
417      self.h5_num = 0;
418      self.h6_num = 0;
419    } else if level == 5 {
420      self.h5_num += 1;
421      self.h6_num = 0;
422    } else if level == 6 {
423      self.h6_num += 1;
424    }
425    
426    let toc = if self.toc { "toc" } else { "" };
427    let title_id = hash_str(&format!("{}.{}.{}.{}.{}{}",self.h2_num,self.h3_num,self.h4_num,self.h5_num,self.h6_num,toc));
428    
429    let link_str = format!("{}.{}.{}.{}.{}",self.h2_num,self.h3_num,self.h4_num,self.h5_num,self.h6_num);
430    let link_id  = hash_str(&link_str);
431
432    let section = if level == 2 { format!("section=\"{}.{}\"", self.h2_num, self.h3_num) } 
433    else if level == 3 { format!("section=\"{}.{}\"", self.h2_num, self.h3_num) }
434    else if level == 4 { format!("section=\"{}.{}.{}\"", self.h2_num, self.h3_num, self.h4_num) }
435    else if level == 5 { format!("section=\"{}.{}.{}.{}\"", self.h2_num, self.h3_num, self.h4_num, self.h5_num) }
436    else if level == 6 { format!("section=\"{}.{}.{}.{}.{}\"", self.h2_num, self.h3_num, self.h4_num, self.h5_num, self.h6_num) }
437    else { "".to_string() };    
438
439    if self.html {
440      format!("<h{} id=\"{}\" {} class=\"mech-program-subtitle {}\"><a class=\"mech-program-subtitle-link {}\" href=\"#{}\">{}</a></h{}>", level, title_id, section, toc, toc, link_id, node.to_string(), level)
441    } else {
442      format!("{}\n-------------------------------------------------------------------------------\n",node.to_string())
443    }
444  }
445
446  pub fn body(&mut self, node: &Body) -> String {
447    let mut src = "".to_string();
448    let section_count = node.sections.len();
449    for (i, section) in node.sections.iter().enumerate() {
450      let s = self.section(section);
451      src = format!("{}{}", src, s);
452    }
453    if self.html {
454      format!("<div class=\"mech-program-body\">{}</div>",src)
455    } else {
456      src
457    }
458  }
459
460  pub fn section(&mut self, node: &Section) -> String {
461    let mut src = match &node.subtitle {
462      Some(title) => self.subtitle(title),
463      None => "".to_string(),
464    };
465    for el in node.elements.iter() {
466      let el_str = self.section_element(el);
467      src = format!("{}{}", src, el_str);
468    }
469    let toc = if self.toc { "toc" } else { "" };
470    let section_id = hash_str(&format!("section-{}",self.h2_num + 1));
471    let id = hash_str(&format!("section-{}{}",self.h2_num + 1, toc));
472     if self.html {
473      format!("<section id=\"{}\" section=\"{}\" class=\"mech-program-section {}\">{}</section>",id,section_id,toc,src)
474    } else {
475      src
476    }
477  }
478
479  pub fn paragraph(&mut self, node: &Paragraph) -> String {
480    let mut src = "".to_string();
481    for el in node.elements.iter() {
482      let el_str = self.paragraph_element(el);
483      src = format!("{}{}", src, el_str);
484    }
485    let result = if self.html {
486      format!("<p class=\"mech-paragraph\">{}</p>",src)
487    } else {
488      format!("{}\n",src)
489    };
490    result
491  }
492
493  pub fn inline_paragraph(&mut self, node: &Paragraph) -> String {
494    let mut src = "".to_string();
495    for el in node.elements.iter() {
496      let el_str = self.paragraph_element(el);
497      src = format!("{}{}", src, el_str);
498    }
499    let result = if self.html {
500      format!("<span class=\"mech-inline-paragraph\">{}</span>",src)
501    } else {
502      format!("{}",src)
503    };
504    result
505  }
506
507  fn footnote_reference(&mut self, node: &Token) -> String {
508    let id_string = node.to_string();
509    let id_hash = hash_str(&format!("footnote-{}",id_string));
510    if self.html {
511      let footnote_num = match self.footnote_map.get(&id_hash) {
512        Some(existing_num) => *existing_num,
513        None => {
514          self.footnote_num += 1;
515          let next_num = self.footnote_num;
516          self.footnote_map.insert(id_hash, next_num);
517          next_num
518        },
519      };
520      format!("<a href=\"#{}\" class=\"mech-footnote-reference\">{}</a>",id_hash, footnote_num)
521    } else {
522      format!("[^{}]",id_string)
523    }
524  }
525
526  fn inline_equation(&mut self, node: &Token) -> String {
527    let id = hash_str(&format!("inline-equation-{}",node.to_string()));
528    if self.html {
529      format!("<span id=\"{}\" equation=\"{}\" class=\"mech-inline-equation\"></span>",id, node.to_string())
530    } else {
531      format!("$${}$$", node.to_string())
532    }
533  }
534
535  fn highlight(&mut self, node: &Token) -> String {
536    if self.html {
537      format!("<mark class=\"mech-highlight\">{}</mark>", node.to_string())
538    } else {
539      format!("!!{}!!", node.to_string())
540    }
541  }
542
543  fn reference(&mut self, node: &Token) -> String {
544    self.citation_num += 1;
545    let id = hash_str(&format!("reference-{}",node.to_string()));
546    let ref_id = hash_str(&format!("{}",node.to_string()));
547    self.citation_map.insert(ref_id, self.citation_num);
548    if self.html {
549      format!("<span id=\"{}\" class=\"mech-reference\">[<a href=\"#{}\" class=\"mech-reference-link\">{}</a>]</span>",id, ref_id, self.citation_num)
550    } else {
551      format!("[{}]",node.to_string())
552    }
553  }
554  
555  pub fn paragraph_element(&mut self, node: &ParagraphElement) -> String {
556    match node {
557      ParagraphElement::Error(t, s) => {
558        if self.html {
559          format!("<span class=\"mech-error\" title=\"Error at {:?}\">{}</span>", s, t.to_string())
560        } else {
561          format!("{{ERROR: {} at {:?}}}", t.to_string(), s)
562        }
563      },
564      ParagraphElement::Highlight(n) => {
565        if self.html {
566          format!("<mark class=\"mech-highlight\">{}</mark>", n.to_string())
567        } else {
568          format!("!!{}!!", n.to_string())
569        }
570      },
571      ParagraphElement::SectionReference(n) => {
572        let section_id_str = n.to_string();
573        let parts: Vec<&str> = section_id_str.split('.').collect();
574        let mut nums = vec!["0"; 5]; // up to h6 level
575        for (i, part) in parts.iter().enumerate() {
576          nums[i] = part;
577        }
578        let id_str = format!("{}.{}.{}.{}.{}",nums[0], nums[1], nums[2], nums[3], nums[4]);
579        let id = hash_str(&id_str);
580
581        if self.html {
582          format!(
583            "<span class=\"mech-section-reference\">
584              <a href=\"#{}\" class=\"mech-section-reference-link\">§{}</a>
585            </span>",
586            id, n.to_string()
587          )
588        } else {
589          format!("§{}", n.to_string())
590        }
591      }
592      ParagraphElement::Reference(n) => self.reference(n),
593      ParagraphElement::InlineEquation(exq) => self.inline_equation(exq),
594      ParagraphElement::Text(n) => {
595        if self.html {
596          format!("<span class=\"mech-text\">{}</span>", n.to_string())
597        } else {
598          n.to_string()
599        }
600      }
601      ParagraphElement::FootnoteReference(n) => self.footnote_reference(n),
602      ParagraphElement::Strong(n) => {
603        let p = self.paragraph_element(n);
604        if self.html {
605          format!("<strong class=\"mech-strong\">{}</strong>", p)
606        } else {
607          format!("**{}**", p)
608        }
609      },
610      ParagraphElement::Hyperlink((text, url)) => {
611        let url_str = url.to_string();
612        let text_str = self.inline_paragraph(text);
613        if self.html {
614          format!("<a href=\"{}\" class=\"mech-hyperlink\">{}</a>",url_str,text_str)
615        } else {
616          format!("[{}]({})",text_str,url_str)
617        }
618      },
619      ParagraphElement::Emphasis(n) => {
620        if self.html {
621          format!("<em class=\"mech-em\">{}</em>", n.to_string())
622        } else {
623          format!("*{}*", n.to_string())
624        }
625      },
626      ParagraphElement::Underline(n) => {
627        if self.html {
628          format!("<u class=\"mech-u\">{}</u>", n.to_string())
629        } else {
630          format!("_{}_", n.to_string())
631        }
632      },
633      ParagraphElement::Strikethrough(n) => {
634        if self.html {
635          format!("<del class=\"mech-del\">{}</del>", n.to_string())
636        } else {
637          format!("~{}~", n.to_string())
638        }
639      },
640      ParagraphElement::InlineCode(n) => {
641        if self.html {
642          format!("<code class=\"mech-inline-code\">{}</code>", n.to_string().trim())
643        } else {
644          format!("`{}`", n.to_string())
645        }
646      },
647      ParagraphElement::InlineMechCode(code) => {
648        let result = self.mech_code(&vec![(code.clone(),None)]);
649        if self.html {
650          format!("<span class=\"mech-inline-mech-code-formatted\">{}</span>", result)
651        } else {
652          format!("{{{}}}", result)
653        }
654      },
655      ParagraphElement::EvalInlineMechCode(expr) => {
656        let code_id = self.inline_eval_id();
657        let result = self.expression(expr);
658        if self.html {
659          let element_id = format!("{}:{}", code_id, self.interpreter_id);
660          format!("<code id=\"{}\" class=\"mech-inline-mech-code\">{}</code>", element_id, result)
661        } else {
662          format!("{{{}}}", result)
663        }
664      },
665    }
666  }
667
668  pub fn fenced_mech_code(&mut self, block: &FencedMechCode) -> String {
669    let parent_interpreter_id = self.interpreter_id;
670    if block.config.namespace != 0 {
671      self.interpreter_id = block.config.namespace;
672    }
673    let block_id = hash_str(&format!("{:?}",block));
674    let namespace_str = &block.config.namespace_str;
675    let mut src = String::new();
676    for (code,cmmnt) in &block.code {
677      let c = match code {
678        MechCode::Comment(cmnt) => self.comment(cmnt),
679        MechCode::Expression(expr) => self.expression(expr),
680        MechCode::FsmSpecification(fsm_spec) => self.fsm_specification(fsm_spec),
681        MechCode::FsmImplementation(fsm_impl) => self.fsm_implementation(fsm_impl),
682        MechCode::FunctionDefine(func_def) => self.function_define(func_def),
683        MechCode::Statement(stmt) => self.statement(stmt),
684        x => format!("{{{:?}}}", x)
685      };
686      let formatted_comment = match cmmnt {
687        Some(cmmt) => self.comment(cmmt),
688        None => String::new(),
689      };
690      if self.html {
691        src.push_str(&format!("<span class=\"mech-code\">{}{}</span>", c, formatted_comment));
692      } else {
693        src.push_str(&format!("{}{}\n", c, formatted_comment));
694      }
695    }
696    let intrp_id = self.interpreter_id;
697    self.interpreter_id = parent_interpreter_id;
698    let disabled_tag = match block.config.disabled {
699      true => "disabled".to_string(),
700      false => "".to_string(),
701    };
702    if self.html {
703      let (out_node,_) = block.code.last().unwrap();
704      let output_id = hash_str(&format!("{:?}", out_node));
705      let style_attr = match &block.options {
706        Some(option_map) if !option_map.elements.is_empty() => {
707          let style_str = option_map
708            .elements
709            .iter()
710            .filter(|(k, _)| k.to_string() != "output")
711            .map(|(k, v)| {
712              let clean_value = v.to_string().trim_matches('"').to_string();
713              format!("{}: {}", k.to_string(), clean_value)
714            })
715            .collect::<Vec<_>>()
716            .join("; ");
717          if style_str.is_empty() {
718            "".to_string()
719          } else {
720            format!(" style=\"{}\"", style_str)
721          }
722        }
723        _ => "".to_string(),
724      };
725      if block.config.disabled {
726        format!("<div class=\"mech-code-block disabled\"{}>{}</div>", style_attr, src)
727      } else if block.config.hidden {
728        // Print it, but give it a hidden class so it can be toggled visible via JS
729        format!("<div class=\"mech-code-block hidden\"{}>{}</div>", style_attr, src)
730      } else {
731        let namespace_str = if namespace_str.is_empty() {
732          "".to_string()
733        } else {
734          format!("<div class=\"mech-code-block-namespace\"><a href=\"#{}\">{}</a></div>", block_id, namespace_str)
735        };
736        let output_node = if block.config.output {
737          format!("<div class=\"mech-block-output\" id=\"{}:{}\"></div>", output_id, intrp_id)
738        } else {
739          "".to_string()
740        };
741        let block_class = if block.config.output {
742          "mech-fenced-mech-block"
743        } else {
744          "mech-fenced-mech-block no-output"
745        };
746        format!("<div id=\"{}\" class=\"{}\"{}>
747          {}
748          <div class=\"mech-code-block\">{}</div>
749          {}
750        </div>", block_id, block_class, style_attr, namespace_str, src, output_node)
751      }
752    } else {
753      format!("```mech{}\n{}\n```", src, format!(":{}", disabled_tag))
754    }
755  }
756
757  pub fn image(&mut self, node: &Image) -> String {
758    self.figure_num += 1;
759
760    let src = node.src.to_string();
761    let caption_p = match &node.caption {
762      Some(caption) => self.paragraph(caption),
763      None => "".to_string(),
764    };
765
766    let figure_label = format!("Fig {}.{}", self.h2_num, self.figure_num);
767    let image_id = hash_str(&src);
768    let figure_id = hash_str(&figure_label);
769
770    if self.html {
771      let style_attr = match &node.style {
772        Some(option_map) if !option_map.elements.is_empty() => {
773          let style_str = option_map
774            .elements
775            .iter()
776            .map(|(k, v)| {
777              let clean_value = v.to_string().trim_matches('"').to_string();
778              format!("{}: {}", k.to_string(), clean_value)
779            })
780            .collect::<Vec<_>>()
781            .join("; ");
782          format!(" style=\"{}\"", style_str)
783        }
784        _ => "".to_string(),
785      };
786      format!(
787"<figure id=\"{}\" class=\"mech-figure\">
788  <img id=\"{}\" class=\"mech-image\" src=\"{}\"{} />
789  <figcaption class=\"mech-figure-caption\">
790    <strong class=\"mech-figure-label\">{}</strong> {}
791  </figcaption>
792</figure>",figure_id, image_id, src, style_attr, figure_label, caption_p)
793    } else {
794      let style_str = match &node.style {
795        Some(option_map) if !option_map.elements.is_empty() => {
796          let inner = option_map
797            .elements
798            .iter()
799            .map(|(k, v)| {
800              let clean_value = v.to_string().trim_matches('"').to_string();
801              format!("{}: \"{}\"", k.to_string(), clean_value)
802            })
803            .collect::<Vec<_>>()
804            .join(", ");
805        format!("{{{}}}", inner)
806      }
807      _ => "".to_string(),
808    };
809
810    format!("![{}]({}){}", caption_p, src, style_str)
811    }
812  }
813
814  pub fn figures(&mut self, node: &FigureTable) -> String {
815    self.figure_num += 1;
816    let figure_label = format!("Fig {}.{}", self.h2_num, self.figure_num);
817    let figure_id = hash_str(&format!("{}-{:?}", figure_label, node.rows));
818
819    let mut figure_ix = 0usize;
820    let mut captions: Vec<String> = vec![];
821
822    if self.html {
823      let mut rows_html = String::new();
824      for row in &node.rows {
825        rows_html.push_str(&format!(
826          "<div class=\"mech-figure-table-row\" style=\"grid-template-columns: repeat({}, minmax(0, 1fr));\">",
827          row.len().max(1)
828        ));
829        for figure in row {
830          let label = ((b'a' + (figure_ix as u8)) as char).to_string();
831          let img_id = hash_str(&format!("{}-{}-{}", figure_label, figure_ix, figure.src.to_string()));
832          rows_html.push_str(&format!(
833            "<div class=\"mech-figure-table-cell\"><div class=\"mech-figure-panel\"><span class=\"mech-figure-subfigure-label\">{}</span><img id=\"{}\" class=\"mech-image mech-figure-grid-image\" src=\"{}\" /></div></div>",
834            label,
835            img_id,
836            figure.src.to_string(),
837          ));
838          captions.push(format!(
839            "<span class=\"mech-figure-caption-ref\">({})</span> <span class=\"mech-figure-caption-text\">{}</span>",
840            label,
841            figure.caption.to_string()
842          ));
843          figure_ix += 1;
844        }
845        rows_html.push_str("</div>");
846      }
847      let caption_block = captions.join(" ");
848      format!(
849        "<figure id=\"{}\" class=\"mech-figure-table\"><div class=\"mech-figure-grid\">{}</div><figcaption class=\"mech-figure-caption mech-figure-table-caption\"><strong class=\"mech-figure-label\">{}</strong> {}</figcaption></figure>",
850        figure_id, rows_html, figure_label, caption_block
851      )
852    } else {
853      let mut lines: Vec<String> = vec![];
854      for row in &node.rows {
855        let mut line = String::from("|");
856        for figure in row {
857          line.push(' ');
858          line.push_str(&format!("![{}]({})", self.paragraph(&figure.caption), figure.src.to_string()));
859          line.push_str(" |");
860          let label = ((b'a' + (figure_ix as u8)) as char).to_string();
861          captions.push(format!("({}) {}", label, self.paragraph(&figure.caption)));
862          figure_ix += 1;
863        }
864        lines.push(line);
865      }
866      format!("{}\n{} {}\n", lines.join("\n"), figure_label, captions.join(" "))
867    }
868  }
869
870
871  pub fn abstract_el(&mut self, node: &Vec<Paragraph>) -> String {
872    let abstract_paragraph = node.iter().map(|p| self.paragraph(p)).collect::<String>();
873    if self.html {
874      format!("<div id=\"abstract\" class=\"mech-abstract\">{}</div>", abstract_paragraph)
875    } else {
876      format!("{}\n", abstract_paragraph)
877    }
878  }
879
880  pub fn equation(&mut self, node: &Token) -> String {
881    let id = hash_str(&format!("equation-{}",node.to_string()));
882    if self.html {
883      format!("<div id=\"{}\" equation=\"{}\" class=\"mech-equation\"></div>",id, node.to_string())
884    } else {
885      format!("$$ {}\n", node.to_string())
886    }
887  }
888
889  pub fn diagram(&mut self, node: &Token) -> String {
890    let id = hash_str(&format!("diagram-{}",node.to_string()));
891    if self.html {
892      format!("<div id=\"{}\" class=\"mech-diagram mermaid\">{}</div>",id, node.to_string())
893    } else {
894      format!("```{{diagram}}\n{}\n```", node.to_string())
895    }
896  }
897
898  pub fn citation(&mut self, node: &Citation) -> String {
899    let id = hash_str(&format!("{}",node.id.to_string()));
900    let parsed_citation = self.citation_paragraph_with_optional_link(&node.text);
901    let citation_num = match self.citation_map.get(&id) {
902      Some(&num) => num,
903      None => {
904        self.citation_num += 1;
905        let next_num = self.citation_num;
906        self.citation_map.insert(id, next_num);
907        next_num
908      }
909    };
910    self.citations.resize(self.citation_num, String::new());
911    let formatted_citation = if self.html {
912      let citation_body = match parsed_citation {
913        Ok((citation_text, Some(link))) => format!(
914          "<a href=\"{}\" class=\"mech-citation-external-link\" target=\"_blank\" rel=\"noopener noreferrer\"><span class=\"mech-citation-link-text\">{}</span><span class=\"mech-citation-link-icon-wrap\" aria-hidden=\"true\">&nbsp;<span class=\"mech-citation-link-icon\"></span></span></a>",
915          link,
916          citation_text,
917        ),
918        Ok((citation_text, None)) => format!("<span class=\"mech-citation-text\">{}</span>", citation_text),
919        Err(err) => format!("<span class=\"mech-error\">{}</span>", err.display_message()),
920      };
921      format!("<div id=\"{}\" class=\"mech-citation\">
922      <div class=\"mech-citation-id\">[{}]:</div>
923      {}
924    </div>",id, citation_num, citation_body)
925    } else {
926      let citation_text = match parsed_citation {
927        Ok((citation_text, Some(link))) => format!("{} {}", citation_text, link),
928        Ok((citation_text, None)) => citation_text,
929        Err(err) => format!("ERROR: {}", err.display_message()),
930      };
931      format!("[{}]: {}",node.id.to_string(), citation_text)
932    };
933    self.citations[citation_num - 1] = formatted_citation;
934    String::new()
935  }
936
937  pub fn float(&mut self, node: &Box<SectionElement>, float_dir: &FloatDirection) -> String {
938    let mut src = "".to_string();
939    let id = hash_str(&format!("float-{:?}",*node));
940    let (float_class,float_sigil) = match float_dir {
941      FloatDirection::Left => ("mech-float left","<<"),
942      FloatDirection::Right => ("mech-float right",">>"),
943    };
944    let el = self.section_element(node);
945    if self.html {
946      format!("<div id=\"{}\" class=\"{}\">{}</div>",id,float_class,el)
947    } else {
948      format!("{}{}\n",float_sigil, el)
949    }
950  }
951
952  pub fn info_block(&mut self, node: &Vec<Paragraph>) -> String {
953    let info_paragraph = node.iter().map(|p| self.paragraph(p)).collect::<String>();
954    if self.html {
955      format!("<div class=\"mech-info-block\">{}</div>",info_paragraph)
956    } else {
957      format!("(i)> {}\n",info_paragraph)
958    }
959  }
960
961  pub fn question_block(&mut self, node: &Vec<Paragraph>) -> String {
962    let question_paragraph = node.iter().map(|p| self.paragraph(p)).collect::<String>();
963    if self.html {
964      format!("<div class=\"mech-question-block\">{}</div>",question_paragraph)
965    } else {
966      format!("(?)> {}\n",question_paragraph)
967    }
968  }
969
970  pub fn success_block(&mut self, node: &Vec<Paragraph>) -> String {
971    let success_paragraph = node.iter().map(|p| self.paragraph(p)).collect::<String>();
972    if self.html {
973      format!("<div class=\"mech-success-block\">{}</div>",success_paragraph)
974    } else {
975      format!("(✓)>> {}\n",success_paragraph)
976    }
977  }
978
979  pub fn warning_block(&mut self, node: &Vec<Paragraph>) -> String {
980    let warning_paragraph = node.iter().map(|p| self.paragraph(p)).collect::<String>();
981    if self.html {
982      format!("<div class=\"mech-warning-block\">{}</div>",warning_paragraph)
983    } else {
984      format!("(!)>> {}\n",warning_paragraph)
985    }
986  }
987
988  pub fn idea_block(&mut self, node: &Vec<Paragraph>) -> String {
989    let idea_paragraph = node.iter().map(|p| self.paragraph(p)).collect::<String>();
990    if self.html {
991      format!("<div class=\"mech-idea-block\">{}</div>",idea_paragraph)
992    } else {
993      format!("(*)> {}\n",idea_paragraph)
994    }
995  }
996
997  pub fn error_block(&mut self, node: &Vec<Paragraph>) -> String {
998    let error_paragraph = node.iter().map(|p| self.paragraph(p)).collect::<String>();
999    if self.html {
1000      format!("<div class=\"mech-error-block\">{}</div>",error_paragraph)
1001    } else {
1002      format!("(✗)>> {}\n",error_paragraph)
1003    }
1004  }
1005
1006  pub fn mika(&mut self, node: &(Mika, Option<MikaSection>)) -> String {
1007    let (mika, section) = node;
1008    let mika_str = format!("<div class=\"mech-mika\">{}</div>", mika.to_string());
1009    if self.html {
1010      match section {
1011        Some(sec) => {
1012          let parent_interpreter_id = self.interpreter_id;
1013          let mika_interp_id = Self::mika_interpreter_id(parent_interpreter_id, node);
1014          self.interpreter_id = mika_interp_id;
1015          let mut sec_str = "".to_string();
1016          for el in &sec.elements.elements {
1017            let section_element = self.section_element(el);
1018            sec_str.push_str(&section_element);
1019          }
1020          self.interpreter_id = parent_interpreter_id;
1021          format!("<div class=\"mech-mika-section\">{} {}</div>", mika_str, sec_str)
1022        },
1023        None => mika_str,
1024      }
1025    } else {
1026      mika_str
1027    }
1028  }
1029
1030  pub fn prompt(&mut self, node: &SectionElement) -> String {
1031    let prompt_str = self.section_element(node);
1032    if self.html {
1033      format!("<div class=\"mech-prompt\"><span class=\"mech-prompt-sigil\">>:</span>{}</div>", prompt_str)
1034    } else {
1035      format!(">: {}\n", prompt_str)
1036    }
1037  }
1038
1039  pub fn inline_para_el(&mut self, node: &Paragraph, class_name: &str) -> String {
1040    let text = self.inline_paragraph(node);
1041    if self.html {
1042      format!("<span class=\"{}\">{}</span>", class_name, text)
1043    } else {
1044      text
1045    }
1046  }
1047
1048  pub fn byline_el(&mut self, node: &Paragraph) -> String {
1049    let byline = self.paragraph(node);
1050    if self.html {
1051      format!("<div class=\"mech-byline\">{}</div>", byline)
1052    } else {
1053      byline
1054    }
1055  }
1056
1057  pub fn synopsis_el(&mut self, node: &Paragraph) -> String {
1058    let summary = self.paragraph(node);
1059    if self.html {
1060      format!("<div class=\"mech-summary\">{}</div>", summary)
1061    } else {
1062      summary
1063    }
1064  }
1065
1066  pub fn hero_el(&mut self, node: &SectionElement) -> String {
1067    let hero = self.section_element(node);
1068    if self.html {
1069      format!("<div class=\"mech-hero-img\">{}</div>", hero)
1070    } else {
1071      hero
1072    }
1073  }
1074    
1075  pub fn section_element(&mut self, node: &SectionElement) -> String {
1076    match node {
1077      SectionElement::Abstract(n) => self.abstract_el(n),
1078      SectionElement::QuoteBlock(n) => self.quote_block(n),
1079      SectionElement::SuccessBlock(n) => self.success_block(n),
1080      SectionElement::IdeaBlock(n) => self.idea_block(n),
1081      SectionElement::InfoBlock(n) => self.info_block(n),
1082      SectionElement::WarningBlock(n) => self.warning_block(n),
1083      SectionElement::ErrorBlock(n) => self.error_block(n),
1084      SectionElement::QuestionBlock(n) => self.question_block(n),
1085      SectionElement::Citation(n) => self.citation(n),
1086      SectionElement::CodeBlock(n) => self.code_block(n),
1087      SectionElement::Comment(n) => self.comment(n),
1088      SectionElement::Diagram(n) => self.diagram(n),
1089      SectionElement::Equation(n) => self.equation(n),
1090      SectionElement::Prompt(n) => self.prompt(n),
1091      SectionElement::FencedMechCode(n) => self.fenced_mech_code(n),
1092      SectionElement::Float((n,f)) => self.float(n,f),
1093      SectionElement::Footnote(n) => self.footnote(n),
1094      SectionElement::Grammar(n) => self.grammar(n),
1095      SectionElement::FigureTable(n) => self.figures(n),
1096      SectionElement::Image(n) => self.image(n),
1097      SectionElement::List(n) => self.list(n),
1098      SectionElement::MechCode(n) => self.mech_code(n),
1099      SectionElement::Mika(n) => self.mika(n),
1100      SectionElement::Paragraph(n) => self.paragraph(n),
1101      SectionElement::Subtitle(n) => self.subtitle(n),
1102      SectionElement::Table(n) => self.mechdown_table(n),
1103      SectionElement::ThematicBreak => self.thematic_break(),
1104      SectionElement::Error(src, range) => self.section_error(src.clone(), range),
1105    }
1106  }
1107
1108  pub fn section_error(&mut self, src: Token, range: &SourceRange) -> String {
1109    if self.html {
1110      let mut error_str = String::new();
1111      error_str.push_str(&format!("<div class=\"mech-section-error\">\n"));
1112      error_str.push_str(&format!("<strong>Error in section at range {:?}-{:?}:</strong>\n", range.start, range.end));
1113      error_str.push_str(&format!("<pre class=\"mech-error-source\">{}</pre>\n", src.to_string()));
1114      error_str.push_str("</div>\n");
1115      error_str  
1116    } else {
1117      let mut error_str = String::new();
1118      error_str.push_str(&format!("Error in section at range {:?}-{:?}:\n", range.start, range.end));
1119      error_str.push_str(&format!("{} ", src.to_string()));
1120      error_str.push_str("\n");
1121      error_str
1122    }
1123  }
1124
1125  pub fn footnote(&mut self, node: &Footnote) -> String {
1126    let (id_name, paragraphs) = node;
1127    let note_paragraph = paragraphs.iter().map(|p| self.paragraph(p)).collect::<String>();
1128    let id: u64 = hash_str(&format!("footnote-{}",id_name.to_string()));
1129    if self.html {
1130      let footnote_num = match self.footnote_map.get(&id) {
1131        Some(existing_num) => *existing_num,
1132        None => {
1133          self.footnote_num += 1;
1134          let next_num = self.footnote_num;
1135          self.footnote_map.insert(id, next_num);
1136          next_num
1137        },
1138      };
1139      self.footnotes.resize(self.footnote_num, String::new());
1140      self.footnotes[footnote_num - 1] = format!("<div class=\"mech-footnote\" id=\"{}\">
1141        <div class=\"mech-footnote-id\">{}:</div>
1142        {}
1143      </div>",id, footnote_num, note_paragraph);
1144      String::new()
1145    } else {
1146      format!("[^{}]: {}\n",id_name.to_string(), note_paragraph)
1147    }
1148  }
1149
1150  pub fn quote_block(&mut self, node: &Vec<Paragraph>) -> String {
1151    let quote_paragraph = node.iter().map(|p| self.paragraph(p)).collect::<String>();
1152    if self.html {
1153      format!("<blockquote class=\"mech-block-quote\">{}</blockquote>",quote_paragraph)
1154    } else {
1155      format!("> {}\n",quote_paragraph)
1156    }
1157  }
1158
1159  pub fn thematic_break(&mut self) -> String {
1160    if self.html {
1161      format!("<hr class=\"mech-thematic-break\"/>")
1162    } else {
1163      format!("***\n")
1164    }
1165  }
1166
1167  pub fn mechdown_table(&mut self, node: &MarkdownTable) -> String {
1168    if self.html {
1169      self.mechdown_table_html(node)
1170    } else {
1171      self.mechdown_table_string(node)
1172    }
1173  }
1174
1175
1176  pub fn mechdown_table_string(&mut self, node: &MarkdownTable) -> String {
1177    // Helper to render a row of Paragraphs as `| ... | ... |`
1178    fn render_row(cells: &[Paragraph], f: &mut impl FnMut(&Paragraph) -> String) -> String {
1179        let mut row = String::from("|");
1180        for cell in cells {
1181            row.push_str(" ");
1182            row.push_str(&f(cell));
1183            row.push_str(" |");
1184        }
1185        row
1186    }
1187
1188    // Render header
1189    let header_line = render_row(&node.header, &mut |p| self.paragraph(p));
1190
1191    // Render alignment row
1192    let mut align_line = String::from("|");
1193    for align in &node.alignment {
1194        let spec = match align {
1195            ColumnAlignment::Left => ":---",
1196            ColumnAlignment::Center => ":---:",
1197            ColumnAlignment::Right => "---:",
1198        };
1199        align_line.push_str(&format!(" {} |", spec));
1200    }
1201
1202    // Render body rows
1203    let mut body_lines = vec![];
1204    for row in &node.rows {
1205        body_lines.push(render_row(row, &mut |p| self.paragraph(p)));
1206    }
1207
1208    // Join everything
1209    let mut markdown = String::new();
1210    markdown.push_str(&header_line);
1211    markdown.push('\n');
1212    markdown.push_str(&align_line);
1213    markdown.push('\n');
1214    for line in body_lines {
1215        markdown.push_str(&line);
1216        markdown.push('\n');
1217    }
1218
1219    markdown
1220}
1221
1222
1223  pub fn mechdown_table_html(&mut self, node: &MarkdownTable) -> String {
1224    let mut html = String::new();
1225    html.push_str("<table class=\"mech-table\">");
1226
1227    // Render the header
1228    if !node.header.is_empty() {
1229      html.push_str("<thead><tr class=\"mech-table-header\">");
1230      for (i, cell) in node.header.iter().enumerate() {
1231        let align = match node.alignment.get(i) {
1232          Some(ColumnAlignment::Left) => "left",
1233          Some(ColumnAlignment::Center) => "center",
1234          Some(ColumnAlignment::Right) => "right",
1235          None => "left", // Default alignment
1236        };
1237        let cell_html = self.paragraph(cell);
1238        html.push_str(&format!(
1239          "<th class=\"mech-table-header-cell {}\">{}</th>", 
1240          align, cell_html
1241        ));
1242      }
1243      html.push_str("</tr></thead>");
1244    }
1245
1246    // Render the rows
1247    html.push_str("<tbody>");
1248    for (row_index, row) in node.rows.iter().enumerate() {
1249      let row_class = if row_index % 2 == 0 { "mech-table-row-even" } else { "mech-table-row-odd" };
1250      html.push_str(&format!("<tr class=\"mech-table-row {}\">", row_class));
1251      for (i, cell) in row.iter().enumerate() {
1252        let align = match node.alignment.get(i) {
1253          Some(ColumnAlignment::Left) => "left",
1254          Some(ColumnAlignment::Center) => "center",
1255          Some(ColumnAlignment::Right) => "right",
1256          None => "left", // Default alignment
1257        };
1258        let cell_html = self.paragraph(cell);
1259        html.push_str(&format!(
1260          "<td class=\"mech-table-cell {}\">{}</td>", 
1261          align, cell_html
1262        ));
1263      }
1264      html.push_str("</tr>");
1265    }
1266    html.push_str("</tbody>");
1267    html.push_str("</table>");
1268    html
1269  }
1270
1271  pub fn grammar(&mut self, node: &Grammar) -> String {
1272    let mut src = "".to_string();
1273    for rule in node.rules.iter() {
1274      let id = self.grammar_identifier(&rule.name);
1275      let rule_str = format!("{} <span class=\"mech-grammar-define-op\">:=</span>{}", id, self.grammar_expression(&rule.expr));
1276      if self.html {
1277        src = format!("{}<div class=\"mech-grammar-rule\">{} ;</div>",src,rule_str);
1278      } else {
1279        src = format!("{}{};\n",src,rule_str); 
1280      }
1281    }
1282    if self.html {
1283      format!("<div class=\"mech-grammar\">{}</div>",src)
1284    } else {
1285      src
1286    }
1287  }
1288
1289  fn grammar_identifier(&mut self, node: &GrammarIdentifier) -> String {
1290    let name = node.name.to_string();
1291    if self.html {
1292      format!("<span id=\"{}\" class=\"mech-grammar-identifier\">{}</span>",hash_str(&name), name)
1293    } else {
1294      name
1295    }
1296  }
1297
1298  fn grammar_expression(&mut self, node: &GrammarExpression) -> String {
1299    let expr = match node {
1300      GrammarExpression::List(element,deliniator) => {
1301        let el = self.grammar_expression(element);
1302        let del = self.grammar_expression(deliniator);
1303        if self.html {
1304          format!("<span class=\"mech-grammar-list\">[<span class=\"mech-grammar-list-element\">{}</span>,<span class=\"mech-grammar-list-deliniator\">{}</span>]</span>",el,del)
1305        } else {
1306          format!("[{},{}]",el,del)
1307        }
1308      },
1309      GrammarExpression::Range(start,end) => {
1310        if self.html {
1311          format!("<span class=\"mech-grammar-range\"><span class=\"mech-grammar-terminal\">\"{}\"</span><span class=\"mech-grammar-range-op\">..</span><span class=\"mech-grammar-terminal\">\"{}\"</span></span>", start.to_string(), end.to_string())
1312        } else {
1313          format!("{}..{}", start.to_string(), end.to_string())
1314        }
1315      }
1316      GrammarExpression::Choice(choices) => {
1317        let mut src = "".to_string();
1318        let inline = choices.len() <= 3;
1319        for (i, choice) in choices.iter().enumerate() {
1320          let choice_str = self.grammar_expression(choice);
1321          if i == 0 {
1322            src = format!("{}", choice_str);
1323          } else {
1324            if self.html {
1325              src = if inline {
1326                format!("{} <span class=\"mech-grammar-choice-op\">|</span> {}", src, choice_str)
1327              } else {
1328                format!("{}<div class=\"mech-grammar-choice\"><span class=\"mech-grammar-choice-op\">|</span> {}</div>", src, choice_str)
1329              };
1330            } else {
1331              src = format!("{} | {}", src, choice_str);
1332            }
1333          }
1334        }
1335        src
1336      },
1337      GrammarExpression::Sequence(seq) => {
1338        let mut src = "".to_string();
1339        let inline = seq.len() <= 3;
1340        for (i, factor) in seq.iter().enumerate() {
1341          let factor_str = self.grammar_expression(factor);
1342          if i == 0 {
1343        src = format!("{}", factor_str);
1344          } else {
1345        if self.html {
1346          src = if inline {
1347            format!("{}, {}", src, factor_str)
1348          } else {
1349            format!("{}<div class=\"mech-grammar-sequence\"><span class=\"mech-grammar-sequence-op\">,</span> {}</div>", src, factor_str)
1350          };
1351        } else {
1352          src = format!("{}, {}", src, factor_str);
1353        }
1354          }
1355        }
1356        src
1357      },
1358      GrammarExpression::Repeat0(expr) => {
1359        let inner_expr = self.grammar_expression(expr);
1360        if self.html {
1361          format!("<span class=\"mech-grammar-repeat0-op\">*</span>{}", inner_expr)
1362        } else {
1363          format!("*{}", inner_expr)
1364        }
1365      },
1366      GrammarExpression::Repeat1(expr) => {
1367        let inner_expr = self.grammar_expression(expr);
1368        if self.html {
1369          format!("<span class=\"mech-grammar-repeat1-op\">+</span>{}", inner_expr)
1370        } else {
1371          format!("+{}", inner_expr)
1372        }
1373      },
1374      GrammarExpression::Optional(expr) => {
1375        let inner_expr = self.grammar_expression(expr);
1376        if self.html {
1377          format!("<span class=\"mech-grammar-optional-op\">?</span>{}", inner_expr)
1378        } else {
1379          format!("?{}", inner_expr)
1380        }
1381      },
1382      GrammarExpression::Peek(expr) => {
1383        let inner_expr = self.grammar_expression(expr);
1384        if self.html {
1385          format!("<span class=\"mech-grammar-peek-op\">&gt;</span>{}", inner_expr)
1386        } else {
1387          format!(">{}", inner_expr)
1388        }
1389      },
1390      GrammarExpression::Not(expr) => {
1391        let inner_expr = self.grammar_expression(expr);
1392        if self.html {
1393          format!("<span class=\"mech-grammar-not-op\">¬</span>{}", inner_expr)
1394        } else {
1395          format!("¬{}", inner_expr)
1396        }
1397      },
1398      GrammarExpression::Terminal(token) => {
1399        if self.html {
1400          format!("<span class=\"mech-grammar-terminal\">\"{}\"</span>", token.to_string())
1401        } else {
1402          format!("\"{}\"", token.to_string())
1403        }
1404      },
1405      GrammarExpression::Group(expr) => {
1406        let inner_expr = self.grammar_expression(expr);
1407        if self.html {
1408          format!("<span class=\"mech-grammar-group\">(<span class=\"mech-grammar-group-content\">{}</span>)</span>", inner_expr)
1409        } else {
1410          format!("({})", inner_expr)
1411        }
1412      },
1413      GrammarExpression::Definition(id) => {
1414        let name = id.name.to_string();
1415        if self.html {
1416          format!("<span class=\"mech-grammar-definition\"><a href=\"#{}\">{}</a></span>",hash_str(&name), name)
1417        } else {
1418          name
1419        }
1420      },
1421    };
1422    
1423    if self.html {
1424      format!("<span class=\"mech-grammar-expression\">{}</span>", expr)
1425    } else {
1426      expr
1427    }
1428  }
1429
1430  pub fn code_block(&mut self, node: &Token) -> String {
1431    let code = node.to_string();
1432    if self.html {
1433      let escaped_code = code
1434        .replace("&", "&amp;")
1435        .replace("<", "&lt;")
1436        .replace(">", "&gt;");
1437      format!("<pre class=\"mech-code-block\">{}</pre>",escaped_code)
1438    } else {
1439      format!("```\n{}\n```",code)
1440    }
1441  }
1442
1443  pub fn comment(&mut self, node: &Comment) -> String {
1444    let comment_text = self.paragraph(&node.paragraph);
1445    if self.html {
1446      format!("<span class=\"mech-comment\"><span class=\"mech-comment-sigil\">--</span>{}</span>", comment_text)
1447    } else {
1448      format!("{}\n",comment_text)
1449    }
1450  }
1451
1452  pub fn list(&mut self, node: &MDList) -> String {
1453    match node {
1454      MDList::Ordered(ordered_list) => self.ordered_list(ordered_list),
1455      MDList::Unordered(unordered_list) => self.unordered_list(unordered_list),
1456      MDList::Check(check_list) => self.check_list(check_list),
1457    }
1458  }
1459
1460  pub fn check_list(&mut self, node: &CheckList) -> String {
1461    let mut lis = "".to_string();
1462    for (i, ((checked, item), sublist)) in node.iter().enumerate() {
1463      let it = self.paragraph(item);
1464      if self.html {
1465        lis = format!("{}<li class=\"mech-check-list-item\"><input type=\"checkbox\" {}>{}</li>", lis, if *checked { "checked" } else { "" }, it);
1466      } else {
1467        lis = format!("{}* [{}] {}\n", lis, if *checked { "x" } else { " " }, it);
1468      }
1469      match sublist {
1470        Some(sublist) => {
1471          let sublist_str = self.list(sublist);
1472          lis = format!("{}{}", lis, sublist_str);
1473        },
1474        None => {},
1475      }
1476    }
1477    if self.html {
1478      format!("<ul class=\"mech-check-list\">{}</ul>", lis)
1479    } else {
1480      lis
1481    }
1482  }
1483
1484  pub fn ordered_list(&mut self, node: &OrderedList) -> String {
1485    let mut lis = "".to_string();
1486    for (i, ((num,item),sublist)) in node.items.iter().enumerate() {
1487      let it = self.paragraph(item);
1488      if self.html {
1489        lis = format!("{}<li class=\"mech-ol-list-item\">{}</li>",lis,it);
1490      } else {
1491        lis = format!("{}{}. {}\n",lis,i+1,it);
1492      }
1493      match sublist {
1494        Some(sublist) => {
1495          let sublist_str = self.list(sublist);
1496          lis = format!("{}{}",lis,sublist_str);
1497        },
1498        None => {},
1499      }
1500    }
1501    if self.html {
1502      format!("<ol start=\"{}\" class=\"mech-ordered-list\">{}</ol>",node.start.to_string(),lis)
1503    } else {
1504      lis
1505    }
1506  }
1507
1508  pub fn unordered_list(&mut self, node: &UnorderedList) -> String {
1509    let mut lis = "".to_string();
1510    for (i, ((bullet, item),sublist)) in node.iter().enumerate() {
1511      let it = self.paragraph(item);
1512      match (bullet, self.html) {
1513        (Some(bullet_tok),true) => lis = format!("{}<li data-bullet=\"{}\" class=\"mech-list-item-emoji\">{}</li>",lis,bullet_tok.to_string(),it),
1514        (None,true) => lis = format!("{}<li class=\"mech-ul-list-item\">{}</li>",lis,it),
1515        (_,false) => lis = format!("{}* {}\n",lis,it),
1516      }
1517      match sublist {
1518        Some(sublist) => {
1519          let sublist_str = self.list(sublist);
1520          lis = format!("{}{}",lis,sublist_str);
1521        },
1522        None => {},
1523      }
1524    }
1525    if self.html {
1526      format!("<ul class=\"mech-unordered-list\">{}</ul>",lis)
1527    } else {
1528      lis
1529    }
1530  }
1531
1532  pub fn mech_code(&mut self, node: &Vec<(MechCode,Option<Comment>)>) -> String {
1533    let mut src = String::new();
1534    for (code,cmmnt) in node {
1535      let c = match code {
1536        MechCode::Comment(cmnt) => self.comment(cmnt),
1537        MechCode::Expression(expr) => self.expression(expr),
1538        MechCode::FsmImplementation(fsm_impl) => self.fsm_implementation(fsm_impl),
1539        MechCode::FsmSpecification(fsm_spec) => self.fsm_specification(fsm_spec),
1540        MechCode::FunctionDefine(func_def) => self.function_define(func_def),
1541        MechCode::Statement(stmt) => self.statement(stmt),
1542        x => todo!("Unhandled MechCode: {:#?}", x),
1543      };
1544      let formatted_comment = match cmmnt {
1545        Some(cmmt) => self.comment(cmmt),
1546        None => String::new(),
1547      };
1548      if self.html {
1549        src.push_str(&format!("<span class=\"mech-code\">{}{}</span>", c, formatted_comment));
1550      } else {
1551        src.push_str(&format!("{}{}\n", c, formatted_comment));
1552      }
1553    }
1554    if self.html {
1555      format!("<span class=\"mech-code-block\">{}</span>",src)
1556    } else {
1557      src
1558    }
1559  }
1560
1561  pub fn fsm_implementation(&mut self, node: &FsmImplementation) -> String {
1562    let name = node.name.to_string();
1563    let mut input = "".to_string();
1564    for (i, ident) in node.input.iter().enumerate() {
1565      let v = self.var(ident);
1566      if i == 0 {
1567        input = format!("{}", v);
1568      } else {
1569        input = format!("{}, {}", input, v);
1570      }
1571    }
1572    let start = self.pattern(&node.start);
1573    let mut arms = "".to_string();
1574    for (i, arm) in node.arms.iter().enumerate() {
1575      let a = self.fsm_arm(arm, i == node.arms.len() - 1);
1576      if i == 0 {
1577        arms = format!("{}", a);
1578      } else {
1579        arms = format!("{}{}", arms, a);
1580      }
1581    }
1582    if self.html {
1583      format!("<div class=\"mech-fsm-implementation\">
1584        <div class=\"mech-fsm-implementation-header\">
1585          <span class=\"mech-fsm-sigil\">#</span>
1586          <span class=\"mech-fsm-name\">{}</span>
1587          <span class=\"mech-left-paren\">(</span>
1588          <span class=\"mech-fsm-input\">{}</span>
1589          <span class=\"mech-right-paren\">)</span>
1590          <span class=\"mech-fsm-define-op\">→</span>
1591          <span class=\"mech-fsm-start\">{}</span>
1592        </div>
1593        <div class=\"mech-fsm-arms\">
1594          {}
1595        </div>
1596      </div>",name,input,start,arms)
1597    } else {
1598      format!("#{}({}) {} {}\n{}", name, input, "->" , start, arms)
1599    }
1600  }
1601
1602  pub fn fsm_arm(&mut self, node: &FsmArm, last: bool) -> String {
1603    let arm = match node {
1604      FsmArm::Comment(comment) => self.comment(comment),
1605      FsmArm::Guard(pattern, guards) => {
1606        let p = self.pattern(pattern);
1607        let mut gs = "".to_string();
1608        for (i, guard) in guards.iter().enumerate() {
1609          let g = self.guard(guard);
1610          if i == 0 {
1611            if self.html {
1612              gs = format!("<div class=\"mech-fsm-guard-arm\">├ {}</div>", g);
1613            } else {
1614              gs = format!("    ├ {}\n", g);
1615            }
1616          } else if i == guards.len() - 1 {
1617            if self.html {
1618              gs = format!("{}<div class=\"mech-fsm-guard-arm\">└ {}</div>", gs, g);
1619            } else {
1620              gs = format!("{}    └ {}", gs, g); 
1621            }
1622          } else {  
1623            if self.html {
1624              gs = format!("{}<div class=\"mech-fsm-guard-arm\">├ {}</div>", gs, g);
1625            } else {
1626              gs = format!("{}    ├ {}\n", gs, g);
1627            }
1628          }
1629        }
1630        if self.html {
1631          format!("<div class=\"mech-fsm-arm-guard\">
1632            <span class=\"mech-fsm-start\">{}</span>
1633            <span class=\"mech-fsm-guards\">{}</span>
1634          </div>",p,gs)
1635        } else {
1636          format!("  {}\n{}", p, gs)
1637        }
1638      },
1639      FsmArm::Transition(pattern, transitions) => {
1640        let p = self.pattern(pattern);
1641        let mut ts = "".to_string();
1642        for (i, transition) in transitions.iter().enumerate() {
1643          let t = self.transition(transition);
1644          if i == 0 {
1645            ts = format!("{}", t);
1646          } else {
1647            ts = format!("{}{}", ts, t);
1648          }
1649        }
1650        if self.html {
1651          format!("<div class=\"mech-fsm-arm\">
1652            <span class=\"mech-fsm-arm-pattern\">{}</span>
1653            <span class=\"mech-fsm-arm-transitions\">{}</span>
1654          </div>",p,ts)
1655        } else {
1656          format!("  {}{}", p, ts)
1657        }
1658      },
1659    };
1660    if self.html {
1661      if last {
1662        format!("<div class=\"mech-fsm-arm-last\">{}.</div>",arm)
1663      } else {
1664        format!("<div class=\"mech-fsm-arm\">{}</div>",arm)
1665      }
1666    } else {
1667      if last { 
1668        format!("{}.", arm)
1669      } else {
1670        format!("{}\n", arm)
1671      }
1672    }
1673  }
1674
1675  pub fn guard(&mut self, node: &Guard) -> String {
1676    let condition = self.pattern(&node.condition);
1677    let mut transitions = "".to_string();
1678    for (i, transition) in node.transitions.iter().enumerate() {
1679      let t = self.transition(transition);
1680      if i == 0 {
1681        transitions = format!("{}", t);
1682      } else {
1683        transitions = format!("{}{}", transitions, t);
1684      }
1685    }
1686    if self.html {
1687      format!("<div class=\"mech-guard\">
1688        <span class=\"mech-guard-condition\">{}</span>
1689        <span class=\"mech-guard-transitions\">{}</span>
1690      </div>",condition,transitions)
1691    } else {
1692      format!("{}{}", condition, transitions)
1693    }
1694  }
1695 
1696
1697  pub fn pattern(&mut self, node: &Pattern) -> String {
1698    let p = match node {
1699      Pattern::Wildcard => {
1700        if self.html {
1701          format!("<span class=\"mech-pattern-wildcard\">*</span>")
1702        } else {
1703          format!("*")
1704        }
1705      },
1706      Pattern::Tuple(tpl) => self.pattern_tuple(tpl),
1707      Pattern::Array(arr) => self.pattern_array(arr),
1708      Pattern::Expression(expr) => self.expression(expr),
1709      Pattern::TupleStruct(tuple_struct) => self.pattern_tuple_struct(tuple_struct),
1710    };
1711    if self.html {
1712      format!("<span class=\"mech-pattern\">{}</span>",p)
1713    } else {
1714      p
1715    }
1716  }
1717
1718  pub fn pattern_tuple_struct(&mut self, node: &PatternTupleStruct) -> String {
1719    let name = node.name.to_string();
1720    let mut patterns = "".to_string();
1721    for (i, pattern) in node.patterns.iter().enumerate() {
1722      let p = self.pattern(pattern);
1723      if i == 0 {
1724        patterns = format!("{}", p);
1725      } else {
1726        patterns = format!("{}, {}", patterns, p);
1727      }
1728    }
1729    if self.html {
1730      format!("<span class=\"mech-tuple-struct\">
1731        <span class=\"mech-tuple-struct-sigil\">:</span>
1732        <span class=\"mech-tuple-struct-name\">{}</span>
1733        <span class=\"mech-left-paren\">(</span>
1734        <span class=\"mech-tuple-struct-patterns\">{}</span>
1735        <span class=\"mech-right-paren\">)</span>
1736      </span>",name,patterns)
1737    } else {
1738      format!(":{}({})", name, patterns)
1739    }
1740  }
1741
1742  pub fn pattern_tuple(&mut self, node: &PatternTuple) -> String {
1743    let mut patterns = "".to_string();
1744    for (i, pattern) in node.0.iter().enumerate() {
1745      let p = self.pattern(pattern);
1746      if i == 0 {
1747        patterns = format!("{}", p);
1748      } else {
1749        patterns = format!("{}, {}", patterns, p);
1750      }
1751    }
1752    if self.html {
1753      format!("<span class=\"mech-pattern-tuple\">
1754        <span class=\"mech-left-paren\">(</span>
1755        <span class=\"mech-patterns\">{}</span>
1756        <span class=\"mech-right-paren\">)</span>
1757      </span>",patterns)
1758    } else {
1759      format!("({})", patterns)
1760    }
1761  }
1762
1763  pub fn transition(&mut self, node: &Transition) -> String {
1764    match node {
1765      Transition::Next(pattern) => {
1766        if self.html {
1767          format!("<span class=\"mech-transition-next\">→ {}</span>",self.pattern(pattern))
1768        } else {
1769          format!(" {} {}", "->", self.pattern(pattern))
1770        }
1771      }
1772      Transition::Output(pattern) => {
1773        if self.html {
1774          format!("<span class=\"mech-transition-output\">⇒ {}</span>",self.pattern(pattern))
1775        } else {
1776          format!(" {} {}", "=>", self.pattern(pattern))
1777        }
1778      }
1779      Transition::Async(pattern) => {
1780        if self.html {
1781          format!("<span class=\"mech-transition-async\">↝ {}</span>",self.pattern(pattern))
1782        } else {
1783          format!(" {} {}", "~>", self.pattern(pattern))
1784
1785        }
1786      }
1787      Transition::Statement(stmt) => {
1788        if self.html {
1789          format!("<span class=\"mech-transition-statement\">→ {}</span>",self.statement(stmt))
1790        } else {
1791          format!(" {} {}", "->", self.statement(stmt))
1792        }
1793      }
1794      Transition::CodeBlock(code) => {
1795        let mut code_str = "".to_string();
1796        let formatted = self.mech_code(code);
1797        if self.html {
1798          code_str.push_str(&format!("<span class=\"mech-transition-code\">→ {}</span>", formatted));
1799        } else {
1800          code_str.push_str(&format!(" {} {}", "->", formatted));
1801        }
1802        code_str
1803      }
1804    }
1805  }
1806
1807  pub fn fsm_specification(&mut self, node: &FsmSpecification) -> String {
1808    let name = node.name.to_string();
1809    let mut input = "".to_string();
1810    for (i, var) in node.input.iter().enumerate() {
1811      let v = self.var(var);
1812      if i == 0 {
1813        input = format!("{}", v);
1814      } else {
1815        input = format!("{}, {}", input, v);
1816      }
1817    }
1818    let output = match &node.output {
1819      Some(kind) => format!(" {} {}", "⇒", self.kind_annotation(&kind.kind)),
1820      None => "".to_string(),
1821    };
1822    let mut states = "".to_string();
1823    for (i, state) in node.states.iter().enumerate() {
1824      let v = self.state_definition(state);
1825      let state_arm = if node.states.len() == 1 {
1826        format!("{} {}", "└", v)
1827      } else if i == 0 {
1828        format!("{} {}", "├", v)
1829      } else if i == node.states.len() - 1 {
1830        format!("{} {}{}", "└", v, ".")
1831      } else {
1832        format!("{} {}", "├", v)
1833      };
1834      if self.html {
1835        states = format!("{}<span class=\"mech-fsm-state\">{}</span>",states,state_arm);
1836      } else {
1837        states = format!("{}    {}\n",states,state_arm);
1838      }
1839    }
1840    if self.html {
1841      format!("<div class=\"mech-fsm-specification\">
1842      <div class=\"mech-fsm-specification-header\">
1843        <span class=\"mech-fsm-sigil\">#</span>
1844        <span class=\"mech-fsm-name\">{}</span>
1845        <span class=\"mech-left-paren\">(</span>
1846        <span class=\"mech-fsm-input\">{}</span>
1847        <span class=\"mech-right-paren\">)</span>
1848        <span class=\"mech-fsm-output\">{}</span>
1849        <span class=\"mech-fsm-define-op\">:=</span>
1850      </div>
1851      <div class=\"mech-fsm-states\">{}</div>
1852      </div>",name,input,output,states)
1853    } else {
1854      format!("#{}({}){} {}\n{}", name, input, output, ":=", states)
1855    }
1856  }
1857
1858  pub fn function_define(&mut self, node: &FunctionDefine) -> String {
1859    let name = node.name.to_string();
1860    let input = node
1861      .input
1862      .iter()
1863      .map(|arg| self.function_argument(arg))
1864      .collect::<Vec<_>>()
1865      .join(", ");
1866
1867    if !node.match_arms.is_empty() {
1868      let output_kind = node
1869        .output
1870        .first()
1871        .map(|arg| self.kind_annotation(&arg.kind.kind))
1872        .unwrap_or_else(|| "<_>".to_string());
1873
1874      let arms = node
1875        .match_arms
1876        .iter()
1877        .enumerate()
1878        .map(|(ix, arm)| {
1879          let branch = if ix + 1 == node.match_arms.len() { "└" } else { "├" };
1880          let pattern = self.pattern(&arm.pattern);
1881          let expression = self.expression(&arm.expression);
1882          if self.html {
1883            format!("<div class=\"mech-function-match-arm\"><span class=\"mech-function-branch\">{}</span><span class=\"mech-function-pattern\">{}</span> <span class=\"mech-function-arrow\">⇒</span><span class=\"mech-function-expression\">{}</span></div>", branch, pattern, expression)
1884          } else {
1885            format!("  {} {} => {}", branch, pattern, expression)
1886          }
1887        })
1888        .collect::<Vec<_>>()
1889        .join(if self.html { "" } else { "\n" });
1890
1891      if self.html {
1892        format!("<div class=\"mech-function-define\"><div class=\"mech-function-signature\"><span class=\"mech-function-name\">{}</span><span class=\"mech-left-paren\">(</span><span class=\"mech-function-input\">{}</span><span class=\"mech-right-paren\">)</span> <span class=\"mech-function-arrow\">⇒</span> <span class=\"mech-function-output\">{}</span></div><div class=\"mech-function-match-arms\">{}<span class=\"mech-function-period\">.</span></div></div>", name, input, output_kind, arms)
1893      } else {
1894        format!("{}({}) => {}\n{}.", name, input, output_kind, arms)
1895      }
1896    } else {
1897      let output = if node.output.len() == 1 {
1898        self.function_argument(&node.output[0])
1899      } else {
1900        format!(
1901          "({})",
1902          node.output
1903            .iter()
1904            .map(|arg| self.function_argument(arg))
1905            .collect::<Vec<_>>()
1906            .join(", ")
1907        )
1908      };
1909
1910      let statements = node
1911        .statements
1912        .iter()
1913        .map(|stmt| self.statement(stmt))
1914        .collect::<Vec<_>>()
1915        .join(if self.html { "" } else { "\n" });
1916
1917      if self.html {
1918        format!("<div class=\"mech-function-define\"><div class=\"mech-function-signature\"><span class=\"mech-function-name\">{}</span><span class=\"mech-left-paren\">(</span><span class=\"mech-function-input\">{}</span><span class=\"mech-right-paren\">)</span> <span class=\"mech-function-equals\">=</span> <span class=\"mech-function-output\">{}</span> <span class=\"mech-define-op\">:=</span></div><div class=\"mech-function-body\">{}.</div></div>", name, input, output, statements)
1919      } else {
1920        format!("{}({}) = {} :=\n{}.", name, input, output, statements)
1921      }
1922    }
1923  }
1924
1925  pub fn function_argument(&mut self, node: &FunctionArgument) -> String {
1926    let name = node.name.to_string();
1927    let kind = self.kind_annotation(&node.kind.kind);
1928    if self.html {
1929      format!("<span class=\"mech-function-argument\"><span class=\"mech-function-argument-name\">{}</span><span class=\"mech-function-argument-kind\">{}</span></span>", name, kind)
1930    } else {
1931      format!("{}{}", name, kind)
1932    }
1933  }
1934
1935  pub fn state_definition(&mut self, node: &StateDefinition) -> String {
1936    let name = node.name.to_string();
1937    let mut state_variables = "".to_string();
1938    match &node.state_variables {
1939      Some(vars) => {
1940        for (i, var) in vars.iter().enumerate() {
1941          let v = self.var(var);
1942          if i == 0 {
1943            state_variables = format!("{}", v);
1944          } else {
1945            state_variables = format!("{}, {}", state_variables, v);
1946          }
1947        }
1948      },
1949      None => {}
1950    }
1951    if self.html {
1952      format!("<div class=\"mech-state-definition\">
1953      <span class=\"mech-state-name\"><span class=\"mech-state-name-sigil\">:</span>{}</span>
1954      <span class=\"mech-left-paren\">(</span>
1955      <span class=\"mech-state-variables\">{}</span>
1956      <span class=\"mech-right-paren\">)</span>
1957      </div>",name,state_variables)
1958    } else {
1959      format!("{}({})", name, state_variables)
1960    }
1961  }
1962
1963  pub fn variable_define(&mut self, node: &VariableDefine) -> String {
1964    let mut mutable = if node.mutable {
1965      "~".to_string()
1966    } else {
1967      "".to_string()
1968    };
1969    let var = self.var(&node.var);
1970    let expression = self.expression(&node.expression);
1971    if self.html {
1972      format!("<span class=\"mech-variable-define\"><span class=\"mech-variable-mutable\">{}</span>{}<span class=\"mech-variable-assign-op\">:=</span>{}</span>",mutable, var, expression)
1973    } else {
1974      format!("{}{} {} {}", mutable, var, ":=", expression)
1975    }
1976  }
1977
1978  pub fn statement(&mut self, node: &Statement) -> String {
1979    let s = match node {
1980      Statement::VariableDefine(var_def) => self.variable_define(var_def),
1981      Statement::OpAssign(op_asgn) => self.op_assign(op_asgn),
1982      Statement::VariableAssign(var_asgn) => self.variable_assign(var_asgn),
1983      Statement::TupleDestructure(tpl_dstrct) => self.tuple_destructure(tpl_dstrct),
1984      Statement::KindDefine(kind_def) => self.kind_define(kind_def),
1985      Statement::EnumDefine(enum_def) => self.enum_define(enum_def),
1986      _ => todo!(),
1987      //Statement::FsmDeclare(fsm_decl) => self.fsm_declare(fsm_decl, src),
1988    };
1989    if self.html {
1990      format!("<span class=\"mech-statement\">{}</span>",s)
1991    } else {
1992      format!("{}", s)
1993    }
1994  }
1995
1996  pub fn enum_define(&mut self, node: &EnumDefine) -> String {
1997    let name = node.name.to_string();
1998    let mut variants = "".to_string();
1999    for (i, variant) in node.variants.iter().enumerate() {
2000      if i == 0 {
2001        if self.html {
2002          variants = format!("<span class=\"mech-enum-variant\">{}</span>", self.enum_variant(variant));
2003        } else {
2004          variants = format!("{}", self.enum_variant(variant));
2005        }
2006      } else {
2007        if self.html {
2008          variants = format!("{}<span class=\"mech-enum-variant-sep\">|</span><span class=\"mech-enum-variant\">{}</span>", variants, self.enum_variant(variant));
2009        } else {
2010          variants = format!("{} | {}", variants, self.enum_variant(variant));
2011        }
2012      }
2013    }
2014    if self.html {
2015      format!("<span class=\"mech-enum-define\"><span class=\"mech-kind-annotation\">&lt;<span class=\"mech-enum-name\">{}</span>&gt;</span><span class=\"mech-enum-define-op\">:=</span><span class=\"mech-enum-variants\">{}</span></span>",name,variants)
2016    } else {
2017      format!("<{}> := {}", name, variants)
2018    }
2019  }
2020
2021  pub fn enum_variant(&mut self, node: &EnumVariant) -> String {
2022    let name = node.name.to_string();
2023    let mut kind = "".to_string();
2024    match &node.value {
2025      Some(k) => {
2026        kind = self.kind_annotation(&k.kind);
2027      },
2028      None => {},
2029    }
2030    if self.html {
2031      format!("<span class=\"mech-enum-variant\"><span class=\"mech-enum-variant-name\">:{}</span><span class=\"mech-enum-variant-kind\">{}</span></span>",name,kind)
2032    } else {
2033      format!(":{}{}", name, kind)
2034    }
2035  }
2036
2037  pub fn kind_define(&mut self, node: &KindDefine) -> String {
2038    let name = node.name.to_string();
2039    let kind = self.kind_annotation(&node.kind.kind);
2040    if self.html {
2041      format!("<span class=\"mech-kind-define\"><span class=\"mech-kind-annotation\">&lt;<span class=\"mech-kind\">{}</span>&gt;</span><span class=\"mech-kind-define-op\">:=</span><span class=\"mech-kind-annotation\">{}</span></span>",name,kind)
2042    } else {
2043      format!("<{}> := {}", name, kind)
2044    }
2045  }
2046
2047  pub fn tuple_destructure(&mut self, node: &TupleDestructure) -> String {
2048    let mut vars = "".to_string();
2049    for (i, var) in node.vars.iter().enumerate() {
2050      let v = var.to_string();
2051      if i == 0 {
2052        if self.html {
2053          let id = format!("{}:{}",hash_str(&v),self.interpreter_id);
2054          vars = format!("<span id=\"{}\" class=\"mech-var-name mech-clickable\">{}</span>",id,v);
2055        } else {
2056          vars = format!("{}", v);
2057        }
2058      } else {
2059        if self.html {
2060          let id = format!("{}:{}",hash_str(&v),self.interpreter_id);
2061          vars = format!("{}, <span id=\"{}\" class=\"mech-var-name mech-clickable\">{}</span>", vars, id, v);
2062        } else {
2063          vars = format!("{}, {}", vars, v);
2064        }
2065      }
2066    }
2067    let expression = self.expression(&node.expression);
2068    if self.html {
2069      format!("<span class=\"mech-tuple-destructure\"><span class=\"mech-tuple-vars\">({})</span><span class=\"mech-assign-op\">:=</span><span class=\"mech-tuple-expression\">{}</span></span>",vars,expression)
2070    } else {
2071      format!("({}) := {}", vars, expression)
2072    }
2073  }
2074
2075  pub fn variable_assign(&mut self, node: &VariableAssign) -> String {
2076    let target = self.slice_ref(&node.target);
2077    let expression = self.expression(&node.expression);
2078    if self.html {
2079      format!("<span class=\"mech-variable-assign\">
2080        <span class=\"mech-target\">{}</span>
2081        <span class=\"mech-assign-op\">=</span>
2082        <span class=\"mech-expression\">{}</span>
2083      </span>",target,expression)
2084    } else {
2085      format!("{} = {}", target, expression)
2086    }
2087  }
2088
2089  pub fn op_assign(&mut self, node: &OpAssign) -> String {
2090    let target = self.slice_ref(&node.target);
2091    let op = self.op_assign_op(&node.op);
2092    let expression = self.expression(&node.expression);
2093    if self.html {
2094      format!("<span class=\"mech-op-assign\"><span class=\"mech-target\">{}</span><span class=\"mech-op\">{}</span><span class=\"mech-expression\">{}</span></span>",target,op,expression)
2095    } else {
2096      format!("{} {} {}", target, op, expression)
2097    }
2098  }
2099
2100  pub fn op_assign_op(&mut self, node: &OpAssignOp) -> String {
2101    let op = match node {
2102      OpAssignOp::Add => "+=".to_string(),
2103      OpAssignOp::Div => "/=".to_string(),
2104      OpAssignOp::Exp => "^=".to_string(),
2105      OpAssignOp::Mod => "%=".to_string(),
2106      OpAssignOp::Mul => "*=".to_string(),
2107      OpAssignOp::Sub => "-=".to_string(),
2108    };
2109    if self.html {
2110      format!("<span class=\"mech-op-assign-op\">{}</span>",op)
2111    } else {
2112      format!("{}", op)
2113    }
2114  }
2115
2116  pub fn slice_ref(&mut self, node: &SliceRef) -> String {
2117    let name = node.name.to_string();
2118    let mut subscript = "".to_string();
2119    match &node.subscript {
2120      Some(subs) => {
2121        for sub in subs.iter() {
2122          let s = self.subscript(sub);
2123          subscript = format!("{}{}", subscript, s);
2124        }
2125      },
2126      None => {},
2127    }
2128    let id = format!("{}:{}",hash_str(&name),self.interpreter_id);
2129    if self.html {
2130      format!("<span class=\"mech-slice-ref\"><span id=\"{}\" class=\"mech-var-name mech-clickable\">{}</span><span class=\"mech-subscript\">{}</span></span>",id,name,subscript)
2131    } else {
2132      format!("{}{}", name, subscript)
2133    }
2134  }
2135
2136  pub fn expression(&mut self, node: &Expression) -> String {
2137    let e = match node {
2138      Expression::Var(var) => self.var(var),
2139      Expression::Formula(factor) => self.factor(factor),
2140      Expression::Literal(literal) => self.literal(literal),
2141      Expression::Structure(structure) => self.structure(structure),
2142      Expression::Slice(slice) => self.slice(slice),
2143      Expression::FunctionCall(function_call) => self.function_call(function_call),
2144      Expression::Range(range) => self.range_expression(range),
2145      Expression::SetComprehension(set_comp) => self.set_comprehension(set_comp),
2146      Expression::MatrixComprehension(matrix_comp) => self.matrix_comprehension(matrix_comp),
2147      Expression::Match(match_expr) => self.match_expression(match_expr),
2148      Expression::FsmPipe(fsm_pipe) => self.fsm_pipe(fsm_pipe),
2149      x => todo!("Unhandled Expression: {:#?}", x),
2150    };
2151    if self.html {
2152      format!("<span class=\"mech-expression\">{}</span>",e)
2153    } else {
2154      format!("{}", e)
2155    }
2156  }
2157
2158  pub fn pattern_array(&mut self, node: &PatternArray) -> String {
2159    let mut parts: Vec<String> = vec![];
2160    for p in &node.prefix {
2161      parts.push(self.pattern(p));
2162    }
2163    if let Some(spread) = &node.spread {
2164      match spread.kind {
2165        PatternArraySpreadKind::Spread => {
2166          parts.push("…".to_string());
2167          if let Some(binding) = &spread.binding {
2168            parts.push(self.pattern(binding));
2169          }
2170        }
2171        PatternArraySpreadKind::Rest => {
2172          parts.push("|".to_string());
2173          if let Some(binding) = &spread.binding {
2174            parts.push(self.pattern(binding));
2175          }
2176        }
2177      }
2178    }
2179    for p in &node.suffix {
2180      parts.push(self.pattern(p));
2181    }
2182    format!("[{}]", parts.join(" "))
2183  }
2184
2185  pub fn match_expression(&mut self, node: &MatchExpression) -> String {
2186    let source = self.expression(&node.source);
2187    let mut lines = vec![format!("{}?", source)];
2188    lines.push(if self.html {
2189      "<div class=\"mech-match-arms\">".to_string()
2190    } else {
2191      "".to_string()
2192    });
2193    for (ix, arm) in node.arms.iter().enumerate() {
2194      let last_arm = ix + 1 == node.arms.len();
2195      let (branch, terminal) = if last_arm {("└", ".")} else {("├", "")};
2196      let pattern = self.pattern(&arm.pattern);
2197      let guard = arm
2198        .guard
2199        .as_ref()
2200        .map(|expr| format!(", {}", self.expression(expr)))
2201        .unwrap_or_default();
2202      let expr = self.expression(&arm.expression);
2203      if self.html {
2204        lines.push(format!(
2205          "<div class=\"mech-match-arm\">\
2206            <span class=\"mech-match-branch\">{}</span> \
2207            <span class=\"mech-match-pattern\">{}{}</span> \
2208            <span class=\"mech-match-arrow\">⇒</span> \
2209            <span class=\"mech-match-expression\">{}</span>\
2210            <span class=\"mech-match-terminal\">{}</span>\
2211          </div>",
2212          branch, pattern, guard, expr, terminal
2213        ));
2214      } else {
2215        lines.push(format!("{}{}{} ⇒ {}{}", branch, pattern, guard, expr, terminal));
2216      }
2217    }
2218    lines.push(if self.html {
2219      "</div>".to_string()
2220    } else {
2221      "".to_string()
2222    });
2223    if self.html {
2224      format!(
2225        "<span class=\"mech-match-expression\">\
2226          <span class=\"mech-match-source\">{}<span class=\"mech-match-op\">?</span></span>{}\
2227          </span>",
2228        source,
2229        lines.iter().skip(1).cloned().collect::<Vec<_>>().join("")
2230      )
2231    } else {
2232      lines.join("\n")
2233    }
2234  }
2235
2236  pub fn fsm_instance(&mut self, node: &FsmInstance) -> String {
2237    let name = node.name.to_string();
2238    let mut args = "".to_string();
2239    match &node.args {
2240      Some(arguments) => {
2241        for (i, (ident, expr)) in arguments.iter().enumerate() {
2242          let e = self.expression(expr);
2243          let arg_str = match ident {
2244            Some(id) => format!("{}: {}", id.to_string(), e),
2245            None => e,
2246          };
2247          if i == 0 {
2248            args = format!("{}", arg_str);
2249          } else {
2250            args = format!("{}, {}", args, arg_str);
2251          }
2252        }
2253        if self.html {
2254          format!("<span class=\"mech-fsm-instance\"><span class=\"mech-fsm-name\">#{}</span><span class=\"mech-left-paren\">(</span><span class=\"mech-fsm-args\">{}</span><span class=\"mech-right-paren\">)</span></span>",name,args)
2255        } else {
2256          format!("#{}({})", name, args)
2257        }
2258      },
2259      None => {
2260        if self.html {
2261          format!("<span class=\"mech-fsm-instance\"><span class=\"mech-fsm-name\">#{}</span></span>",name)
2262        } else {
2263          format!("#{}", name)
2264        }
2265      },
2266    }
2267  }
2268
2269  pub fn fsm_pipe(&mut self, node: &FsmPipe) -> String {
2270    let start = self.fsm_instance(&node.start);
2271    let mut transitions = "".to_string();
2272    for (i, transition) in node.transitions.iter().enumerate() {
2273      let t = self.transition(transition);
2274      if i == 0 {
2275        transitions = format!("{}", t);
2276      } else {
2277        transitions = format!("{}{}", transitions, t);
2278      }
2279    }
2280    if self.html {
2281      format!("<span class=\"mech-fsm-pipe\"><span class=\"mech-fsm-pipe-start\">{}</span><span class=\"mech-fsm-pipe-transitions\">{}</span></span>",start,transitions)
2282    } else {
2283      format!("{}{}", start, transitions)
2284    }
2285  }
2286
2287  pub fn set_comprehension(&mut self, node: &SetComprehension) -> String {
2288    let expr = self.expression(&node.expression);
2289
2290    let qualifiers = node
2291      .qualifiers
2292      .iter()
2293      .map(|q| self.comprehension_qualifier(q))
2294      .collect::<Vec<_>>()
2295      .join(", ");
2296
2297    if self.html {
2298      format!(
2299        "<span class=\"mech-set-comprehension\">\
2300          <span class=\"mech-set-open\">{{</span>\
2301          <span class=\"mech-set-expression\">{}</span>\
2302          <span class=\"mech-set-bar\"> | </span>\
2303          <span class=\"mech-set-qualifiers\">{}</span>\
2304          <span class=\"mech-set-close\">}}</span>\
2305        </span>",
2306        expr, qualifiers
2307      )
2308    } else {
2309      format!("{{ {} | {} }}", expr, qualifiers)
2310    }
2311  }
2312
2313  pub fn matrix_comprehension(&mut self, node: &MatrixComprehension) -> String {
2314    let expr = self.expression(&node.expression);
2315    let quals = node.qualifiers
2316      .iter()
2317      .map(|q| self.comprehension_qualifier(q))
2318      .collect::<Vec<_>>()
2319      .join(", ");
2320
2321    if self.html {
2322      format!(
2323        "<span class=\"mech-matrix-comprehension\">
2324          <span class=\"mech-bracket start\">[</span>
2325          <span class=\"mech-comp-expr\">{}</span>
2326          <span class=\"mech-comp-bar\">|</span>
2327          <span class=\"mech-comp-quals\">{}</span>
2328          <span class=\"mech-bracket end\">]</span>
2329        </span>",
2330        expr, quals
2331      )
2332    } else {
2333      format!("[ {} | {} ]", expr, quals)
2334    }
2335  }
2336
2337  pub fn comprehension_qualifier(&mut self, node: &ComprehensionQualifier) -> String {
2338    match node {
2339      ComprehensionQualifier::Generator((pattern, expr)) => {
2340        self.generator(pattern, expr)
2341      }
2342      ComprehensionQualifier::Let(var_def) => {
2343        self.variable_define(var_def)
2344      }
2345      ComprehensionQualifier::Filter(expr) => {
2346        self.expression(expr)
2347      }
2348    }
2349  }
2350
2351  pub fn generator(&mut self, pattern: &Pattern, expr: &Expression) -> String {
2352    let p = self.pattern(pattern);
2353    let e = self.expression(expr);
2354
2355    if self.html {
2356      format!(
2357        "<span class=\"mech-generator\">\
2358          <span class=\"mech-generator-pattern\">{}</span>\
2359          <span class=\"mech-generator-arrow\"> ← </span>\
2360          <span class=\"mech-generator-expression\">{}</span>\
2361        </span>",
2362        p, e
2363      )
2364    } else {
2365      format!("{} ← {}", p, e)
2366    }
2367  }
2368
2369  pub fn range_expression(&mut self, node: &RangeExpression) -> String {
2370    let start = self.factor(&node.start);
2371    let operator = match &node.operator {
2372      RangeOp::Inclusive => "..=".to_string(),
2373      RangeOp::Exclusive => "..".to_string(),
2374    };
2375    let terminal = self.factor(&node.terminal);
2376    let increment = match &node.increment {
2377      Some((op, factor)) => {
2378        let o = match op {
2379          RangeOp::Inclusive => "..=".to_string(),
2380          RangeOp::Exclusive => "..".to_string(),
2381        };
2382        let f = self.factor(factor);
2383        if self.html {
2384          format!("<span class=\"mech-range-increment\">{}{}</span>",o,f)
2385        } else {
2386          format!("{}{}", o, f)
2387        }
2388      },
2389      None => "".to_string(),
2390    };
2391    if self.html {
2392      format!("<span class=\"mech-range-expression\"><span class=\"mech-range-start\">{}</span><span class=\"mech-range-operator\">{}</span><span class=\"mech-range-terminal\">{}</span>{}</span>",start,operator,terminal,increment)
2393    } else {
2394      format!("{}{}{}{}", start, operator, terminal, increment)
2395    }
2396  }
2397
2398  pub fn function_call(&mut self, node: &FunctionCall) -> String {
2399    let name = node.name.to_string();
2400    let mut args = "".to_string();
2401    for (i, arg) in node.args.iter().enumerate() {
2402      let a = self.argument(arg);
2403      if i == 0 {
2404        args = format!("{}", a);
2405      } else {
2406        args = format!("{}, {}", args, a);
2407      }
2408    }
2409    let id = format!("{}:{}",hash_str(&name),self.interpreter_id);
2410    if self.html {
2411      format!("<span class=\"mech-function-call\"><span id=\"{}\" class=\"mech-function-name mech-clickable\">{}</span><span class=\"mech-left-paren\">(</span><span class=\"mech-argument-list\">{}</span><span class=\"mech-right-paren\">)</span></span>",id,name,args)
2412    } else {
2413      format!("{}({})", name, args)
2414    }
2415  }
2416
2417  pub fn argument(&mut self, node: &(Option<Identifier>, Expression)) -> String {
2418    let (name, expr) = node;
2419    let n = match name {
2420      Some(ident) => ident.to_string(),
2421      None => "".to_string(),
2422    };
2423    let e = self.expression(expr);
2424    if self.html {
2425      format!("<span class=\"mech-argument\"><span class=\"mech-argument-name\">{}</span><span class=\"mech-argument-expression\">{}</span></span>",n,e)
2426    } else {
2427      format!("{}{}", n, e)
2428    }
2429  }
2430
2431  pub fn slice(&mut self, node: &Slice) -> String {
2432    let name = node.name.to_string();
2433    let mut subscript = "".to_string();
2434    for (i, sub) in node.subscript.iter().enumerate() {
2435      let s = self.subscript(sub);
2436      subscript = format!("{}{}", subscript, s);
2437    }
2438    let id = format!("{}:{}",hash_str(&name),self.interpreter_id);
2439    if self.html {
2440      format!("<span class=\"mech-slice\"><span id=\"{}\" class=\"mech-var-name mech-clickable\">{}</span><span class=\"mech-subscript\">{}</span></span>",id,name,subscript)
2441    } else {
2442      format!("{}{}", name, subscript)
2443    }
2444  }
2445
2446  pub fn subscript(&mut self, node: &Subscript) -> String {
2447    match node {
2448      Subscript::Bracket(subs) => self.bracket(subs),
2449      Subscript::Formula(factor) => self.factor(factor),
2450      Subscript::All => self.all(),
2451      Subscript::Dot(ident) => self.dot(ident),
2452      Subscript::Swizzle(idents) => self.swizzle(idents),
2453      Subscript::Range(range) => self.range_expression(range),
2454      Subscript::Brace(subs) => self.brace(subs),
2455      Subscript::DotInt(real) => self.dot_int(real),
2456    }
2457  }
2458
2459  pub fn brace(&mut self, node: &Vec<Subscript>) -> String {
2460    let mut src = "".to_string();
2461    for (i, sub) in node.iter().enumerate() {
2462      let s = self.subscript(sub);
2463      if i == 0 {
2464        src = format!("{}", s);
2465      } else {
2466        src = format!("{},{}", src, s);
2467      }
2468    }
2469    if self.html {
2470      format!("<span class=\"mech-brace\">{{{}}}</span>",src)
2471    } else {
2472      format!("{{{}}}",src)
2473    }
2474  }
2475
2476  pub fn swizzle(&mut self, node: &Vec<Identifier>) -> String {
2477    let mut src = "".to_string();
2478    for (i, ident) in node.iter().enumerate() {
2479      let s = self.dot(ident);
2480      if i == 0 {
2481        src = format!("{}", s);
2482      } else {
2483        src = format!("{},{}", src, s);
2484      }
2485    }
2486    if self.html {
2487      format!("<span class=\"mech-swizzle\">{}</span>",src)
2488    } else {
2489      format!("{}",src)
2490    }
2491  }
2492
2493  pub fn dot_int(&mut self, node: &RealNumber) -> String {
2494    let node_str = match node {
2495      RealNumber::Integer(tkn) => tkn.to_string(),
2496      _ => "".to_string(),
2497    };
2498    if self.html {
2499      format!(".<span class=\"mech-dot-int\">{}</span>",node_str)
2500    } else {
2501      format!(".{}",node_str)
2502    }
2503  } 
2504
2505  pub fn dot(&mut self, node: &Identifier) -> String {
2506    if self.html {
2507      format!(".<span class=\"mech-dot\">{}</span>",node.to_string())
2508    } else {
2509      format!(".{}",node.to_string())
2510    }
2511  }
2512
2513  pub fn all(&mut self) -> String {
2514    if self.html {
2515      format!("<span class=\"mech-all\">:</span>")
2516    } else {
2517      ":".to_string()
2518    }
2519  }
2520
2521  pub fn bracket(&mut self, node: &Vec<Subscript>) -> String {
2522    let mut src = "".to_string();
2523    for (i, sub) in node.iter().enumerate() {
2524      let s = self.subscript(sub);
2525      if i == 0 {
2526        src = format!("{}", s);
2527      } else {
2528        src = format!("{},{}", src, s);
2529      }
2530    }
2531    if self.html {
2532      format!("<span class=\"mech-bracket\">[{}]</span>",src)
2533    } else {
2534      format!("[{}]",src)
2535    }
2536  }
2537
2538  pub fn structure(&mut self, node: &Structure) -> String {
2539    let s = match node {
2540      Structure::Matrix(matrix) => self.matrix(matrix),
2541      Structure::Record(record) => self.record(record),
2542      Structure::Empty => "_".to_string(),
2543      Structure::Table(table) => self.table(table),
2544      Structure::Tuple(tuple) => self.tuple(tuple),
2545      Structure::TupleStruct(tuple_struct) => self.tuple_struct(tuple_struct),
2546      Structure::Set(set) => self.set(set),
2547      Structure::Map(map) => self.map(map),
2548    };
2549    if self.html {
2550      format!("<span class=\"mech-structure\">{}</span>",s)
2551    } else {
2552      format!("{}", s)
2553    }
2554  }
2555
2556  pub fn map(&mut self, node: &Map) -> String {
2557    let mut src = "".to_string();
2558    for (i, mapping) in node.elements.iter().enumerate() {
2559      let m = self.mapping(mapping);
2560      if i == 0 {
2561        src = format!("{}", m);
2562      } else {
2563        src = format!("{}, {}", src, m);
2564      }
2565    }
2566    if self.html {
2567      format!("<span class=\"mech-map\"><span class=\"mech-start-brace\">{{</span>{}<span class=\"mech-end-brace\">}}</span></span>",src)
2568    } else {
2569      format!("{{{}}}", src)
2570    }
2571  }
2572
2573  pub fn mapping(&mut self, node: &Mapping) -> String {
2574    let key = self.expression(&node.key);
2575    let value = self.expression(&node.value);
2576    if self.html {
2577      format!("<span class=\"mech-mapping\"><span class=\"mech-key\">{}</span><span class=\"mech-colon-op\">:</span><span class=\"mech-value\">{}</span></span>",key,value)
2578    } else {
2579      format!("{}: {}", key, value)
2580    }
2581  }
2582
2583  pub fn set(&mut self, node: &Set) -> String {
2584    let mut src = "".to_string();
2585    for (i, element) in node.elements.iter().enumerate() {
2586      let e = self.expression(element);
2587      if i == 0 {
2588        src = format!("{}", e);
2589      } else {
2590        src = format!("{}, {}", src, e);
2591      }
2592    }
2593    if self.html {
2594      format!("<span class=\"mech-set\"><span class=\"mech-start-brace\">{{</span>{}<span class=\"mech-end-brace\">}}</span></span>",src)
2595    } else {
2596      format!("{{{}}}", src)
2597    }
2598  }
2599
2600  pub fn tuple_struct(&mut self, node: &TupleStruct) -> String {
2601    let name = node.name.to_string();
2602    let value = self.expression(&node.value);
2603    if self.html {
2604      format!("
2605        <span class=\"mech-tuple-struct\">
2606        <span class=\"mech-tuple-struct-sigil\">:</span>
2607        <span class=\"mech-tuple-struct-name\">{}</span>
2608        <span class=\"mech-left-paren\">(</span>
2609        <span class=\"mech-tuple-struct-value\">{}</span>
2610        <span class=\"mech-right-paren\">)</span>
2611      </span>", name, value)
2612    } else {
2613      format!("{}{}", name, value)
2614    }
2615  }
2616
2617  pub fn table(&mut self, node: &Table) -> String {
2618    let header = self.table_header(&node.header);
2619    let mut rows = "".to_string();
2620    for (i, row) in node.rows.iter().enumerate() {
2621      let r = self.table_row(row);
2622      if i == 0 {
2623        rows = format!("{}", r);
2624      } else {
2625        rows = format!("{}{}", rows, r);
2626      }
2627    }
2628    if self.html {
2629      format!("<table class=\"mech-table\">{}<tbody class=\"mech-table-body\">{}</tbody></table>",header,rows)
2630    } else {
2631      format!("{}{}", header, rows)
2632    }
2633  }
2634
2635  pub fn table_header(&mut self, node: &TableHeader) -> String {
2636    let mut src = "".to_string();
2637    for (i, field) in node.0.iter().enumerate() {
2638      let f = self.field(field);
2639      if self.html {
2640        src = format!("{}<th class=\"mech-table-field\">{}</th>",src, f);
2641      } else {
2642        src = format!("{}{}",src, f);
2643      }
2644    }
2645    if self.html {
2646      format!("<thead class=\"mech-table-header\"><tr>{}</tr></thead>",src)
2647    } else {
2648      src
2649    }
2650  }
2651
2652  pub fn table_row(&mut self, node: &TableRow) -> String {
2653    let mut src = "".to_string();
2654    for (i, column) in node.columns.iter().enumerate() {
2655      let c = self.table_column(column);
2656      if i == 0 {
2657        src = format!("{}", c);
2658      } else {
2659        src = format!("{} {}", src, c);
2660      }
2661    }
2662    if self.html {
2663      format!("<tr class=\"mech-table-row\">{}</tr>",src)
2664    } else {
2665      src
2666    }
2667  }
2668
2669  pub fn table_column(&mut self, node: &TableColumn) -> String {
2670    let element = self.expression(&node.element);
2671    if self.html {
2672      format!("<td class=\"mech-table-column\">{}</td>",element)
2673    } else {
2674      element
2675    }
2676  }
2677
2678  pub fn field(&mut self, node: &Field) -> String {
2679    let name = node.name.to_string();
2680    let kind = if let Some(kind) = &node.kind {
2681      self.kind_annotation(&kind.kind)
2682    } else {
2683      "".to_string()
2684    };
2685    if self.html {
2686      format!("<div class=\"mech-field\"><span class=\"mech-field-name\">{}</span><span class=\"mech-field-kind\">{}</span></div>",name,kind)
2687    } else {
2688      format!("{}: {}", name, kind)
2689    }
2690  }
2691
2692  pub fn tuple(&mut self, node: &Tuple) -> String {
2693    let mut src = "".to_string();
2694    for (i, element) in node.elements.iter().enumerate() {
2695      let e = self.expression(element);
2696      if i == 0 {
2697        src = format!("{}", e);
2698      } else {
2699        src = format!("{},{}", src, e);
2700      }
2701    }
2702    if self.html {
2703      format!("<span class=\"mech-tuple\"><span class=\"mech-start-paren\">(</span>{}<span class=\"mech-end-paren\">)</span></span>",src)
2704    } else {
2705      format!("({})", src)
2706    }
2707  }
2708
2709  pub fn record(&mut self, node: &Record) -> String {
2710    let mut src = "".to_string();
2711    for (i, binding) in node.bindings.iter().enumerate() {
2712      let b = self.binding(binding);
2713      if i == 0 {
2714        src = format!("{}", b);
2715      } else {
2716        src = format!("{}, {}", src, b);
2717      }
2718    }
2719    if self.html {
2720      format!("<span class=\"mech-record\"><span class=\"mech-start-brace\">{{</span>{}<span class=\"mech-end-brace\">}}</span></span>",src)
2721    } else {
2722      format!("{{{}}}",src)
2723    }
2724  }
2725
2726  pub fn binding(&mut self, node: &Binding) -> String {
2727    let name = node.name.to_string();
2728    let kind = if let Some(kind) = &node.kind {
2729      self.kind_annotation(&kind.kind)
2730    } else {
2731      "".to_string()
2732    };
2733    let value = self.expression(&node.value);
2734    if self.html {
2735      format!("<span class=\"mech-binding\"><span class=\"mech-binding-name\">{}</span><span class=\"mech-binding-kind\">{}</span><span class=\"mech-binding-colon-op\">:</span><span class=\"mech-binding-value\">{}</span></span>",name,kind,value)
2736    } else {
2737      format!("{}{}: {}", name, kind, value)
2738    }
2739  }
2740
2741  pub fn matrix(&mut self, node: &Matrix) -> String {
2742    let mut src = "".to_string();
2743    if node.rows.len() == 0 {
2744      if self.html {
2745        return format!("<span class=\"mech-matrix empty\"><span class=\"mech-bracket start\">[</span><span class=\"mech-bracket end\">]</span></span>");
2746      } else {
2747        return format!("[]");
2748      }
2749    }
2750    let column_count = node.rows[0].columns.len(); // Assume all rows have the same number of columns
2751
2752    for col_index in 0..column_count {
2753        let mut column_elements = Vec::new();
2754        for row in &node.rows {
2755            column_elements.push(&row.columns[col_index]);
2756        }
2757        let c = self.matrix_column_elements(&column_elements);
2758
2759        if col_index == 0 {
2760            src = format!("{}", c);
2761        } else {
2762            src = format!("{} {}", src, c);
2763        }
2764    }
2765
2766    if self.html {
2767        format!("<span class=\"mech-matrix\"><span class=\"mech-bracket start\">[</span>{}<span class=\"mech-bracket end\">]</span></span>", src)
2768    } else {
2769        format!("[{}]", src)
2770    }
2771}
2772
2773pub fn matrix_column_elements(&mut self, column_elements: &[&MatrixColumn]) -> String {
2774    let mut src = "".to_string();
2775    for (i, cell) in column_elements.iter().enumerate() {
2776        let c = self.matrix_column(cell);
2777        if i == 0 {
2778            src = format!("{}", c);
2779        } else {
2780            src = format!("{} {}", src, c);
2781        }
2782    }
2783    if self.html {
2784        format!("<div class=\"mech-matrix-column\">{}</div>", src)
2785    } else {
2786        src
2787    }
2788}
2789
2790
2791  pub fn matrix_row(&mut self, node: &MatrixRow) -> String {
2792    let mut src = "".to_string();
2793    for (i, cell) in node.columns.iter().enumerate() {
2794      let c = self.matrix_column(cell);
2795      if i == 0 {
2796        src = format!("{}", c);
2797      } else { 
2798        src = format!("{} {}", src, c);
2799      }
2800    }
2801    if self.html {
2802      format!("<div class=\"mech-matrix-row\">{}</div>",src)
2803    } else {
2804      src
2805    }
2806  }
2807
2808  pub fn matrix_column(&mut self, node: &MatrixColumn) -> String {
2809    let element = self.expression(&node.element);
2810    if self.html {
2811      format!("<span class=\"mech-matrix-element\">{}</span>",element)
2812    } else {
2813      element
2814    }    
2815  }  
2816
2817  pub fn var(&mut self, node: &Var) -> String {
2818    let annotation = if let Some(kind) = &node.kind {
2819      self.kind_annotation(&kind.kind)
2820    } else {
2821      "".to_string()
2822    };
2823    let name = &node.name.to_string();
2824    let id = format!("{}:{}",hash_str(&name),self.interpreter_id);
2825    if self.html {
2826      format!("<span class=\"mech-var-name mech-clickable\" id=\"{}\">{}</span>{}", id, node.name.to_string(), annotation)
2827    } else {
2828      format!("{}{}", node.name.to_string(), annotation)
2829    }
2830  }
2831
2832  pub fn kind_annotation(&mut self, node: &Kind) -> String {
2833    let kind = self.kind(node);
2834    if self.html {
2835      format!("<span class=\"mech-kind-annotation\">&lt;{}&gt;</span>",kind)
2836    } else {
2837      format!("<{}>", kind)
2838    }
2839  }
2840
2841  pub fn kind(&mut self, node: &Kind) -> String {
2842    let annotation = match node {
2843      Kind::Kind(kind) => {
2844        let kind_kind = self.kind(kind);
2845        if self.html {
2846          format!("<span class=\"mech-kind-annotation\">&lt;{}&gt;</span>",kind_kind)
2847        } else {
2848          format!("<{}>", kind_kind)
2849        }
2850      },
2851      Kind::Option(kind) => {
2852        let k = self.kind(kind);
2853        if self.html {
2854          format!("{}<span class=\"mech-option-question\">?</span>", k)
2855        } else {
2856          format!("{}?", k)
2857        }
2858      },
2859      Kind::Set(kind,size) => {
2860        let k = self.kind(kind);
2861        let size_str = match size{
2862          Some(size) => {
2863            let size_ltrl = self.literal(size);
2864            format!(":{}", size_ltrl)
2865          }
2866          None => "".to_string(),
2867        };
2868        format!("{{{}}}{}", k, size_str)
2869      },
2870      Kind::Any => "*".to_string(),
2871      Kind::Scalar(ident) => ident.to_string(),
2872      Kind::Empty => "_".to_string(),
2873      Kind::Atom(ident) => format!(":{}",ident.to_string()),
2874      Kind::Tuple(kinds) => {
2875        let mut src = "".to_string();
2876        for (i, kind) in kinds.iter().enumerate() {
2877          let k = self.kind(kind);
2878          if i == 0 {
2879            src = format!("{}", k);
2880          } else {
2881            src = format!("{},{}", src, k);
2882          }
2883        }
2884        format!("({})", src)
2885      },
2886      Kind::Matrix((kind, literals)) => {
2887        let mut src = "".to_string();
2888        let k = self.kind(kind);
2889        src = format!("{}", k);
2890        let mut src2 = "".to_string();
2891        for (i, literal) in literals.iter().enumerate() {
2892          let l = self.literal(literal);
2893          if i == 0 {
2894            src2 = format!(":{}", l);
2895          } else {
2896            src2 = format!("{},{}", src2, l);
2897          }
2898        }
2899        format!("[{}]{}", src, src2)
2900      },
2901      Kind::Record(kinds) => {
2902        let mut src = "".to_string();
2903        for (i, (ident, kind)) in kinds.iter().enumerate() {
2904          let k = self.kind(kind);
2905          let ident_s = ident.to_string();
2906          if i == 0 {
2907            src = format!("{}&lt;{}&gt;", ident_s, k);
2908          } else {
2909            src = format!("{},{}&lt;{}&gt;", src, ident_s, k);
2910          }
2911        }
2912        format!("{{{}}}", src)
2913      },
2914      Kind::Table((kinds, literal)) => {
2915        let mut src = "".to_string();
2916        for (i, (ident,kind)) in kinds.iter().enumerate() {
2917          let k = self.kind(kind);
2918          let ident_s = ident.to_string();
2919          if i == 0 {
2920            src = format!("{}&lt;{}&gt;", ident_s, k);
2921          } else {
2922            src = format!("{},{}&lt;{}&gt;", src, ident_s, k);
2923          }
2924        }
2925        let mut src2 = "".to_string();
2926        let sz = match &**literal {
2927          Literal::Empty(_) => "".to_string(),
2928          _ => format!(":{}", self.literal(literal)),
2929        };
2930        format!("|{}|{}", src, sz)
2931      },
2932      Kind::Map(kind1, kind2) => {
2933        let k1 = self.kind(kind1);
2934        let k2 = self.kind(kind2);
2935        format!("{{{}:{}}}", k1, k2)
2936      },
2937    };
2938    if self.html {
2939      format!("<span class=\"mech-kind\">{}</span>",annotation)
2940    } else {
2941      annotation
2942    }
2943  }
2944
2945  pub fn factor(&mut self, node: &Factor) -> String {
2946    let f = match node {
2947      Factor::Term(term) => self.term(term),
2948      Factor::Expression(expr) => self.expression(expr),
2949      Factor::Parenthetical(paren) => {
2950        if self.html {
2951          format!("<span class=\"mech-parenthetical\">({})</span>", self.factor(paren))
2952        } else {
2953          format!("({})", self.factor(&paren))
2954        }
2955      }
2956      Factor::Negate(factor) => {
2957        if self.html {
2958          format!("<span class=\"mech-negate-op\">-</span><span class=\"mech-negate\">{}</span>", self.factor(factor))
2959        } else {
2960          format!("-{}", self.factor(factor))
2961        }
2962      }
2963      Factor::Not(factor) => {
2964        if self.html {
2965          format!("<span class=\"mech-not-op\">¬</span><span class=\"mech-not\">{}</span>", self.factor(factor))
2966        } else {
2967          format!("¬{}", self.factor(factor))
2968        }
2969      }
2970      Factor::Transpose(factor) => {
2971        if self.html {
2972          format!("<span class=\"mech-transpose\">{}</span><span class=\"mech-transpose-op\">'</span>", self.factor(factor))
2973        } else {
2974          format!("{}'", self.factor(factor))
2975        }
2976      }
2977    };
2978    if self.html {
2979      format!("<span class=\"mech-factor\">{}</span>",f)
2980    } else {
2981      f
2982    }
2983  }
2984
2985  pub fn term(&mut self, node: &Term) -> String {
2986    let mut src = self.factor(&node.lhs);
2987    for (formula_operator, rhs) in &node.rhs {
2988      let op = self.formula_operator(formula_operator);
2989      let rhs = self.factor(rhs);
2990      src = format!("{}{}{}", src, op, rhs);
2991    }
2992    if self.html {
2993      format!("<span class=\"mech-term\">{}</span>",src)
2994    } else {
2995      src
2996    }
2997  }
2998
2999  pub fn formula_operator(&mut self, node: &FormulaOperator) -> String {
3000    let f = match node {
3001      FormulaOperator::AddSub(op) => self.add_sub_op(op),
3002      FormulaOperator::MulDiv(op) => self.mul_div_op(op),
3003      FormulaOperator::Power(op) => self.power_op(op),
3004      FormulaOperator::Vec(op) => self.vec_op(op),
3005      FormulaOperator::Comparison(op) => self.comparison_op(op),
3006      FormulaOperator::Logic(op) => self.logic_op(op),
3007      FormulaOperator::Table(op) => self.table_op(op),
3008      FormulaOperator::Set(op) => self.set_op(op),
3009    };
3010    if self.html {
3011      format!("<span class=\"mech-formula-operator\">{}</span>",f)
3012    } else {
3013      format!(" {} ", f)
3014    }
3015  }
3016
3017  pub fn table_op(&mut self, node: &TableOp) -> String {
3018    match node {
3019      TableOp::InnerJoin => "⋈".to_string(),
3020      TableOp::LeftOuterJoin => "⟕".to_string(),
3021      TableOp::RightOuterJoin => "⟖".to_string(),
3022      TableOp::FullOuterJoin => "⟗".to_string(),
3023      TableOp::LeftSemiJoin => "⋉".to_string(),
3024      TableOp::LeftAntiJoin => "▷".to_string(),
3025    }
3026  }
3027
3028  pub fn set_op(&mut self, node: &SetOp) -> String {
3029    match node {
3030      SetOp::Union => "∪".to_string(),
3031      SetOp::Intersection => "∩".to_string(),
3032      SetOp::Difference => "∖".to_string(),
3033      SetOp::Complement => "∁".to_string(),
3034      SetOp::Subset => "⊂".to_string(),
3035      SetOp::Superset => "⊃".to_string(),
3036      SetOp::ProperSubset => "⊊".to_string(),
3037      SetOp::ProperSuperset => "⊋".to_string(),
3038      SetOp::ElementOf => "∈".to_string(),
3039      SetOp::NotElementOf => "∉".to_string(),
3040      SetOp::SymmetricDifference => "Δ".to_string(),
3041    }
3042  }
3043
3044  pub fn add_sub_op(&mut self, node: &AddSubOp) -> String {
3045    match node {
3046      AddSubOp::Add => "+".to_string(),
3047      AddSubOp::Sub => "-".to_string(),
3048    }
3049  }
3050
3051  pub fn mul_div_op(&mut self, node: &MulDivOp) -> String {
3052    match node {
3053      MulDivOp::Div => "/".to_string(),
3054      MulDivOp::Mod => "%".to_string(),
3055      MulDivOp::Mul => "*".to_string(),
3056    }
3057  }
3058
3059  pub fn power_op(&mut self, node: &PowerOp) -> String {
3060    match node {
3061      PowerOp::Pow => "^".to_string(),
3062    }
3063  }
3064
3065  pub fn vec_op(&mut self, node: &VecOp) -> String {
3066    match node {
3067      VecOp::MatMul => "**".to_string(),
3068      VecOp::Solve => "\\".to_string(),
3069      VecOp::Cross => "×".to_string(),
3070      VecOp::Dot => "·".to_string(),
3071    }
3072  }
3073
3074  pub fn comparison_op(&mut self, node: &ComparisonOp) -> String {
3075    match node {
3076      ComparisonOp::Equal => "⩵".to_string(),
3077      ComparisonOp::StrictEqual => "=:=".to_string(),
3078      ComparisonOp::StrictNotEqual => "=/=".to_string(),
3079      ComparisonOp::NotEqual => "≠".to_string(),
3080      ComparisonOp::GreaterThan => ">".to_string(),
3081      ComparisonOp::GreaterThanEqual => "≥".to_string(),
3082      ComparisonOp::LessThan => "<".to_string(),
3083      ComparisonOp::LessThanEqual => "≤".to_string(),
3084    }
3085  }
3086
3087  pub fn logic_op(&mut self, node: &LogicOp) -> String {
3088    match node {
3089      LogicOp::And => "&&".to_string(),
3090      LogicOp::Or => "||".to_string(),
3091      LogicOp::Xor => "⊻".to_string(),
3092      LogicOp::Not => "¬".to_string(),
3093    }
3094  }
3095
3096  pub fn boolean(&mut self, node: &Token) -> String {
3097    let b = node.to_string();
3098    if self.html {
3099      format!("<span class=\"mech-boolean\">{}</span>", b)
3100    } else {
3101      b
3102    }
3103  }
3104
3105  pub fn empty(&mut self, node: &Token) -> String {
3106    let e = node.to_string();
3107    if self.html {
3108      format!("<span class=\"mech-empty\">{}</span>", e)
3109    } else {
3110      e
3111    }
3112  }
3113
3114  pub fn literal(&mut self, node: &Literal) -> String {
3115    let l = match node {
3116      Literal::Empty(tkn) => self.empty(tkn),
3117      Literal::Boolean(tkn) => self.boolean(tkn),
3118      Literal::Number(num) => self.number(num),
3119      Literal::String(mech_string) => self.string(mech_string),
3120      Literal::Atom(atm) => self.atom(atm),
3121      Literal::Kind(knd) => self.kind_annotation(knd),
3122      Literal::TypedLiteral((boxed_literal, kind_annotation)) => {
3123        let literal = self.literal(boxed_literal);
3124        let annotation = self.kind_annotation(&kind_annotation.kind);
3125        format!("{}{}", literal, annotation)
3126      }
3127    };
3128    if self.html {
3129      format!("<span class=\"mech-literal\">{}</span>",l)
3130    } else {
3131      l
3132    }
3133  }
3134
3135  pub fn atom(&mut self, node: &Atom) -> String {
3136    if self.html {
3137      format!("<span class=\"mech-atom\"><span class=\"mech-atom-name\">:{}</span></span>",node.name.to_string())
3138    } else {
3139      format!(":{}", node.name.to_string())
3140    }
3141  }
3142
3143  pub fn string(&mut self, node: &MechString) -> String {
3144    if self.html {
3145      format!("<span class=\"mech-string\">\"{}\"</span>", node.text.to_string())
3146    } else {
3147      format!("\"{}\"", node.text.to_string())
3148    }
3149  }
3150
3151  pub fn number(&mut self, node: &Number) -> String {
3152    let n = match node {
3153      Number::Real(real) => self.real_number(real),
3154      Number::Complex(complex) => self.complex_numer(complex),
3155    };
3156    if self.html {
3157      format!("<span class=\"mech-number\">{}</span>",n)
3158    } else {
3159      n
3160    }
3161  }
3162
3163  pub fn real_number(&mut self, node: &RealNumber) -> String {
3164    match node {
3165      RealNumber::Negated(real_number) => format!("-{}", self.real_number(real_number)),
3166      RealNumber::Integer(token) => token.to_string(),
3167      RealNumber::Float((whole, part)) => format!("{}.{}", whole.to_string(), part.to_string()),
3168      RealNumber::Decimal(token) => format!("0d{}", token.to_string()),
3169      RealNumber::Hexadecimal(token) => format!("0x{}", token.to_string()),
3170      RealNumber::Octal(token) => format!("0o{}", token.to_string()),
3171      RealNumber::Binary(token) => format!("0b{}", token.to_string()),
3172      RealNumber::Scientific(((whole, part), (sign, ewhole, epart))) => format!("{}.{}e{}{}.{}", whole.to_string(), part.to_string(), if *sign { "-" } else { "+" }, ewhole.to_string(), epart.to_string()),
3173      RealNumber::Rational((numerator, denominator)) => format!("{}/{}", numerator.to_string(), denominator.to_string()),
3174      RealNumber::TypedInteger((token, kind_annotation)) => {
3175        let num = token.to_string();
3176        let annotation = &kind_annotation.kind.tokens().iter().map(|tkn| tkn.to_string()).collect::<Vec<String>>().join("");
3177        format!("{}{}", num, annotation)
3178      }
3179    }
3180  }
3181
3182  pub fn complex_numer(&mut self, node: &C64Node) -> String {
3183    let real = if let Some(real) = &node.real {
3184      let num = self.real_number(&real);
3185      format!("{}+", num)
3186    } else {
3187      "".to_string()
3188    };
3189    let im = self.imaginary_number(&node.imaginary);
3190    format!("{}{}", real, im)
3191  }
3192
3193  pub fn imaginary_number(&mut self, node: &ImaginaryNumber) -> String {
3194    let real = self.real_number(&node.number);
3195    format!("{}i", real)
3196  }
3197
3198  pub fn humanize_html(input: String) -> String {
3199    let mut result = String::new();
3200    let mut indent_level = 0;
3201    let mut in_special_tag = false;
3202    let mut special_tag = "";
3203    let chars: Vec<char> = input.chars().collect();
3204    let mut i = 0;
3205    
3206    let self_closing_tags = HashSet::from([
3207      "area", "base", "br", "col", "embed", "hr", "img", "input", 
3208      "link", "meta", "param", "source", "track", "wbr"
3209    ]);
3210    
3211    fn matches_tag(chars: &[char], pos: usize, tag: &str) -> bool {
3212      let tag_chars: Vec<char> = tag.chars().collect();
3213      pos + tag_chars.len() <= chars.len() && chars[pos..pos+tag_chars.len()] == tag_chars[..]
3214    }
3215    
3216    while i < chars.len() {
3217      // Handle <pre> and <code> tags
3218      if !in_special_tag && (matches_tag(&chars, i, "<pre") || matches_tag(&chars, i, "<code")) {
3219        in_special_tag = true;
3220        special_tag = if matches_tag(&chars, i, "<pre") { "pre" } else { "code" };
3221        
3222        result.push('\n');
3223        result.push_str(&"  ".repeat(indent_level));
3224        
3225        // Add the opening tag
3226        while i < chars.len() && chars[i] != '>' {
3227          result.push(chars[i]);
3228          i += 1;
3229        }
3230        result.push('>');
3231        i += 1;
3232        indent_level += 1;
3233        
3234        // Process content
3235        let start = i;
3236        while i < chars.len() && !matches_tag(&chars, i, &format!("</{}>", special_tag)) {
3237          i += 1;
3238        }
3239        
3240        // Add the content as is
3241        result.extend(chars[start..i].iter());
3242        
3243        // Add the closing tag
3244        if i < chars.len() {
3245          result.push_str(&format!("</{}>", special_tag));
3246          i += special_tag.len() + 3;
3247          in_special_tag = false;
3248          indent_level -= 1;
3249        }
3250      // Open tag
3251      } else if !in_special_tag && i < chars.len() && chars[i] == '<' && i+1 < chars.len() && chars[i+1] != '/' {
3252        let tag_start = i + 1;
3253        let mut j = tag_start;
3254        
3255        // Get tag name
3256        while j < chars.len() && chars[j].is_alphanumeric() {
3257          j += 1;
3258      }
3259        
3260        let tag_name: String = chars[tag_start..j].iter().collect();
3261        let is_self_closing = self_closing_tags.contains(tag_name.as_str());
3262        
3263        // Add newline and indentation
3264        result.push('\n');
3265        result.push_str(&"  ".repeat(indent_level));
3266        
3267        // Add the tag
3268        while i < chars.len() && chars[i] != '>' {
3269          result.push(chars[i]);
3270          i += 1;
3271        }
3272        result.push('>');
3273        i += 1;
3274        
3275        if !is_self_closing {
3276          indent_level += 1;
3277        }
3278      // Close tag
3279      } else if !in_special_tag && i < chars.len() && chars[i] == '<' && i+1 < chars.len() && chars[i+1] == '/' {
3280        indent_level = indent_level.saturating_sub(1);
3281        
3282        result.push('\n');
3283        result.push_str(&"  ".repeat(indent_level));
3284        
3285        while i < chars.len() && chars[i] != '>' {
3286            result.push(chars[i]);
3287            i += 1;
3288        }
3289        result.push('>');
3290        i += 1;
3291      // Regular text content
3292      } else if !in_special_tag {
3293        let start = i;
3294        while i < chars.len() && chars[i] != '<' {
3295          i += 1;
3296        }
3297        
3298        let content: String = chars[start..i].iter().collect();
3299        if !content.trim().is_empty() {
3300          result.push('\n');
3301          result.push_str(&"  ".repeat(indent_level));
3302          result.push_str(&content);
3303      }
3304      // Inside <pre> or <code>
3305      } else {
3306        result.push(chars[i]);
3307        i += 1;
3308      }
3309    }
3310    result.push('\n');
3311    result
3312  }
3313}