use crate::lex::ast::elements::inlines::InlineNode;
use crate::lex::ast::elements::{Annotation, Label, Parameter};
pub fn math_to_mathml_processor(node: InlineNode) -> InlineNode {
match node {
InlineNode::Math {
text,
mut annotations,
} => {
match parse_asciimath_to_mathml(&text) {
Ok(mathml) => {
let params = vec![Parameter::new("type".to_string(), "mathml".to_string())];
let mut anno =
Annotation::with_parameters(Label::new("doc.data".to_string()), params);
anno.children
.push(crate::lex::ast::elements::ContentItem::TextLine(
crate::lex::ast::elements::TextLine::new(
crate::lex::ast::TextContent::from(mathml),
),
));
annotations.push(anno);
InlineNode::Math { text, annotations }
}
Err(err) => {
let params = vec![
Parameter::new("type".to_string(), "error".to_string()),
Parameter::new("message".to_string(), err),
];
let anno =
Annotation::with_parameters(Label::new("doc.data".to_string()), params);
annotations.push(anno);
InlineNode::Math { text, annotations }
}
}
}
other => other,
}
}
fn parse_asciimath_to_mathml(asciimath: &str) -> Result<String, String> {
let mathml = polymath_rs::to_math_ml(asciimath);
Ok(mathml)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lex::inlines::InlineParser;
use crate::lex::token::InlineKind;
#[test]
fn parses_simple_expression() {
let result = parse_asciimath_to_mathml("x + y");
assert!(result.is_ok());
let mathml = result.unwrap();
assert!(mathml.contains("<math"));
assert!(mathml.contains("</math>"));
}
#[test]
fn parses_superscript() {
let result = parse_asciimath_to_mathml("x^2");
assert!(result.is_ok());
let mathml = result.unwrap();
assert!(mathml.contains("<msup"));
}
#[test]
fn parses_fraction() {
let result = parse_asciimath_to_mathml("a/b");
assert!(result.is_ok());
let mathml = result.unwrap();
assert!(mathml.contains("<mfrac"));
}
#[test]
fn post_processor_adds_annotation() {
let node = InlineNode::math("x + y".to_string());
let processed = math_to_mathml_processor(node);
match processed {
InlineNode::Math { text, annotations } => {
assert_eq!(text, "x + y");
assert_eq!(annotations.len(), 1);
assert_eq!(annotations[0].data.label.value, "doc.data");
assert_eq!(annotations[0].data.parameters.len(), 1);
assert_eq!(annotations[0].data.parameters[0].key, "type");
assert_eq!(annotations[0].data.parameters[0].value, "mathml");
assert!(!annotations[0].children.is_empty());
}
_ => panic!("Expected Math node"),
}
}
#[test]
fn post_processor_preserves_non_math_nodes() {
let node = InlineNode::plain("text".to_string());
let processed = math_to_mathml_processor(node.clone());
assert_eq!(processed, node);
}
#[test]
fn integration_with_parser() {
let parser =
InlineParser::new().with_post_processor(InlineKind::Math, math_to_mathml_processor);
let nodes = parser.parse("#x^2 + y#");
assert_eq!(nodes.len(), 1);
match &nodes[0] {
InlineNode::Math { text, annotations } => {
assert_eq!(text, "x^2 + y");
assert_eq!(annotations.len(), 1);
assert_eq!(annotations[0].data.label.value, "doc.data");
}
_ => panic!("Expected Math node"),
}
}
#[test]
fn integration_full_lex_document_to_mathml() {
use crate::lex::loader::DocumentLoader;
let lex_source = "A formula #x^2 + y# in text.";
let loader = DocumentLoader::from_string(lex_source);
let doc = loader.parse().expect("Failed to parse document");
let para = doc.root.first_paragraph().expect("Expected a paragraph");
let parser =
InlineParser::new().with_post_processor(InlineKind::Math, math_to_mathml_processor);
let inlines = parser.parse(¶.text());
assert_eq!(inlines.len(), 3);
match &inlines[1] {
InlineNode::Math { text, annotations } => {
assert_eq!(text, "x^2 + y");
assert_eq!(annotations.len(), 1);
let anno = &annotations[0];
assert_eq!(anno.data.label.value, "doc.data");
assert_eq!(anno.data.parameters.len(), 1);
assert_eq!(anno.data.parameters[0].key, "type");
assert_eq!(anno.data.parameters[0].value, "mathml");
assert!(!anno.children.is_empty());
if let crate::lex::ast::elements::ContentItem::TextLine(line) = &anno.children[0] {
let mathml = line.text();
assert!(mathml.contains("<math"));
assert!(mathml.contains("</math>"));
assert!(mathml.contains("<msup")); } else {
panic!("Expected TextLine in annotation children");
}
}
_ => panic!("Expected Math node at position 1"),
}
}
}