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