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