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