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