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