use itertools::Itertools;
use crate::{
ast::{Expression, Expressions, Literal, Table, TableRow, AST},
tokens::{
types::{
Arrow, BinaryOperator, Function, Greek, LBrace, Logical, Misc, Operation, RBrace,
Relational, TokenType, UnaryOperator,
},
Token,
},
};
pub fn to_mathml(ast: &AST) -> String {
format!(
"<math display=\"block\">{}</math>",
expressions_to_mathml(&ast.expressions)
)
}
fn expressions_to_mathml(expressions: &Expressions) -> String {
expressions
.expressions
.iter()
.map(|expression| expression_to_mathml(expression))
.join("")
}
fn expression_to_mathml(expr: &Expression) -> String {
match expr {
Expression::Frac(frac) => frac_to_mathml(frac),
Expression::Sub(bi_expression) => sub_to_mathml(bi_expression),
Expression::Pow(bi_expression) => pow_to_mathml(bi_expression),
Expression::SubPow(tri_expression) => sub_pow_to_mathml(tri_expression),
Expression::Group(group) => group_to_mathml(group),
Expression::Unary(unary) => unary_to_mathml(unary),
Expression::Binary(binary) => binary_to_mathml(binary),
Expression::Literal(literal) => literal_to_mathml(literal),
Expression::Expressions(expressions) => expressions_to_mathml(expressions),
Expression::Unit => unreachable!(),
}
}
fn binary_to_mathml(binary: &crate::ast::Binary) -> String {
match binary.operator.token_type {
crate::tokens::types::TokenType::BinaryOperator(BinaryOperator::Root) => format!(
"<mroot><mrow>{}</mrow><mrow>{}</mrow></mroot>",
match binary.expression_2.as_ref() {
Expression::Group(group) => expressions_to_mathml(&group.expressions),
_ => expression_to_mathml(&binary.expression_2),
},
match binary.expression_1.as_ref() {
Expression::Group(group) => expressions_to_mathml(&group.expressions),
_ => expression_to_mathml(&binary.expression_1),
}
),
crate::tokens::types::TokenType::BinaryOperator(BinaryOperator::Overset) => format!(
"<mover><mrow>{}</mrow><mrow>{}</mrow></mover>",
match binary.expression_2.as_ref() {
Expression::Group(group) => expressions_to_mathml(&group.expressions),
_ => expression_to_mathml(&binary.expression_2),
},
match binary.expression_1.as_ref() {
Expression::Group(group) => expressions_to_mathml(&group.expressions),
_ => expression_to_mathml(&binary.expression_1),
}
),
crate::tokens::types::TokenType::BinaryOperator(BinaryOperator::Underset) => format!(
"<munder><mrow>{}</mrow><mrow>{}</mrow></munder>",
match binary.expression_2.as_ref() {
Expression::Group(group) => expressions_to_mathml(&group.expressions),
_ => expression_to_mathml(&binary.expression_2),
},
match binary.expression_1.as_ref() {
Expression::Group(group) => expressions_to_mathml(&group.expressions),
_ => expression_to_mathml(&binary.expression_1),
}
),
crate::tokens::types::TokenType::BinaryOperator(BinaryOperator::Color) => format!(
"<mstyle mathcolor=\"{}\">{}</mstyle>",
match binary.expression_1.as_ref() {
Expression::Group(group) => group
.expressions
.expressions
.iter()
.map(|expression| match expression {
Expression::Literal(Literal::Literal(literal)) => literal.span.text,
_ => "",
})
.join(""),
_ => "".to_string(),
},
match binary.expression_2.as_ref() {
Expression::Group(group) => expressions_to_mathml(&group.expressions),
_ => expression_to_mathml(&binary.expression_2),
},
),
_ => format!(
"{}{}{}",
binary.operator.span.text,
expression_to_mathml(&binary.expression_1),
expression_to_mathml(&binary.expression_2),
),
}
}
fn group_to_mathml(group: &crate::ast::Group) -> String {
format!(
"<mrow>{}{}{}</mrow>",
l_brace_to_math_ml(&group.l_brace),
group
.expressions
.expressions
.iter()
.map(|expr| expression_to_mathml(expr))
.join(""),
r_brace_to_math_ml(&group.r_brace),
)
}
fn sub_pow_to_mathml(tri_expression: &crate::ast::TriExpression) -> String {
if let Expression::Literal(Literal::Literal(literal)) = tri_expression.expression_1.as_ref() {
match &literal.token_type {
TokenType::Operation(op) => match op {
Operation::Sum
| Operation::Prod
| Operation::BigWedge
| Operation::BigCap
| Operation::BigCup => {
return format!(
"<munderover>{}{}{}</munderover>",
expression_to_mathml_braceless(&tri_expression.expression_1),
expression_to_mathml_braceless(&tri_expression.expression_2),
expression_to_mathml_braceless(&tri_expression.expression_3)
)
}
_ => {}
},
TokenType::Misc(Misc::Lim) => {
return format!(
"<munderover>{}{}{}</munderover>",
expression_to_mathml_braceless(&tri_expression.expression_1),
expression_to_mathml_braceless(&tri_expression.expression_2),
expression_to_mathml_braceless(&tri_expression.expression_3)
)
}
_ => {}
}
} else if let Expression::Unary(unary) = tri_expression.expression_1.as_ref() {
match unary.operator.token_type {
TokenType::UnaryOperator(UnaryOperator::UBrace) => {
return format!(
"<munderover><munder>{}</munder>{}{}</munderover>",
expression_to_mathml_braceless(&tri_expression.expression_1),
expression_to_mathml_braceless(&tri_expression.expression_2),
expression_to_mathml_braceless(&tri_expression.expression_3)
)
}
TokenType::UnaryOperator(UnaryOperator::OBrace) => {
return format!(
"<munderover><mover>{}</mover>{}{}</munderover>",
expression_to_mathml_braceless(&tri_expression.expression_1),
expression_to_mathml_braceless(&tri_expression.expression_2),
expression_to_mathml_braceless(&tri_expression.expression_3)
)
}
_ => {}
}
}
format!(
"<msubsup>{}{}{}</msubsup>",
expression_to_mathml(&tri_expression.expression_1),
expression_to_mathml(&tri_expression.expression_2),
expression_to_mathml(&tri_expression.expression_3)
)
}
fn pow_to_mathml(bi_expression: &crate::ast::BiExpression) -> String {
if let Expression::Literal(Literal::Literal(token)) = bi_expression.expression_1.as_ref() {
match &token.token_type {
TokenType::Operation(op) => match op {
Operation::Sum
| Operation::Prod
| Operation::BigWedge
| Operation::BigCap
| Operation::BigCup => {
return format!(
"<mover>{}{}</mover>",
expression_to_mathml_braceless(&bi_expression.expression_1),
expression_to_mathml_braceless(&bi_expression.expression_2)
)
}
_ => {}
},
TokenType::Misc(Misc::Lim) => {
return format!(
"<mover>{}{}</mover>",
expression_to_mathml_braceless(&bi_expression.expression_1),
expression_to_mathml_braceless(&bi_expression.expression_2)
)
}
_ => {}
}
} else if let Expression::Unary(unary) = bi_expression.expression_1.as_ref() {
if let TokenType::UnaryOperator(UnaryOperator::OBrace) = unary.operator.token_type {
return format!(
"<mover><mover>{}</mover>{}</mover>",
expression_to_mathml_braceless(&bi_expression.expression_1),
expression_to_mathml_braceless(&bi_expression.expression_2),
);
}
}
format!(
"<msup>{}{}</msup>",
expression_to_mathml(&bi_expression.expression_1),
expression_to_mathml(&bi_expression.expression_2)
)
}
fn sub_to_mathml(bi_expression: &crate::ast::BiExpression) -> String {
if let Expression::Literal(Literal::Literal(literal)) = bi_expression.expression_1.as_ref() {
match &literal.token_type {
TokenType::Operation(op) => match op {
Operation::Sum
| Operation::Prod
| Operation::BigWedge
| Operation::BigCap
| Operation::BigCup => {
return format!(
"<munder>{}{}</munder>",
expression_to_mathml_braceless(&bi_expression.expression_1),
expression_to_mathml_braceless(&bi_expression.expression_2)
)
}
_ => {}
},
TokenType::Misc(Misc::Lim) => {
return format!(
"<munder>{}{}</munder>",
expression_to_mathml_braceless(&bi_expression.expression_1),
expression_to_mathml_braceless(&bi_expression.expression_2)
)
}
_ => {}
}
} else if let Expression::Unary(unary) = bi_expression.expression_1.as_ref() {
if let TokenType::UnaryOperator(UnaryOperator::UBrace) = unary.operator.token_type {
return format!(
"<munder><munder>{}</munder>{}</munder>",
expression_to_mathml_braceless(&bi_expression.expression_1),
expression_to_mathml_braceless(&bi_expression.expression_2),
);
}
}
format!(
"<msub>{}{}</msub>",
expression_to_mathml(&bi_expression.expression_1),
expression_to_mathml(&bi_expression.expression_2)
)
}
fn expression_to_mathml_braceless(expression: &Expression) -> String {
match expression {
Expression::Group(group) => {
format!("<mrow>{}</mrow>", expressions_to_mathml(&group.expressions))
}
_ => expression_to_mathml(expression),
}
}
fn frac_to_mathml(frac: &crate::ast::BiExpression) -> String {
format!(
"<mfrac>{}{}</mfrac>",
expression_to_mathml_braceless(&frac.expression_1),
expression_to_mathml_braceless(&frac.expression_2)
)
}
fn unary_to_mathml(unary: &crate::ast::Unary) -> String {
match unary.operator.token_type {
TokenType::UnaryOperator(UnaryOperator::Hat)
| TokenType::UnaryOperator(UnaryOperator::Bar)
| TokenType::UnaryOperator(UnaryOperator::Vec)
| TokenType::UnaryOperator(UnaryOperator::Tilde)
| TokenType::UnaryOperator(UnaryOperator::Dot)
| TokenType::UnaryOperator(UnaryOperator::DDot)
| TokenType::UnaryOperator(UnaryOperator::OBrace) => {
let symbol = match unary.operator.token_type {
TokenType::UnaryOperator(UnaryOperator::Hat) => "^",
TokenType::UnaryOperator(UnaryOperator::Bar) => "¯",
TokenType::UnaryOperator(UnaryOperator::Vec) => "→",
TokenType::UnaryOperator(UnaryOperator::Tilde) => "~",
TokenType::UnaryOperator(UnaryOperator::Dot) => ".",
TokenType::UnaryOperator(UnaryOperator::DDot) => "..",
TokenType::UnaryOperator(UnaryOperator::OBrace) => "⏞",
_ => "",
};
format!(
"<mover>{}<mo>{}</mo></mover>",
expression_to_mathml_braceless(&unary.expression),
symbol
)
}
TokenType::UnaryOperator(UnaryOperator::Ul)
| TokenType::UnaryOperator(UnaryOperator::UBrace) => {
let symbol = match unary.operator.token_type {
TokenType::UnaryOperator(UnaryOperator::Ul) => "̲",
TokenType::UnaryOperator(UnaryOperator::UBrace) => "⏟",
_ => "",
};
format!(
"<munder>{}<mo>{}</mo></munder>",
expression_to_mathml_braceless(&unary.expression),
symbol
)
}
TokenType::UnaryOperator(UnaryOperator::Abs)
| TokenType::UnaryOperator(UnaryOperator::Floor)
| TokenType::UnaryOperator(UnaryOperator::Ceil)
| TokenType::UnaryOperator(UnaryOperator::Norm) => {
let (left_symbol, right_symbol) = match unary.operator.token_type {
TokenType::UnaryOperator(UnaryOperator::Abs) => ("|", "|"),
TokenType::UnaryOperator(UnaryOperator::Floor) => ("⌊", "⌋"),
TokenType::UnaryOperator(UnaryOperator::Ceil) => ("⌈", "⌉"),
TokenType::UnaryOperator(UnaryOperator::Norm) => ("∥", "∥"),
_ => ("", ""),
};
format!(
"<mo>{}</mo>{}<mo>{}</mo>",
left_symbol,
expression_to_mathml_braceless(&unary.expression),
right_symbol
)
}
TokenType::UnaryOperator(UnaryOperator::Cancel) => format!(
"<menclose notation=\"updiagonalstrike\">{}</menclose>",
expression_to_mathml_braceless(&unary.expression)
),
TokenType::UnaryOperator(UnaryOperator::Sqrt) => {
format!(
"<msqrt>{}</msqrt>",
expression_to_mathml_braceless(&unary.expression)
)
}
TokenType::UnaryOperator(UnaryOperator::Text) => {
format!(
"<mtext>{}</mtext>",
expression_to_mathml_braceless(&unary.expression)
)
}
_ => "".to_string(),
}
}
fn literal_to_mathml(literal: &crate::ast::Literal) -> String {
match literal {
Literal::Literal(token) => token_to_mathml(token),
Literal::Table(table) => table_to_mathml(table),
}
}
fn table_to_mathml(table: &crate::ast::Table) -> String {
format!(
"<mrow>{}<mtable {}>{}</mtable>{}</mrow>",
l_brace_to_math_ml(&table.l_brace),
format_column_line(table),
expressions_to_mathml_table(&table.rows),
r_brace_to_math_ml(&table.r_brace)
)
}
fn format_column_line(table: &Table) -> String {
table
.rows
.first()
.map(|row| {
format!(
"columnlines=\"{}\"",
row.cols
.iter()
.enumerate()
.skip(1)
.map(|(index, _)| {
if table.seperators.contains(&index) {
"solid".to_string()
} else {
"none".to_string()
}
})
.join(" ")
)
})
.unwrap_or("".to_string())
}
fn expressions_to_mathml_table(rows: &[TableRow]) -> String {
rows.iter()
.map(|row| {
format!(
"<mtr>{}</mtr>",
row.cols
.iter()
.map(|col| format!("<mtd>{}</mtd>", expressions_to_mathml(col)))
.join("")
)
})
.join("")
}
fn l_brace_to_math_ml(token: &Token) -> String {
match &token.token_type {
TokenType::LBrace(lbrace) => match lbrace {
LBrace::LParen => "<mo>(</mo>".to_string(),
LBrace::LBracket => "<mo>[</mo>".to_string(),
LBrace::LBrace => "<mo>{</mo>".to_string(),
LBrace::LColonBrace => "".to_string(),
LBrace::LAngle => "<mo><</mo>".to_string(),
},
_ => token.span.text.to_string(),
}
}
fn r_brace_to_math_ml(token: &Token) -> String {
match &token.token_type {
TokenType::RBrace(rbrace) => match rbrace {
RBrace::RParen => "<mo>)</mo>".to_string(),
RBrace::RBracket => "<mo>]</mo>".to_string(),
RBrace::RBrace => "<mo>}</mo>".to_string(),
RBrace::RColonBrace => "".to_string(),
RBrace::RAngle => "<mo>></mo>".to_string(),
},
_ => token.span.text.to_string(),
}
}
fn token_to_mathml(token: &Token) -> String {
match &token.token_type {
TokenType::Symbol => format!("<mi>{}</mi>", token.span.text),
TokenType::Greek(greek) => greek_to_mathml(greek),
TokenType::Operation(op) => format!("<mo>{}</mo>", operation_to_mathml(op)),
TokenType::Misc(misc) => misc_to_mathml(misc),
TokenType::Relational(relational) => relational_to_mathml(relational),
TokenType::Arrow(arrow) => format!("<mo>{}</mo>", arrow_to_mathml(arrow)),
TokenType::Logical(logical) => logical_to_mathml(logical),
TokenType::Number => format!("<mn>{}</mn>", token.span.text),
TokenType::Text => format!("<mtext>{}</mtext>", token.span.text),
TokenType::Function(function) => format!("<mi>{}</mi>", function_to_mathml(function)),
TokenType::None => "".to_string(),
_ => format!("<mi>{}</mi>", token.span.text),
}
}
fn greek_to_mathml(greek: &Greek) -> String {
let symbol = match greek {
Greek::Alpha => "α",
Greek::Beta => "β",
Greek::Gamma => "β",
Greek::UGamma => "Γ",
Greek::Delta => "δ",
Greek::UDelta => "Δ",
Greek::Epsilon => "ε",
Greek::VarEpsilon => "ɛ",
Greek::Zeta => "ζ",
Greek::Eta => "η",
Greek::Theta => "θ",
Greek::UTheta => "Θ",
Greek::VarTheta => "ϑ",
Greek::Iota => "ι",
Greek::Kappa => "κ",
Greek::Lambda => "λ",
Greek::ULambda => "Λ",
Greek::Mu => "μ",
Greek::Nu => "ν",
Greek::Xi => "ξ",
Greek::UXi => "Ξ",
Greek::Pi => "π",
Greek::UPi => "Π",
Greek::Rho => "ρ",
Greek::Sigma => "σ",
Greek::USigma => "Σ",
Greek::Tau => "τ",
Greek::Upsilon => "υ",
Greek::Phi => "ϕ",
Greek::UPhi => "Φ",
Greek::VarPhi => "φ",
Greek::Chi => "χ",
Greek::Psi => "ψ",
Greek::UPsi => "Ψ",
Greek::Omega => "ω",
Greek::UOmega => "Ω",
};
format!("<mi>{symbol}</mi>")
}
fn logical_to_mathml(logical: &Logical) -> String {
match logical {
Logical::Not
| Logical::Implies
| Logical::Iff
| Logical::ForAll
| Logical::Exists
| Logical::Bot
| Logical::Top
| Logical::VDash
| Logical::Models => {
let symbol = match logical {
Logical::Not => "¬",
Logical::Implies => "⇒",
Logical::Iff => "⇔",
Logical::ForAll => "∀",
Logical::Exists => "∃",
Logical::Bot => "⊥",
Logical::Top => "⊤",
Logical::VDash => "⊢",
Logical::Models => "⊨",
_ => "",
};
format!("<mo>{symbol}</mo>")
}
Logical::And | Logical::Or | Logical::If => {
let symbol = match logical {
Logical::And => "and",
Logical::Or => "or",
Logical::If => "if",
_ => "",
};
format!("<mrow><mspace width=\"1ex\" /><mtext>{symbol}</mtext><msapce with=\"1ex\" /></mrow>")
}
}
}
fn function_to_mathml(function: &Function) -> &'static str {
match function {
Function::Sin => "sin",
Function::Cos => "cos",
Function::Tan => "tan",
Function::Sec => "sec",
Function::Csc => "csc",
Function::Cot => "cot",
Function::Arcsin => "arcsin",
Function::Arccos => "arccos",
Function::Arctan => "arctan",
Function::Sinh => "sinh",
Function::Cosh => "cosh",
Function::Tanh => "tanh",
Function::Sech => "sech",
Function::Csch => "csch",
Function::Coth => "coth",
Function::Exp => "exp",
Function::Log => "log",
Function::Ln => "ln",
Function::Det => "det",
Function::Dim => "dim",
Function::Mod => "mod",
Function::Gcd => "gcd",
Function::Lcm => "lcm",
Function::Lub => "lub",
Function::Glb => "glb",
Function::Min => "min",
Function::Max => "max",
Function::F => "f",
Function::G => "g",
}
}
fn arrow_to_mathml(arrow: &Arrow) -> &'static str {
match arrow {
Arrow::UpArrow => "↑",
Arrow::DownArrow => "↓",
Arrow::RightArrow => "→",
Arrow::ToArrow => "→",
Arrow::RightArrowTail => "↣",
Arrow::RightArrowTwoHead => "↠",
Arrow::RightArrowTwoHeadTail => "⤖",
Arrow::MapsTo => "↦",
Arrow::LeftArrow => "←",
Arrow::LeftRightArrow => "↔",
Arrow::DoubleRightArrow => "⇒",
Arrow::DoubleLeftArrow => "⇐",
Arrow::DoubleLeftRightArrow => "⇔",
}
}
fn relational_to_mathml(relational: &Relational) -> String {
match relational {
Relational::Equals
| Relational::NotEquals
| Relational::Lt
| Relational::Gt
| Relational::Lte
| Relational::Gte
| Relational::Prec
| Relational::PrecEq
| Relational::Succ
| Relational::SuccEq
| Relational::In
| Relational::NotIn
| Relational::Sub
| Relational::Sup
| Relational::SubEq
| Relational::SupEq
| Relational::Equiv
| Relational::Cong
| Relational::Approx
| Relational::Prop => {
let symbol = match relational {
Relational::Equals => "=",
Relational::NotEquals => "≠",
Relational::Lt => "<",
Relational::Gt => ">",
Relational::Lte => "≤",
Relational::Gte => "≥",
Relational::Prec => "≺",
Relational::PrecEq => "⪯",
Relational::Succ => "≻",
Relational::SuccEq => "⪰",
Relational::In => "∈",
Relational::NotIn => "∉",
Relational::Sub => "⊂",
Relational::SubEq => "⊆",
Relational::Sup => "⊃",
Relational::SupEq => "⊇",
Relational::Equiv => "≡",
Relational::Cong => "≅",
Relational::Approx => "≈",
Relational::Prop => "∝",
_ => "",
};
format!("<mo>{}</mo>", symbol)
}
Relational::Mlt => "<mi>m</mi><mo><</mo>".to_string(),
Relational::Mgt => "<mi>m</mi><mo>></mo>".to_string(),
}
}
fn misc_to_mathml(misc: &Misc) -> String {
match misc {
Misc::DoublePipes => {
return "<mrow><mo>∣</mo></mrow><mrow><mo>∣</mo></mrow>".to_string()
}
Misc::DoublePipesQuad => {
return "<mrow><mo>|</mo><mo>  </mo><mo>|</mo></mrow>".to_string()
}
_ => {}
}
let symbol = match misc {
Misc::Int => "∫",
Misc::OInt => "∮",
Misc::Del => "∂",
Misc::Grad => "∇",
Misc::PlusMinus => "±",
Misc::EmptySet => "∅",
Misc::Infinity => "∞",
Misc::Aleph => "ℵ",
Misc::Therefore => "∴",
Misc::Because => "∵",
Misc::LDots => "...",
Misc::CDots => "⋯",
Misc::VDots => "⋮",
Misc::DDots => "⋱",
Misc::Angle => "∠",
Misc::Frown => "⌢",
Misc::Triangle => "△",
Misc::Diamond => "⋄",
Misc::Square => "□",
Misc::LFloor => "⌊",
Misc::RFloor => "⌋",
Misc::LCeiling => "⌈",
Misc::RCeiling => "⌉",
Misc::CC => "ℂ",
Misc::NN => "ℕ",
Misc::QQ => "ℚ",
Misc::RR => "ℝ",
Misc::ZZ => "ℤ",
Misc::DoublePipes => "",
Misc::DoublePipesQuad => "",
Misc::Lim => "lim",
};
format!("<mo>{symbol}</mo>")
}
fn operation_to_mathml(op: &Operation) -> &'static str {
match op {
Operation::Plus => "+",
Operation::Minus => "-",
Operation::CDot => "⋅",
Operation::Ast => "∗",
Operation::Star => "⋆",
Operation::Slash => "/",
Operation::Backslash => "\\",
Operation::Times => "×",
Operation::Div => "÷",
Operation::LTimes => "⋉",
Operation::RTimes => "⋊",
Operation::Bowtie => "⋈",
Operation::Circ => "∘",
Operation::OPlus => "⊕",
Operation::OTimes => "⊗",
Operation::ODot => "⊙",
Operation::Sum => "∑",
Operation::Prod => "∏",
Operation::Wedge => "∧",
Operation::BigWedge => "⋀",
Operation::Vee => "∨",
Operation::BigVee => "⋁",
Operation::Cap => "∩",
Operation::BigCap => "⋂",
Operation::Cup => "∪",
Operation::BigCup => "⋃",
}
}