diff --git a/src/elements/special.rs b/src/elements/special.rs index d83616b..8352fb1 100644 --- a/src/elements/special.rs +++ b/src/elements/special.rs @@ -39,11 +39,13 @@ pub struct Frac { #[derive(Debug, Clone, PartialOrd, PartialEq)] pub struct Pow { + pub(crate) base: Box, pub(crate) exp: Box, } #[derive(Debug, Clone, PartialOrd, PartialEq)] pub struct Sub { + pub(crate) base: Box, pub(crate) lower: Box, } diff --git a/src/format/mathml.rs b/src/format/mathml.rs index 1e98cfe..ebcd1b0 100644 --- a/src/format/mathml.rs +++ b/src/format/mathml.rs @@ -3,7 +3,9 @@ use crate::elements::group::{ Abs, Angles, Braces, Brackets, Ceil, Floor, Group, Matrix, Norm, Parentheses, Vector, XGroup, }; use crate::elements::literal::{Literal, Number, PlainText, Symbol}; -use crate::elements::special::Expression; +use crate::elements::special::{ + Expression, Frac, Integral, OIntegral, Pow, Prod, Root, Special, Sqrt, Sub, Sum, +}; use crate::elements::Element; use crate::tokens::{ Accent, Arrow, FontCommand, Function, Greek, Logical, Misc, Operation, Relation, @@ -266,7 +268,7 @@ impl ToMathML for Operation { let inner = match self { Operation::Plus => "+", Operation::Minus => "−", - Operation::CDot => "∙", + Operation::CDot => "⋅", Operation::Ast => "∗", Operation::Star => "⋆", Operation::Slash => "/", @@ -498,6 +500,159 @@ impl ToMathML for Vector { } } +impl ToMathML for Special { + fn to_mathml(&self) -> String { + match self { + Special::Sum(s) => s.to_mathml(), + Special::Prod(p) => p.to_mathml(), + Special::Frac(f) => f.to_mathml(), + Special::Pow(p) => p.to_mathml(), + Special::Sub(s) => s.to_mathml(), + Special::Sqrt(s) => s.to_mathml(), + Special::Root(r) => r.to_mathml(), + Special::Integral(i) => i.to_mathml(), + Special::OIntegral(i) => i.to_mathml(), + } + } +} + +impl ToMathML for Sum { + fn to_mathml(&self) -> String { + if let Some(bottom) = &self.bottom { + if let Some(top) = &self.top { + format!( + "{}{}", + bottom.to_mathml(), + top.to_mathml() + ) + } else { + format!("{}", bottom.to_mathml()) + } + } else if let Some(top) = &self.top { + format!("{}", top.to_mathml()) + } else { + format!("") + } + } +} + +impl ToMathML for Prod { + fn to_mathml(&self) -> String { + if let Some(bottom) = &self.bottom { + if let Some(top) = &self.top { + format!( + "{}{}", + bottom.to_mathml(), + top.to_mathml() + ) + } else { + format!("{}", bottom.to_mathml()) + } + } else if let Some(top) = &self.top { + format!("{}", top.to_mathml()) + } else { + format!("") + } + } +} + +impl ToMathML for Frac { + fn to_mathml(&self) -> String { + format!( + "{}{}", + self.top.to_mathml(), + self.bottom.to_mathml() + ) + } +} + +impl ToMathML for Sqrt { + fn to_mathml(&self) -> String { + format!("{}", self.inner.to_mathml()) + } +} + +impl ToMathML for Root { + fn to_mathml(&self) -> String { + format!( + "{}{}", + self.base.to_mathml(), + self.inner.to_mathml() + ) + } +} + +impl ToMathML for Pow { + fn to_mathml(&self) -> String { + format!( + "{}{}", + self.base.to_mathml(), + self.exp.to_mathml() + ) + } +} + +impl ToMathML for Sub { + fn to_mathml(&self) -> String { + format!( + "{}{}", + self.base.to_mathml(), + self.lower.to_mathml() + ) + } +} + +impl ToMathML for Integral { + fn to_mathml(&self) -> String { + if let Some(bottom) = &self.bottom { + if let Some(top) = &self.top { + format!( + "{}{}", + bottom.to_mathml(), + top.to_mathml() + ) + } else { + format!("{}", bottom.to_mathml()) + } + } else if let Some(top) = &self.top { + format!("{}", top.to_mathml()) + } else { + format!("") + } + } +} + +impl ToMathML for OIntegral { + fn to_mathml(&self) -> String { + if let Some(bottom) = &self.bottom { + if let Some(top) = &self.top { + format!( + "{}{}", + bottom.to_mathml(), + top.to_mathml() + ) + } else { + format!("{}", bottom.to_mathml()) + } + } else if let Some(top) = &self.top { + format!("{}", top.to_mathml()) + } else { + format!("") + } + } +} + +impl ToMathML for ExpressionAccent { + fn to_mathml(&self) -> String { + match self { + ExpressionAccent::Generic(g) => g.to_mathml(), + ExpressionAccent::OverSet(o) => o.to_mathml(), + ExpressionAccent::UnderSet(u) => u.to_mathml(), + ExpressionAccent::Color(c) => c.to_mathml(), + } + } +} + impl ToMathML for Expression { fn to_mathml(&self) -> String { format!( @@ -511,6 +666,11 @@ impl ToMathML for Expression { impl ToMathML for Element { fn to_mathml(&self) -> String { - unimplemented!() + match self { + Element::Special(s) => s.to_mathml(), + Element::Literal(l) => l.to_mathml(), + Element::Group(g) => g.to_mathml(), + Element::Accent(a) => a.to_mathml(), + } } } diff --git a/src/lib.rs b/src/lib.rs index 138b74a..bd139de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,7 @@ mod tests { use crate::elements::literal::{Literal, Number}; use crate::elements::special::{Expression, Special, Sum}; use crate::elements::Element; + use crate::format::mathml::ToMathML; use crate::parse; use crate::parsing::tokenizer::Tokenizer; use crate::parsing::tree_parser::TreeParser; @@ -347,15 +348,19 @@ mod tests { ) } + #[allow(dead_code)] //#[test] - fn it_parses_into_a_tree3() { + fn it_writes_mathml() { + let str_expression = + "sqrt 1 in NN implies 2^4 + sum_(k = 1)^3 - ((1),(2)) [[2, 3 + 3],[4, 5]]"; + let expression = parse(str_expression.to_string()); fs::write( - "test-files/test.txt", + "test-files/test.html", format!( - "{:#?}", - parse( - "color(red)(a) * b^4 - c(c-2) [[1, 3, 2 + 2],[3 - x, 4] ((2),(3))".to_string() - ) + "
{}
{}
{:#?}
", + str_expression, + expression.to_mathml(), + expression, ), ) .unwrap(); diff --git a/src/parsing/tree_parser.rs b/src/parsing/tree_parser.rs index 6ddf84b..f847749 100644 --- a/src/parsing/tree_parser.rs +++ b/src/parsing/tree_parser.rs @@ -73,7 +73,16 @@ impl TreeParser { while !self.end_reached() { if let Some(element) = self.parse_element() { - expression.add_child(element); + // parse elements that are based on the previous one + if let Some(pow) = self.parse_pow_element(&element) { + expression.add_child(Element::Special(Special::Pow(pow))) + } else if let Some(frac) = self.parse_frac_element(&element) { + expression.add_child(Element::Special(Special::Frac(frac))) + } else if let Some(sub) = self.parse_sub_element(&element) { + expression.add_child(Element::Special(Special::Sub(sub))) + } else { + expression.add_child(element); + } } if self.group_return { break; @@ -214,18 +223,6 @@ impl TreeParser { fn parse_misc(&mut self, token: Misc) -> Element { match token { - Misc::Pow => { - self.step(); - Element::Special(Special::Pow(Pow { - exp: self.parse_element().unwrap().boxed(), - })) - } - Misc::Sub => { - self.step(); - Element::Special(Special::Sub(Sub { - lower: self.parse_element().unwrap().boxed(), - })) - } Misc::LatexFrac => { self.step(); Element::Special(Special::Frac(Frac { @@ -427,6 +424,48 @@ impl TreeParser { } } + // tries to parse a pow element + fn parse_pow_element(&mut self, previous: &Element) -> Option { + if let Some(Token::Misc(Misc::Pow)) = self.peek() { + self.step(); + self.step(); + Some(Pow { + base: previous.clone().boxed(), + exp: self.parse_element().unwrap().boxed(), + }) + } else { + None + } + } + + // tries to parse a sub element + fn parse_sub_element(&mut self, previous: &Element) -> Option { + if let Some(Token::Misc(Misc::Sub)) = self.peek() { + self.step(); + self.step(); + Some(Sub { + base: previous.clone().boxed(), + lower: self.parse_element().unwrap().boxed(), + }) + } else { + None + } + } + + // tries to parse an ascii frac + fn parse_frac_element(&mut self, previous: &Element) -> Option { + if let Some(Token::Misc(Misc::AsciiFrac)) = self.peek() { + self.step(); + self.step(); + Some(Frac { + top: previous.clone().boxed(), + bottom: self.parse_element().unwrap().boxed(), + }) + } else { + None + } + } + /// Remaps an expresion vector into a matrix of expressions by splitting on each MSep token fn transform_vec_to_matrix(&self, expressions: Vec) -> Vec> { expressions