use crate::command_registry::{parse_custom_macros, CommandRegistry};
use crate::tex_parser::LatexParser;
use crate::typst_writer::SymbolShorthand;
use regex::{Captures, Regex};
pub mod command_registry;
pub mod converter;
pub mod definitions;
pub mod map;
mod tests;
pub mod tex_parser;
pub mod tex_parser_utils;
pub mod tex_tokenizer;
pub mod typst_writer;
pub fn tex2typst(tex: &str) -> Result<String, String> {
let tex_tree = tex_parser::parse_tex(tex)?;
let typst_tree = converter::convert_tree(&tex_tree)?;
let mut writer = typst_writer::TypstWriter::new();
writer.serialize(&typst_tree)?;
let typst = writer.finalize()?;
Ok(typst)
}
pub fn tex2typst_with_macros(tex: &str, macro_definitions: &str) -> Result<String, String> {
let tokens = tex_tokenizer::tokenize(tex)?;
let custom_macros = parse_custom_macros(macro_definitions)?;
let mut registry = CommandRegistry::new();
registry.register_custom_macros(custom_macros);
let expanded_tokens = registry.expand_macros(&tokens)?;
let parser = LatexParser::new(false, false);
let tex_tree = parser.parse(expanded_tokens)?;
let typst_tree = converter::convert_tree(&tex_tree)?;
let mut writer = typst_writer::TypstWriter::new();
writer.serialize(&typst_tree)?;
let typst = writer.finalize()?;
Ok(typst)
}
pub fn text_and_tex2typst(input: &str) -> Result<String, String> {
let regex = Regex::new(r"\\\((.+?)\\\)|(?s)\\\[(.+?)\\\]").unwrap();
replace_all(®ex, input, |caps: &Captures| {
if let Some(inline_math) = caps.get(1) {
let typst_math = tex2typst(inline_math.as_str().trim())?;
Ok(format!("${}$", typst_math))
} else if let Some(display_math) = caps.get(2) {
let typst_math = tex2typst(display_math.as_str().trim()).map_err(|e| e.to_string())?;
Ok(format!("$\n{}\n$", typst_math))
} else {
Ok(caps[0].to_string())
}
})
}
pub fn text_and_tex2typst_with_macros(input: &str, macro_definitions: &str) -> Result<String, String> {
let regex = Regex::new(r"\\\((.+?)\\\)|(?s)\\\[(.+?)\\\]").unwrap();
replace_all(®ex, input, |caps: &Captures| {
if let Some(inline_math) = caps.get(1) {
let typst_math = tex2typst_with_macros(inline_math.as_str().trim(), macro_definitions)?;
Ok(format!("${}$", typst_math))
} else if let Some(display_math) = caps.get(2) {
let typst_math =
tex2typst_with_macros(display_math.as_str().trim(), macro_definitions).map_err(|e| e.to_string())?;
Ok(format!("$\n{}\n$", typst_math))
} else {
Ok(caps[0].to_string())
}
})
}
pub fn replace_all<E>(
re: &Regex,
haystack: &str,
replacement: impl Fn(&Captures) -> Result<String, E>,
) -> Result<String, E> {
let mut new = String::with_capacity(haystack.len());
let mut last_match = 0;
for caps in re.captures_iter(haystack) {
let m = caps.get(0).unwrap();
new.push_str(&haystack[last_match..m.start()]);
new.push_str(&replacement(&caps)?);
last_match = m.end();
}
new.push_str(&haystack[last_match..]);
Ok(new)
}
pub fn tex2typst_with_shorthands(tex: &str, shorthands: &Vec<SymbolShorthand>) -> Result<String, String> {
let tex_tree = tex_parser::parse_tex(tex)?;
let typst_tree = converter::convert_tree(&tex_tree)?;
let mut writer = typst_writer::TypstWriter::new();
writer.serialize(&typst_tree)?;
writer.replace_with_shorthand(shorthands);
let typst = writer.finalize()?;
Ok(typst)
}
pub fn text_and_tex2typst_with_shorthands(input: &str, shorthands: &Vec<SymbolShorthand>) -> Result<String, String> {
let regex = Regex::new(r"\\\((.+?)\\\)|(?s)\\\[(.+?)\\\]").unwrap();
replace_all(®ex, input, |caps: &Captures| {
if let Some(inline_math) = caps.get(1) {
let typst_math = tex2typst_with_shorthands(inline_math.as_str().trim(), shorthands)?;
Ok(format!("${}$", typst_math))
} else if let Some(display_math) = caps.get(2) {
let typst_math =
tex2typst_with_shorthands(display_math.as_str().trim(), shorthands).map_err(|e| e.to_string())?;
Ok(format!("$\n{}\n$", typst_math))
} else {
Ok(caps[0].to_string())
}
})
}