katex-parser 0.1.0

A port of the KaTeX parser: lexing and parsing LaTeX math expressions with macro expansion into a typed AST, plus a Unicode rendering backend
Documentation
use crate::ast::{ArrayColumn, ColumnSeparationType, ParseNode, StyleLevel};
use crate::error::ParseError;
use crate::environments::registry::{ArrayEnvironmentOptions, EnvironmentContext, EnvironmentParser};

pub(crate) fn matrix_column_count(body: &[Vec<ParseNode>]) -> usize {
    body.iter().map(|row| row.len()).max().unwrap_or(0)
}

fn matrix_columns(count: usize, alignment: &str) -> Vec<ArrayColumn> {
    (0..count)
        .map(|_| ArrayColumn::AlignColumn {
            alignment: alignment.to_string(),
            pre_gap: 0.0,
            post_gap: 0.0,
        })
        .collect()
}

pub(crate) fn array_with_columns(node: ParseNode, columns: Vec<ArrayColumn>) -> Result<ParseNode, ParseError> {
    match node {
        ParseNode::Array {
            mode,
            body,
            add_jot,
            array_stretch,
            row_gaps,
            hskip_before_and_after,
            hlines_before_row,
            column_separation_type,
            tags,
            auto_tags,
            leqno,
            ..
        } => Ok(ParseNode::Array {
            mode,
            body,
            add_jot,
            array_stretch,
            columns: Some(columns),
            row_gaps,
            hskip_before_and_after,
            hlines_before_row,
            column_separation_type,
            tags,
            auto_tags,
            leqno,
        }),
        _ => Err(ParseError::InternalInvariant {
            message: "Expected matrix array".to_string(),
        }),
    }
}

fn matrix_delimiters(name: &str) -> Option<(String, String)> {
    match name {
        "matrix" => None,
        "pmatrix" => Some(("(".to_string(), ")".to_string())),
        "bmatrix" => Some(("[".to_string(), "]".to_string())),
        "Bmatrix" => Some(("\\{".to_string(), "\\}".to_string())),
        "vmatrix" => Some(("|".to_string(), "|".to_string())),
        "Vmatrix" => Some(("\\Vert".to_string(), "\\Vert".to_string())),
        _ => None,
    }
}

pub(crate) fn matrix_environment_handler(
    parser: &mut dyn EnvironmentParser,
    context: &EnvironmentContext,
    _args: &[ParseNode],
    _opt_args: &[Option<ParseNode>],
) -> Result<ParseNode, ParseError> {
    let starred = context.env_name.ends_with('*');
    let base_name = if starred {
        context.env_name[..context.env_name.len() - 1].to_string()
    } else {
        context.env_name.clone()
    };
    let requested_alignment = if starred {
        parser.parse_matrix_alignment()?
    } else {
        None
    };
    let alignment = requested_alignment.unwrap_or_else(|| "c".to_string());
    let array = parser.parse_array(ArrayEnvironmentOptions {
        columns: None,
        array_stretch: 1.0,
        hskip_before_and_after: false,
        cell_style: StyleLevel::TextStyle,
        max_columns: None,
        single_row: false,
        auto_tag: None,
        leqno: false,
        add_jot: false,
        column_separation_type: None,
    })?;
    let count = match &array {
        ParseNode::Array { body, .. } => matrix_column_count(body),
        _ => {
            return Err(ParseError::InternalInvariant {
                message: "Expected matrix array".to_string(),
            })
        }
    };
    let array = array_with_columns(array, matrix_columns(count, &alignment))?;
    if let Some((left, right)) = matrix_delimiters(&base_name) {
        Ok(ParseNode::LeftRight {
            mode: context.mode,
            body: vec![array],
            left,
            right,
            right_color: None,
        })
    } else {
        Ok(array)
    }
}

pub(crate) fn smallmatrix_environment_handler(
    parser: &mut dyn EnvironmentParser,
    _context: &EnvironmentContext,
    _args: &[ParseNode],
    _opt_args: &[Option<ParseNode>],
) -> Result<ParseNode, ParseError> {
    let array = parser.parse_array(ArrayEnvironmentOptions {
        columns: None,
        array_stretch: 0.5,
        hskip_before_and_after: false,
        cell_style: StyleLevel::ScriptStyle,
        max_columns: None,
        single_row: false,
        auto_tag: None,
        leqno: false,
        add_jot: false,
        column_separation_type: Some(ColumnSeparationType::SmallSeparation),
    })?;
    let count = match &array {
        ParseNode::Array { body, .. } => matrix_column_count(body),
        _ => {
            return Err(ParseError::InternalInvariant {
                message: "Expected smallmatrix array".to_string(),
            })
        }
    };
    array_with_columns(array, matrix_columns(count, "c"))
}