subscript_compiler/frontend/pass/
math.rs

1//! Compile math mode to the given target.
2//!
3//! Eventually the given options will be
4//! * LaTeX math (for some external compiler such as MathJax when using the HTML target). 
5//! * Native typesetter. 
6use lazy_static::lazy_static;
7use std::iter::FromIterator;
8use std::collections::HashSet;
9use std::rc::Rc;
10use std::borrow::Cow;
11use crate::frontend::data::{
12    Atom,
13    Text,
14    Enclosure,
15    EnclosureKind,
16    INLINE_MATH_TAG,
17};
18use crate::frontend::ast::{Ann, Node, NodeEnvironment, Tag};
19
20pub static LATEX_ENVIRONMENT_NAME_LIST: &'static [&'static str] = &[
21    "equation",
22    "split",
23];
24
25lazy_static! {
26    pub static ref LATEX_ENV_NAMES: HashSet<&'static str> = {
27        HashSet::from_iter(
28            LATEX_ENVIRONMENT_NAME_LIST.to_vec()
29        )
30    };
31}
32
33
34/// Converts math nodes to a valid latex code within the AST data model.
35fn to_valid_latex_math<'a>(node: Node<'a>) -> Node<'a> {
36    // HELPERS
37    fn init_env<'a>(env_name: &'a str, children: Vec<Node<'a>>) -> Node<'a> {
38        Node::new_fragment(vec![
39            Node::unannotated_tag_(
40                "begin",
41                Node::Enclosure(Ann::unannotated(
42                    Enclosure::new_curly_brace_(Node::unannotated_str(env_name))
43                ))
44            ),
45            Node::new_fragment(children),
46            Node::unannotated_tag_(
47                "end",
48                Node::Enclosure(Ann::unannotated(
49                    Enclosure::new_curly_brace_(Node::unannotated_str(env_name))
50                ))
51            ),
52        ])
53    }
54    // FUNCTION
55    fn f<'a>(env: NodeEnvironment, x: Node<'a>) -> Node<'a> {
56        match x {
57            Node::Tag(tag) if LATEX_ENV_NAMES.contains(tag.name()) => {
58                let env_name = *LATEX_ENV_NAMES.get(tag.name()).unwrap();
59                init_env(
60                    env_name,
61                    tag.children
62                )
63            }
64            Node::Tag(mut tag) => {
65                tag.children = tag.children
66                    .into_iter()
67                    // .flat_map(Node::unblock)
68                    .collect();
69                Node::Tag(tag)
70            }
71            x => x,
72        }
73    }
74    // GO! (BOTTOM UP)
75    node.transform(NodeEnvironment::default(), Rc::new(f))
76}
77
78
79/// Entrypoint.
80pub fn latex_pass<'a>(node: Node<'a>) -> Node<'a> {
81    match node {
82        Node::Tag(tag) if tag.has_name("equation") => {
83            let node = tag.children
84                .into_iter()
85                .flat_map(Node::unblock)
86                .map(to_valid_latex_math)
87                .map(|x| x.to_string())
88                .collect::<Vec<_>>()
89                .join("");
90            let start = "\\begin{equation}\\begin{split}";
91            let end = "\\end{split}\\end{equation}";
92            Node::String(Ann::unannotated(Cow::Owned(format!(
93                "\\[{}{}{}\\]",
94                start,
95                node,
96                end,
97            ))))
98        }
99        Node::Tag(tag) if tag.has_name(INLINE_MATH_TAG) => {
100            let node = tag.children
101                .into_iter()
102                .flat_map(Node::unblock)
103                .map(to_valid_latex_math)
104                .map(|x| x.to_string())
105                .collect::<Vec<_>>()
106                .join("");
107            Node::String(Ann::unannotated(Cow::Owned(format!(
108                "\\({}\\)",
109                node,
110            ))))
111        }
112        Node::Tag(mut tag) => {
113            tag.children = tag.children
114                .into_iter()
115                .map(latex_pass)
116                .collect();
117            Node::Tag(tag)
118        }
119        Node::Enclosure(mut block) => {
120            block.data.children = block.data.children
121                .into_iter()
122                .map(latex_pass)
123                .collect();
124            Node::Enclosure(block)
125        }
126        node @ Node::Ident(_) => node,
127        node @ Node::String(_) => node,
128        node @ Node::InvalidToken(_) => node,
129    }
130}
131