mod binary;
mod constants;
mod templates;
use crate::formula::ast::MathNode;
pub struct MtefParser<'arena> {
#[allow(dead_code)]
arena: &'arena bumpalo::Bump,
binary_parser: Option<binary::MtefBinaryParser<'arena>>,
}
impl<'arena> MtefParser<'arena> {
pub fn new(arena: &'arena bumpalo::Bump, data: &'arena [u8]) -> Self {
let binary_parser = match binary::MtefBinaryParser::new(arena, data) {
Ok(parser) => Some(parser),
Err(e) => {
eprintln!("DEBUG: MTEF parser validation failed ({} bytes): {}", data.len(), e);
if data.len() >= 28 {
eprintln!("DEBUG: OLE header bytes: {:02X?}", &data[0..28]);
if data.len() >= 40 {
eprintln!("DEBUG: Bytes 28-40: {:02X?}", &data[28..40]);
}
}
None
}
};
Self {
arena,
binary_parser,
}
}
pub fn parse(&mut self) -> Result<Vec<MathNode<'arena>>, MtefError> {
if let Some(ref mut parser) = self.binary_parser {
parser.parse()
} else {
Ok(Vec::new())
}
}
pub fn is_valid(&self) -> bool {
self.binary_parser.is_some()
}
pub fn version_info(&self) -> Option<(u8, u8, u8, u8, u8)> {
self.binary_parser.as_ref().map(|p| (
p.mtef_version,
p.platform,
p.product,
p.version,
p.version_sub,
))
}
}
#[derive(Debug)]
pub enum MtefError {
InvalidFormat(String),
UnexpectedEof,
UnknownTag(u8),
ParseError(String),
}
impl std::fmt::Display for MtefError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MtefError::InvalidFormat(msg) => write!(f, "Invalid format: {}", msg),
MtefError::UnexpectedEof => write!(f, "Unexpected end of file"),
MtefError::UnknownTag(tag) => write!(f, "Unknown tag: {:#x}", tag),
MtefError::ParseError(msg) => write!(f, "Parse error: {}", msg),
}
}
}
impl std::error::Error for MtefError {}
#[cfg(test)]
mod tests {
use super::*;
use crate::formula::ast::Formula;
use smallvec::smallvec;
use std::borrow::Cow;
#[test]
fn test_mtef_parser_creation() {
let formula = Formula::new();
let parser = MtefParser::new(formula.arena(), &[0u8; 100]);
assert!(!parser.is_valid());
}
#[test]
fn test_mtef_parser_with_valid_header() {
let data = vec![
0x1C, 0x00, 0x00, 0x00, 0x02, 0x00, 0xD3, 0xC2, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x04, 0x6D, 0x74, 0x05, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, ];
let formula = Formula::new();
let parser = MtefParser::new(formula.arena(), &data);
assert!(parser.is_valid());
if let Some((version, platform, product, ver, sub)) = parser.version_info() {
assert_eq!(version, 5);
assert_eq!(platform, 1);
assert_eq!(product, 1);
assert_eq!(ver, 1);
assert_eq!(sub, 0);
}
}
#[test]
fn test_mtef_parser_invalid_data() {
let formula = Formula::new();
let parser1 = MtefParser::new(formula.arena(), &[0u8; 10]);
assert!(!parser1.is_valid());
let mut data = vec![0u8; 28];
data[0] = 0x10; let parser2 = MtefParser::new(formula.arena(), &data);
assert!(!parser2.is_valid());
}
#[test]
fn test_character_lookup() {
use crate::formula::mtef::binary::charset::lookup_character;
let result = lookup_character(132, 97, 1);
assert_eq!(result, Some("\\alpha "));
let result = lookup_character(133, 68, 1);
assert_eq!(result, Some("\\Delta "));
let result = lookup_character(134, 61, 1);
assert_eq!(result, Some("="));
let result = lookup_character(999, 999, 1);
assert_eq!(result, None);
}
#[test]
fn test_embellishment_templates() {
use crate::formula::mtef::binary::charset::get_embellishment_template;
let template = get_embellishment_template(2); assert_eq!(template, "\\dot{%1} ,\\.%1 ");
let template = get_embellishment_template(9); assert_eq!(template, "\\hat{%1} ,\\^%1 ");
let template = get_embellishment_template(11); assert_eq!(template, "\\vec{%1} ,%1 ");
let template = get_embellishment_template(255);
assert_eq!(template, "");
}
#[test]
fn test_template_parsing() {
use crate::formula::mtef::templates::TemplateParser;
let template = TemplateParser::find_template(0, 3); assert!(template.is_some());
let template_def = template.unwrap();
assert_eq!(template_def.selector, 0);
assert_eq!(template_def.variation, 3);
assert!(template_def.template.contains("\\left\\langle"));
let template = TemplateParser::find_template(11, 0); assert!(template.is_some());
let template_def = template.unwrap();
assert!(template_def.template.contains("\\frac"));
let template = TemplateParser::find_template(255, 0);
assert!(template.is_none());
}
#[test]
fn test_fence_template_conversion() {
use crate::formula::mtef::templates::TemplateParser;
let fence = TemplateParser::fence_from_selector(1); assert_eq!(fence, Some(crate::formula::ast::Fence::Paren));
let fence = TemplateParser::fence_from_selector(2); assert_eq!(fence, Some(crate::formula::ast::Fence::Brace));
let fence = TemplateParser::fence_from_selector(3); assert_eq!(fence, Some(crate::formula::ast::Fence::Bracket));
let fence = TemplateParser::fence_from_selector(4); assert_eq!(fence, Some(crate::formula::ast::Fence::Pipe));
let fence = TemplateParser::fence_from_selector(255); assert!(fence.is_none());
}
#[test]
fn test_large_operator_conversion() {
use crate::formula::mtef::templates::TemplateParser;
let op = TemplateParser::large_op_from_selector(15); assert_eq!(op, Some(crate::formula::ast::LargeOperator::Integral));
let op = TemplateParser::large_op_from_selector(16); assert_eq!(op, Some(crate::formula::ast::LargeOperator::Sum));
let op = TemplateParser::large_op_from_selector(17); assert_eq!(op, Some(crate::formula::ast::LargeOperator::Product));
let op = TemplateParser::large_op_from_selector(21); assert_eq!(op, Some(crate::formula::ast::LargeOperator::Integral));
let op = TemplateParser::large_op_from_selector(255); assert!(op.is_none());
}
#[test]
fn test_template_ast_parsing() {
use crate::formula::mtef::templates::TemplateParser;
let args: smallvec::SmallVec<[smallvec::SmallVec<[crate::formula::ast::MathNode; 8]>; 4]> = smallvec![
smallvec![crate::formula::ast::MathNode::Number(Cow::Borrowed("1"))],
smallvec![crate::formula::ast::MathNode::Number(Cow::Borrowed("2"))]
];
let result = TemplateParser::parse_fraction(args[0].to_vec(), args[1].to_vec());
match result {
crate::formula::ast::MathNode::Frac { numerator, denominator, .. } => {
assert_eq!(numerator.len(), 1);
assert_eq!(denominator.len(), 1);
}
_ => panic!("Expected fraction node"),
}
}
#[test]
fn test_charset_attributes() {
use crate::formula::mtef::binary::charset::get_charset_attributes;
let attrs = get_charset_attributes(0); assert_eq!(attrs.math_attr, 1); assert!(attrs.do_lookup);
assert!(attrs.use_codepoint);
let attrs = get_charset_attributes(1); assert_eq!(attrs.math_attr, 2); assert!(attrs.do_lookup);
assert!(attrs.use_codepoint);
let attrs = get_charset_attributes(100);
assert_eq!(attrs.math_attr, 3); assert!(attrs.do_lookup);
assert!(attrs.use_codepoint);
}
}