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 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::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 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 let header_line = render_row(&node.header, &mut |p| self.paragraph(p));
1007
1008 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 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 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 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", };
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 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", };
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\">></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::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 };
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\"><<span class=\"mech-kind\">{}</span>></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 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 };
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(); 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\"><{}></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!("{}<{}>", ident_s, k);
2345 } else {
2346 src = format!("{},{}<{}>", 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!("{}<{}>", ident_s, k);
2358 } else {
2359 src = format!("{},{}<{}>", 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 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 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 let start = i;
2667 while i < chars.len() && !matches_tag(&chars, i, &format!("</{}>", special_tag)) {
2668 i += 1;
2669 }
2670
2671 result.extend(chars[start..i].iter());
2673
2674 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 } 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 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 result.push('\n');
2696 result.push_str(&" ".repeat(indent_level));
2697
2698 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 } 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 } 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 } else {
2737 result.push(chars[i]);
2738 i += 1;
2739 }
2740 }
2741
2742 result.push('\n');
2743 result
2744}
2745
2746}