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