diff --git a/crates/math-core/src/commands.rs b/crates/math-core/src/commands.rs index 145cb1a7..b0b21f60 100644 --- a/crates/math-core/src/commands.rs +++ b/crates/math-core/src/commands.rs @@ -1,5 +1,5 @@ use mathml_renderer::attribute::{ - FracAttr, HtmlTextStyle, MathVariant, Notation, OpAttr, ParenType, Size, Style, TextTransform, + FracAttr, HtmlTextStyle, MathVariant, Notation, OpAttrs, ParenType, Size, Style, TextTransform, }; use mathml_renderer::symbol::{self, Rel}; @@ -119,7 +119,7 @@ static COMMANDS: phf::Map<&'static str, Token> = phf::phf_map! { "_" => Letter(symbol::LOW_LINE.as_op().as_char(), Mode::MathOrText), "`" => TextMode(TextToken::Accent(symbol::COMBINING_GRAVE_ACCENT)), "aa" => TextMode(TextToken::Letter('å')), - "acute" => Accent(symbol::ACUTE_ACCENT, true, None), + "acute" => Accent(symbol::ACUTE_ACCENT, true, OpAttrs::empty()), "ae" => TextMode(TextToken::Letter('æ')), "aleph" => Letter(symbol::ALEF_SYMBOL, Mode::Math), "alpha" => Letter(symbol::GREEK_SMALL_LETTER_ALPHA, Mode::Math), @@ -142,7 +142,7 @@ static COMMANDS: phf::Map<&'static str, Token> = phf::phf_map! { "backsimeq" => Relation(symbol::REVERSED_TILDE_EQUALS), "backslash" => Ord(symbol::REVERSE_SOLIDUS), "backtrprime" => Ord(symbol::REVERSED_TRIPLE_PRIME), - "bar" => Accent(symbol::MACRON, true, Some(OpAttr::StretchyFalse)), + "bar" => Accent(symbol::MACRON, true, OpAttrs::STRETCHY_FALSE), "barwedge" => BinaryOp(symbol::NAND), "bcancel" => Enclose(Notation::DOWN_DIAGONAL), "because" => Relation(symbol::BECAUSE), @@ -195,7 +195,7 @@ static COMMANDS: phf::Map<&'static str, Token> = phf::phf_map! { "boxslash" => BinaryOp(symbol::SQUARED_RISING_DIAGONAL_SLASH), "boxtimes" => ForceBinaryOp(symbol::SQUARED_TIMES.as_op()), "bra" => CustomCmd(1, &predefined::BRA), - "breve" => Accent(symbol::BREVE, true, None), + "breve" => Accent(symbol::BREVE, true, OpAttrs::empty()), "bullet" => ForceBinaryOp(symbol::BULLET_OPERATOR.as_op()), "bumpeq" => Relation(symbol::DIFFERENCE_BETWEEN), "c" => TextMode(TextToken::Accent(symbol::COMBINING_CEDILLA)), @@ -206,7 +206,7 @@ static COMMANDS: phf::Map<&'static str, Token> = phf::phf_map! { "cdots" => CustomCmd(0, &predefined::CDOTS), "centerdot" => ForceBinaryOp(symbol::BULLET_OPERATOR.as_op()), "cfrac" => Frac(Some(FracAttr::CFracStyle)), - "check" => Accent(symbol::CARON, true, Some(OpAttr::StretchyFalse)), + "check" => Accent(symbol::CARON, true, OpAttrs::STRETCHY_FALSE), "checkmark" => Letter('✓', Mode::Math), "chi" => Letter(symbol::GREEK_SMALL_LETTER_CHI, Mode::Math), "circ" => ForceBinaryOp(symbol::RING_OPERATOR.as_op()), @@ -244,9 +244,9 @@ static COMMANDS: phf::Map<&'static str, Token> = phf::phf_map! { "dblcolon" => Relation(symbol::PROPORTION), "ddag" => Letter(symbol::DOUBLE_DAGGER, Mode::Math), "ddagger" => Letter(symbol::DOUBLE_DAGGER, Mode::Math), - "ddddot" => Accent(symbol::COMBINING_FOUR_DOTS_ABOVE, true, Some(OpAttr::StretchyFalse)), - "dddot" => Accent(symbol::COMBINING_THREE_DOTS_ABOVE, true, Some(OpAttr::StretchyFalse)), - "ddot" => Accent(symbol::DIAERESIS, true, Some(OpAttr::StretchyFalse)), + "ddddot" => Accent(symbol::COMBINING_FOUR_DOTS_ABOVE, true, OpAttrs::STRETCHY_FALSE), + "dddot" => Accent(symbol::COMBINING_THREE_DOTS_ABOVE, true, OpAttrs::STRETCHY_FALSE), + "ddot" => Accent(symbol::DIAERESIS, true, OpAttrs::STRETCHY_FALSE), "ddots" => Relation(symbol::DOWN_RIGHT_DIAGONAL_ELLIPSIS), "delta" => Letter(symbol::GREEK_SMALL_LETTER_DELTA, Mode::Math), "dfrac" => Frac(Some(FracAttr::DisplayStyleTrue)), @@ -258,7 +258,7 @@ static COMMANDS: phf::Map<&'static str, Token> = phf::phf_map! { "div" => BinaryOp(symbol::DIVISION_SIGN), "divideontimes" => ForceBinaryOp(symbol::DIVISION_TIMES.as_op()), "dj" => TextMode(TextToken::Letter('đ')), - "dot" => Accent(symbol::DOT_ABOVE, true, None), + "dot" => Accent(symbol::DOT_ABOVE, true, OpAttrs::empty()), "doteq" => Relation(symbol::APPROACHES_THE_LIMIT), "doteqdot" => Relation(symbol::GEOMETRICALLY_EQUAL_TO), "dotplus" => BinaryOp(symbol::DOT_PLUS), @@ -315,14 +315,14 @@ static COMMANDS: phf::Map<&'static str, Token> = phf::phf_map! { "gneq" => Relation(symbol::GREATER_THAN_AND_SINGLE_LINE_NOT_EQUAL_TO), "gneqq" => Relation(symbol::GREATER_THAN_BUT_NOT_EQUAL_TO), "gnsim" => Relation(symbol::GREATER_THAN_BUT_NOT_EQUIVALENT_TO), - "grave" => Accent(symbol::GRAVE_ACCENT, true, None), + "grave" => Accent(symbol::GRAVE_ACCENT, true, OpAttrs::empty()), "gt" => OpGreaterThan, "gtrapprox" => Relation(symbol::GREATER_THAN_OR_APPROXIMATE), "gtreqless" => Relation(symbol::GREATER_THAN_EQUAL_TO_OR_LESS_THAN), "gtreqqless" => Relation(symbol::GREATER_THAN_ABOVE_DOUBLE_LINE_EQUAL_ABOVE_LESS_THAN), "gtrless" => Relation(symbol::GREATER_THAN_OR_LESS_THAN), "gtrsim" => Relation(symbol::GREATER_THAN_OR_EQUIVALENT_TO), - "hat" => Accent(symbol::COMBINING_CIRCUMFLEX_ACCENT, true, Some(OpAttr::StretchyFalse)), + "hat" => Accent(symbol::COMBINING_CIRCUMFLEX_ACCENT, true, OpAttrs::STRETCHY_FALSE), "hbar" => Letter(symbol::PLANCK_CONSTANT_OVER_TWO_PI, Mode::Math), "heartsuit" => Letter(symbol::WHITE_HEART_SUIT, Mode::Math), "hookleftarrow" => Relation(symbol::LEFTWARDS_ARROW_WITH_HOOK), @@ -511,10 +511,10 @@ static COMMANDS: phf::Map<&'static str, Token> = phf::phf_map! { "otimes" => ForceBinaryOp(symbol::CIRCLED_TIMES.as_op()), "overbrace" => OverUnderBrace(symbol::TOP_CURLY_BRACKET, true), "overbracket" => OverUnderBrace(symbol::TOP_SQUARE_BRACKET, true), - "overleftarrow" => Accent(symbol::LEFTWARDS_ARROW, true, None), - "overline" => Accent(symbol::LOW_LINE, true, None), + "overleftarrow" => Accent(symbol::LEFTWARDS_ARROW, true, OpAttrs::empty()), + "overline" => Accent(symbol::LOW_LINE, true, OpAttrs::empty()), "overparen" => OverUnderBrace(symbol::TOP_PARENTHESIS, true), - "overrightarrow" => Accent(symbol::RIGHTWARDS_ARROW, true, None), + "overrightarrow" => Accent(symbol::RIGHTWARDS_ARROW, true, OpAttrs::empty()), "overset" => Overset, "parallel" => Relation(symbol::PARALLEL_TO), "partial" => Letter(symbol::PARTIAL_DIFFERENTIAL, Mode::Math), @@ -641,7 +641,7 @@ static COMMANDS: phf::Map<&'static str, Token> = phf::phf_map! { "theta" => Letter(symbol::GREEK_SMALL_LETTER_THETA, Mode::Math), "thickspace" => Space(LatexUnit::Mu.length_with_unit(5.0)), "thinspace" => Space(LatexUnit::Mu.length_with_unit(3.0)), - "tilde" => Accent(symbol::COMBINING_TILDE, true, Some(OpAttr::StretchyTrue)), // should be stretchy=false, but Safari has a bug + "tilde" => Accent(symbol::COMBINING_TILDE, true, OpAttrs::STRETCHY_TRUE), // should be stretchy=false, but Safari has a bug "times" => ForceBinaryOp(symbol::MULTIPLICATION_SIGN.as_op()), "to" => Relation(symbol::RIGHTWARDS_ARROW), "top" => Letter(symbol::DOWN_TACK, Mode::Math), @@ -658,7 +658,7 @@ static COMMANDS: phf::Map<&'static str, Token> = phf::phf_map! { "ulcorner" => Letter(symbol::TOP_LEFT_CORNER, Mode::Math), "underbrace" => OverUnderBrace(symbol::BOTTOM_CURLY_BRACKET, false), "underbracket" => OverUnderBrace(symbol::BOTTOM_SQUARE_BRACKET, false), - "underline" => Accent(symbol::LOW_LINE, false, None), + "underline" => Accent(symbol::LOW_LINE, false, OpAttrs::empty()), "underparen" => OverUnderBrace(symbol::BOTTOM_PARENTHESIS, false), "underset" => Underset, "unlhd" => Relation(symbol::NORMAL_SUBGROUP_OF_OR_EQUAL_TO), @@ -696,7 +696,7 @@ static COMMANDS: phf::Map<&'static str, Token> = phf::phf_map! { "vartriangle" => Letter(symbol::WHITE_UP_POINTING_TRIANGLE, Mode::Math), "vdash" => Relation(symbol::RIGHT_TACK), "vdots" => Relation(symbol::VERTICAL_ELLIPSIS), - "vec" => Accent(symbol::COMBINING_RIGHT_ARROW_ABOVE, true, Some(OpAttr::StretchyFalse)), + "vec" => Accent(symbol::COMBINING_RIGHT_ARROW_ABOVE, true, OpAttrs::STRETCHY_FALSE), "vee" => BinaryOp(symbol::LOGICAL_OR), "veebar" => BinaryOp(symbol::XOR), "veeeq" => Relation(symbol::EQUIANGULAR_TO), // from "stix" @@ -704,10 +704,10 @@ static COMMANDS: phf::Map<&'static str, Token> = phf::phf_map! { "vert" => Ord(symbol::VERTICAL_LINE), "wedge" => BinaryOp(symbol::LOGICAL_AND), "wedgeq" => Relation(symbol::ESTIMATES), // from "stix" - "widecheck" => Accent(symbol::CARON, true, None), - "widehat" => Accent(symbol::COMBINING_CIRCUMFLEX_ACCENT, true, Some(OpAttr::StretchyTrue)), + "widecheck" => Accent(symbol::CARON, true, OpAttrs::empty()), + "widehat" => Accent(symbol::COMBINING_CIRCUMFLEX_ACCENT, true, OpAttrs::STRETCHY_TRUE), "wideparen" => OverUnderBrace(symbol::TOP_PARENTHESIS, true), - "widetilde" => Accent(symbol::TILDE, true, None), + "widetilde" => Accent(symbol::TILDE, true, OpAttrs::empty()), "wp" => Letter(symbol::SCRIPT_CAPITAL_P, Mode::Math), "wr" => ForceBinaryOp(symbol::WREATH_PRODUCT.as_op()), "xcancel" => Enclose(Notation::UP_DIAGONAL.union(Notation::DOWN_DIAGONAL)), diff --git a/crates/math-core/src/parser.rs b/crates/math-core/src/parser.rs index ea1eb885..6fd252c9 100644 --- a/crates/math-core/src/parser.rs +++ b/crates/math-core/src/parser.rs @@ -4,7 +4,7 @@ use mathml_renderer::{ arena::{Arena, Buffer}, ast::Node, attribute::{ - LetterAttr, MathSpacing, MathVariant, OpAttr, ParenType, RowAttr, StretchMode, Style, + LetterAttr, MathSpacing, MathVariant, OpAttrs, ParenType, RowAttr, StretchMode, Style, TextTransform, }, length::Length, @@ -318,17 +318,17 @@ where RelCategory::A => { // We let it be stretchy if it's explicitly marked as stretchy if matches!(tok, Token::StretchyRel(_)) { - None + OpAttrs::empty() } else { - Some(OpAttr::StretchyFalse) + OpAttrs::STRETCHY_FALSE } } - RelCategory::Default => None, + RelCategory::Default => OpAttrs::empty(), }; let (left, right) = self.state.relation_spacing(prev_class, next_class); Ok(Node::Operator { op: relation.as_op(), - attr, + attrs: attr, left, right, }) @@ -342,7 +342,7 @@ where }; Ok(Node::Operator { op: punc.as_op(), - attr: None, + attrs: OpAttrs::empty(), left: None, right, }) @@ -352,9 +352,9 @@ where // Category F+G operators will stretch in pre- and postfix positions, // so we explicitly set the stretchy attribute to false to prevent that. // Alternatively, we could set `form="infix"` on them. - Some(OpAttr::StretchyFalse) + OpAttrs::STRETCHY_FALSE } else { - None + OpAttrs::empty() }; let (left, right) = if matches!( ord.category(), @@ -368,7 +368,7 @@ where }; Ok(Node::Operator { op: ord.as_op(), - attr, + attrs: attr, left, right, }) @@ -383,7 +383,7 @@ where ); Ok(Node::Operator { op: binary_op.as_op(), - attr: None, + attrs: OpAttrs::empty(), left: spacing, right: spacing, }) @@ -395,7 +395,7 @@ where .bin_op_spacing(parse_as.in_sequence(), prev_class, next_class, true); Ok(Node::Operator { op, - attr: None, + attrs: OpAttrs::empty(), left: spacing, right: spacing, }) @@ -440,7 +440,7 @@ where .bin_op_spacing(parse_as.in_sequence(), prev_class, next_class, true); Ok(Node::Operator { op, - attr: Some(OpAttr::StretchyFalse), + attrs: OpAttrs::STRETCHY_FALSE, left: spacing, right: spacing, }) @@ -470,7 +470,7 @@ where }; Ok(Node::Operator { op: op.as_op(), - attr: None, + attrs: OpAttrs::empty(), left, right, }) @@ -479,7 +479,7 @@ where let (left, right) = self.state.relation_spacing(prev_class, next_class); Ok(Node::PseudoOp { name: ">", - attr: None, + attrs: OpAttrs::empty(), left, right, }) @@ -488,14 +488,14 @@ where let (left, right) = self.state.relation_spacing(prev_class, next_class); Ok(Node::PseudoOp { name: "<", - attr: None, + attrs: OpAttrs::empty(), left, right, }) } Token::OpAmpersand => Ok(Node::PseudoOp { name: "&", - attr: None, + attrs: OpAttrs::empty(), left: None, right: None, }), @@ -503,7 +503,7 @@ where let (left, right) = self.big_operator_spacing(parse_as, prev_class, true)?; class = Class::Operator; Ok(Node::PseudoOp { - attr: None, + attrs: OpAttrs::empty(), left, right, name, @@ -682,7 +682,7 @@ where let target = self.parse_next(ParseAs::ArgWithSpace)?; let symbol = self.commit(Node::Operator { op: x.as_op(), - attr: None, + attrs: OpAttrs::empty(), left: None, right: None, }); @@ -728,13 +728,13 @@ where }; let (left, right) = self.big_operator_spacing(parse_as, prev_class, false)?; let attr = if has_movable_limits && limits { - Some(OpAttr::NoMovableLimits) + OpAttrs::NO_MOVABLE_LIMITS } else { - None + OpAttrs::empty() }; let target = self.commit(Node::Operator { op: op.as_op(), - attr, + attrs: attr, left, right, }); @@ -762,9 +762,9 @@ where Token::PseudoOperatorLimits(name) => { let movablelimits = if matches!(self.tokens.peek().token(), Token::Limits) { self.next_token()?; // Discard the limits token. - Some(OpAttr::NoMovableLimits) + OpAttrs::NO_MOVABLE_LIMITS } else { - Some(OpAttr::ForceMovableLimits) + OpAttrs::FORCE_MOVABLE_LIMITS }; class = Class::Operator; let bounds = self.get_bounds()?; @@ -772,8 +772,8 @@ where // consider tokens that are part of the bounds for spacing calculations. let (left, right) = self.big_operator_spacing(parse_as, prev_class, true)?; let op = self.commit(Node::PseudoOp { - attr: if matches!(bounds, Bounds(None, None)) { - None + attrs: if matches!(bounds, Bounds(None, None)) { + OpAttrs::empty() } else { movablelimits }, @@ -813,14 +813,14 @@ where if let Some(negated) = get_negated_op(op) { Ok(Node::Operator { op: negated.as_op(), - attr: None, + attrs: OpAttrs::empty(), left, right, }) } else { Ok(Node::Operator { op: op.as_op(), - attr: None, + attrs: OpAttrs::empty(), left, right, }) @@ -834,7 +834,7 @@ where } else { symbol::NOT_GREATER_THAN.as_op() }, - attr: None, + attrs: OpAttrs::empty(), left, right, }) @@ -842,7 +842,7 @@ where // We have to special-case `\exists` here because it is not a relation. Token::Ord(symbol::THERE_EXISTS) => Ok(Node::Operator { op: symbol::THERE_DOES_NOT_EXIST.as_op(), - attr: None, + attrs: OpAttrs::empty(), left: None, right: None, }), @@ -879,7 +879,7 @@ where }; Ok(Node::Operator { op, - attr: None, + attrs: OpAttrs::empty(), left, right, }) @@ -888,7 +888,7 @@ where class = Class::Close; Ok(Node::Operator { op, - attr: None, + attrs: OpAttrs::empty(), left: None, right: None, }) @@ -920,12 +920,12 @@ where // For this category of symbol, we have to force the form attribute // in order to get correct spacing. if open { - Some(OpAttr::FormPrefix) + OpAttrs::FORM_PREFIX } else { - Some(OpAttr::FormPostfix) + OpAttrs::FORM_POSTFIX } } else { - None + OpAttrs::empty() }; Ok(Node::StretchableOp( stretchable_op, @@ -940,7 +940,7 @@ where Ok(Node::StretchableOp( SQ_L_BRACKET, StretchMode::NoStretch, - None, + OpAttrs::empty(), )) } Token::SquareBracketClose => { @@ -949,7 +949,7 @@ where Ok(Node::StretchableOp( SQ_R_BRACKET, StretchMode::NoStretch, - None, + OpAttrs::empty(), )) } Token::Left => { @@ -982,7 +982,11 @@ where Token::Middle => { let tok_loc = self.next_token()?; let op = extract_delimiter(tok_loc, DelimiterModifier::Middle)?; - Ok(Node::StretchableOp(op, StretchMode::Middle, None)) + Ok(Node::StretchableOp( + op, + StretchMode::Middle, + OpAttrs::empty(), + )) } Token::Big(size, paren_type) => { let tok_loc = self.next_token()?; @@ -1088,7 +1092,7 @@ where let letters = builder.finish(self.arena); let (left, right) = self.big_operator_spacing(parse_as, prev_class, true)?; let op = self.commit(Node::PseudoOp { - attr: None, + attrs: OpAttrs::empty(), left, right, name: letters, @@ -1226,7 +1230,7 @@ where }); let symbol = self.commit(Node::Operator { op: symbol::PRIME.as_op(), - attr: None, + attrs: OpAttrs::empty(), left: None, right: None, }); @@ -1450,7 +1454,7 @@ where if let Some(op) = PRIME_SELECTION.get(prime_count - 1) { primes.push(self.commit(Node::Operator { op: op.as_op(), - attr: None, + attrs: OpAttrs::empty(), left: None, right: None, })); @@ -1458,7 +1462,7 @@ where for _ in 0..prime_count { primes.push(self.commit(Node::Operator { op: symbol::PRIME.as_op(), - attr: None, + attrs: OpAttrs::empty(), left: None, right: None, })); @@ -1734,14 +1738,14 @@ pub(crate) fn node_vec_to_node<'arena>( if reset_spacing { if let Node::Operator { op, - attr, + attrs: attr, left: _, right: _, } = single { arena.push(Node::Operator { op: *op, - attr: *attr, + attrs: *attr, left: None, right: None, }) diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__colon_fusion_in_subscript.snap b/crates/math-core/src/snapshots/math_core__parser__tests__colon_fusion_in_subscript.snap index 33866850..e1cfa9d6 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__colon_fusion_in_subscript.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__colon_fusion_in_subscript.snap @@ -7,20 +7,20 @@ expression: "x_:\\equiv, x_:=" target: IdentifierChar('x', Default), symbol: Operator( op: ':', - attr: None, + attrs: OpAttrs(""), left: None, right: None, ), ), Operator( op: '≡', - attr: None, + attrs: OpAttrs(""), left: None, right: Some(Zero), ), Operator( op: ',', - attr: None, + attrs: OpAttrs(""), left: None, right: None, ), @@ -28,14 +28,14 @@ expression: "x_:\\equiv, x_:=" target: IdentifierChar('x', Default), symbol: Operator( op: ':', - attr: None, + attrs: OpAttrs(""), left: None, right: None, ), ), Operator( op: '=', - attr: None, + attrs: OpAttrs(""), left: None, right: Some(Zero), ), diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__colon_fusion_stop.snap b/crates/math-core/src/snapshots/math_core__parser__tests__colon_fusion_stop.snap index dd0edbc7..50627dbb 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__colon_fusion_stop.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__colon_fusion_stop.snap @@ -5,26 +5,26 @@ expression: ":2=:=" [ Operator( op: ':', - attr: None, + attrs: OpAttrs(""), left: Some(Zero), right: Some(FiveMu), ), Number("2"), Operator( op: '=', - attr: None, + attrs: OpAttrs(""), left: None, right: Some(Zero), ), Operator( op: ':', - attr: None, + attrs: OpAttrs(""), left: Some(Zero), right: Some(Zero), ), Operator( op: '=', - attr: None, + attrs: OpAttrs(""), left: Some(Zero), right: Some(Zero), ), diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__displaystyle_ended_by_end.snap b/crates/math-core/src/snapshots/math_core__parser__tests__displaystyle_ended_by_end.snap index 5a19fc76..c4c19fd5 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__displaystyle_ended_by_end.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__displaystyle_ended_by_end.snap @@ -9,7 +9,7 @@ expression: "\\begin{matrix}\\sum\\displaystyle\\sum\\end{matrix}" content: [ Operator( op: '∑', - attr: None, + attrs: OpAttrs(""), left: Some(Zero), right: None, ), @@ -17,7 +17,7 @@ expression: "\\begin{matrix}\\sum\\displaystyle\\sum\\end{matrix}" nodes: [ Operator( op: '∑', - attr: None, + attrs: OpAttrs(""), left: Some(Zero), right: Some(Zero), ), diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__displaystyle_ended_by_right.snap b/crates/math-core/src/snapshots/math_core__parser__tests__displaystyle_ended_by_right.snap index eb9d230e..204bde33 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__displaystyle_ended_by_right.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__displaystyle_ended_by_right.snap @@ -11,7 +11,7 @@ expression: "\\left(\\displaystyle \\int\\right)\\int" nodes: [ Operator( op: '∫', - attr: None, + attrs: OpAttrs(""), left: Some(Zero), right: Some(Zero), ), @@ -21,7 +21,7 @@ expression: "\\left(\\displaystyle \\int\\right)\\int" ), Operator( op: '∫', - attr: None, + attrs: OpAttrs(""), left: None, right: Some(Zero), ), diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__double_prime.snap b/crates/math-core/src/snapshots/math_core__parser__tests__double_prime.snap index c009adb5..2110604f 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__double_prime.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__double_prime.snap @@ -7,7 +7,7 @@ expression: "f''" target: IdentifierChar('f', Default), symbol: Operator( op: '″', - attr: None, + attrs: OpAttrs(""), left: None, right: None, ), diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__int_bounds_relation.snap b/crates/math-core/src/snapshots/math_core__parser__tests__int_bounds_relation.snap index 00b88023..b7d9b52c 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__int_bounds_relation.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__int_bounds_relation.snap @@ -8,7 +8,7 @@ expression: "{\\int_0^\\infty = 4}" SubSup( target: Operator( op: '∫', - attr: None, + attrs: OpAttrs(""), left: Some(Zero), right: Some(Zero), ), @@ -17,7 +17,7 @@ expression: "{\\int_0^\\infty = 4}" ), Operator( op: '=', - attr: None, + attrs: OpAttrs(""), left: None, right: None, ), diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__int_relation.snap b/crates/math-core/src/snapshots/math_core__parser__tests__int_relation.snap index 26704bf8..2087294e 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__int_relation.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__int_relation.snap @@ -7,13 +7,13 @@ expression: "{\\int = 4}" nodes: [ Operator( op: '∫', - attr: None, + attrs: OpAttrs(""), left: Some(Zero), right: Some(Zero), ), Operator( op: '=', - attr: None, + attrs: OpAttrs(""), left: None, right: None, ), diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__integral_with_reversed_limits.snap b/crates/math-core/src/snapshots/math_core__parser__tests__integral_with_reversed_limits.snap index 100c64de..2fb8c237 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__integral_with_reversed_limits.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__integral_with_reversed_limits.snap @@ -6,7 +6,7 @@ expression: "\\int\\limits^1_0 dx" UnderOver( target: Operator( op: '∫', - attr: None, + attrs: OpAttrs(""), left: Some(Zero), right: None, ), diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__mathit_func.snap b/crates/math-core/src/snapshots/math_core__parser__tests__mathit_func.snap index 199c97c8..df113d8f 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__mathit_func.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__mathit_func.snap @@ -7,7 +7,7 @@ expression: "\\mathit{ab \\log cd}" nodes: [ IdentifierStr("𝑎𝑏"), PseudoOp( - attr: None, + attrs: OpAttrs(""), left: Some(ThreeMu), right: Some(ThreeMu), name: "log", diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__mathit_of_max.snap b/crates/math-core/src/snapshots/math_core__parser__tests__mathit_of_max.snap index d6df39d6..8dbb2971 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__mathit_of_max.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__mathit_of_max.snap @@ -7,7 +7,7 @@ expression: "\\mathit{ab \\max \\alpha\\beta}" nodes: [ IdentifierStr("𝑎𝑏"), PseudoOp( - attr: None, + attrs: OpAttrs(""), left: Some(ThreeMu), right: Some(ThreeMu), name: "max", diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__number_after_lim.snap b/crates/math-core/src/snapshots/math_core__parser__tests__number_after_lim.snap index c59af4ca..2cd0ca98 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__number_after_lim.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__number_after_lim.snap @@ -7,7 +7,7 @@ expression: "\\sum\\limits_12" symbol: Number("1"), target: Operator( op: '∑', - attr: Some(NoMovableLimits), + attrs: OpAttrs("NO_MOVABLE_LIMITS"), left: Some(Zero), right: None, ), diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__number_after_overbrace.snap b/crates/math-core/src/snapshots/math_core__parser__tests__number_after_overbrace.snap index 26db6782..e05d36cd 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__number_after_overbrace.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__number_after_overbrace.snap @@ -6,7 +6,7 @@ expression: "\\overbrace12" Over( symbol: Operator( op: '⏞', - attr: None, + attrs: OpAttrs(""), left: None, right: None, ), diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__number_with_spaces_with_dots.snap b/crates/math-core/src/snapshots/math_core__parser__tests__number_with_spaces_with_dots.snap index de1266cb..fbc9a3bb 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__number_with_spaces_with_dots.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__number_with_spaces_with_dots.snap @@ -6,7 +6,7 @@ expression: "1 2. 3 , 4" Number("12.3"), Operator( op: ',', - attr: None, + attrs: OpAttrs(""), left: None, right: None, ), diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__operatorname_number.snap b/crates/math-core/src/snapshots/math_core__parser__tests__operatorname_number.snap index 8db49ef7..6c623f6e 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__operatorname_number.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__operatorname_number.snap @@ -4,7 +4,7 @@ expression: "\\operatorname123" --- [ PseudoOp( - attr: None, + attrs: OpAttrs(""), left: Some(Zero), right: Some(ThreeMu), name: "1", diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__pmod_subscript.snap b/crates/math-core/src/snapshots/math_core__parser__tests__pmod_subscript.snap index 9b93e4c5..0d630b78 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__pmod_subscript.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__pmod_subscript.snap @@ -7,7 +7,7 @@ expression: "\\pmod{3}_4" value: 1.0, unit: Em, )), - StretchableOp(StretchableOp('(', Always), NoStretch, None), + StretchableOp(StretchableOp('(', Always), NoStretch, OpAttrs("")), IdentifierStr("mod"), Space(Length( value: 0.33333334, @@ -15,7 +15,7 @@ expression: "\\pmod{3}_4" )), Number("3"), Sub( - target: StretchableOp(StretchableOp(')', Always), NoStretch, None), + target: StretchableOp(StretchableOp(')', Always), NoStretch, OpAttrs("")), symbol: Number("4"), ), ] diff --git a/crates/math-core/src/snapshots/math_core__parser__tests__sum_relation.snap b/crates/math-core/src/snapshots/math_core__parser__tests__sum_relation.snap index d8ad26f9..c3110a0d 100644 --- a/crates/math-core/src/snapshots/math_core__parser__tests__sum_relation.snap +++ b/crates/math-core/src/snapshots/math_core__parser__tests__sum_relation.snap @@ -7,13 +7,13 @@ expression: "{\\sum = 4}" nodes: [ Operator( op: '∑', - attr: None, + attrs: OpAttrs(""), left: Some(Zero), right: Some(Zero), ), Operator( op: '=', - attr: None, + attrs: OpAttrs(""), left: None, right: None, ), diff --git a/crates/math-core/src/token.rs b/crates/math-core/src/token.rs index 37f2ef51..c70a3b6e 100644 --- a/crates/math-core/src/token.rs +++ b/crates/math-core/src/token.rs @@ -3,7 +3,7 @@ use std::ops::Range; use strum_macros::IntoStaticStr; use mathml_renderer::attribute::{ - FracAttr, HtmlTextStyle, MathVariant, Notation, OpAttr, ParenType, Size, Style, + FracAttr, HtmlTextStyle, MathVariant, Notation, OpAttrs, ParenType, Size, Style, }; use mathml_renderer::length::Length; use mathml_renderer::symbol::{Bin, MathMLOperator, Op, OrdLike, Punct, Rel}; @@ -82,7 +82,7 @@ pub enum Token<'source> { Big(Size, Option), /// Stretchy and non-stretchy accents, e.g. `\hat`, `\widehat`, `\bar`, `\overline`, etc. /// The `bool` is `true` for over-accents and `false` for under-accents. - Accent(Rel, bool, Option), + Accent(Rel, bool, OpAttrs), /// A token corresponding to LaTeX's "mathord" character class (class 0). Ord(OrdLike), /// A token corresponding to LaTeX's "mathop" character class (class 1). diff --git a/crates/mathml-renderer/src/ast.rs b/crates/mathml-renderer/src/ast.rs index 0c52804a..cf994bc7 100644 --- a/crates/mathml-renderer/src/ast.rs +++ b/crates/mathml-renderer/src/ast.rs @@ -5,7 +5,7 @@ use std::num::NonZeroU16; use serde::Serialize; use crate::attribute::{ - FracAttr, HtmlTextStyle, LetterAttr, MathSpacing, Notation, OpAttr, ParenType, RowAttr, Size, + FracAttr, HtmlTextStyle, LetterAttr, MathSpacing, Notation, OpAttrs, ParenType, RowAttr, Size, StretchMode, Style, }; use crate::fmt::new_line_and_indent; @@ -27,14 +27,14 @@ pub enum Node<'arena> { /// `...` for a single character. Operator { op: MathMLOperator, - attr: Option, + attrs: OpAttrs, left: Option, right: Option, }, - StretchableOp(StretchableOp, StretchMode, Option), + StretchableOp(StretchableOp, StretchMode, OpAttrs), /// `...` for a string. PseudoOp { - attr: Option, + attrs: OpAttrs, left: Option, right: Option, name: &'arena str, @@ -58,7 +58,7 @@ pub enum Node<'arena> { sup: &'arena Node<'arena>, }, /// `...` - OverAccent(MathMLOperator, Option, &'arena Node<'arena>), + OverAccent(MathMLOperator, OpAttrs, &'arena Node<'arena>), /// `...` UnderAccent(MathMLOperator, &'arena Node<'arena>), /// `...` @@ -203,7 +203,7 @@ impl Node<'_> { } Node::Operator { op, - attr, + attrs: attr, left, right, } => { @@ -211,7 +211,7 @@ impl Node<'_> { write!(s, ">{}", char::from(op))?; } Node::PseudoOp { - attr, + attrs: attr, left, right, name: text, @@ -340,9 +340,7 @@ impl Node<'_> { write!(s, "")?; target.emit(s, child_indent)?; writeln_indent!(s, child_indent, "::from(attr))?; - } + attr.write_to(s); write!(s, ">{}", char::from(op))?; writeln_indent!(s, base_indent, ""); } @@ -411,11 +409,11 @@ impl Node<'_> { None => write!(s, "")?, } new_line_and_indent(s, child_indent); - emit_stretchy_op(s, StretchMode::Fence, *open, None)?; + emit_stretchy_op(s, StretchMode::Fence, *open, OpAttrs::empty())?; // TODO: if `content` is an `mrow`, we should flatten it before emitting. content.emit(s, child_indent)?; new_line_and_indent(s, child_indent); - emit_stretchy_op(s, StretchMode::Fence, *close, None)?; + emit_stretchy_op(s, StretchMode::Fence, *close, OpAttrs::empty())?; writeln_indent!(s, base_indent, ""); } Node::SizedParen(size, paren, paren_type) => { @@ -613,14 +611,12 @@ impl Node<'_> { fn emit_operator_attributes( s: &mut String, - attr: Option, + attrs: OpAttrs, left: Option, right: Option, ) -> std::fmt::Result { - match attr { - Some(attributes) => write!(s, "::from(attributes))?, - None => write!(s, " { write!( @@ -768,9 +764,9 @@ fn emit_stretchy_op( s: &mut String, stretch_mode: StretchMode, op: Option, - attr: Option, + attrs: OpAttrs, ) -> std::fmt::Result { - emit_operator_attributes(s, attr, None, None)?; + emit_operator_attributes(s, attrs, None, None)?; if let Some(op) = op { match (stretch_mode, op.stretchy) { (StretchMode::Fence, Stretchy::Never) @@ -849,7 +845,7 @@ mod tests { assert_eq!( render(&Node::Operator { op: symbol::COLON.as_op(), - attr: None, + attrs: OpAttrs::empty(), left: Some(MathSpacing::FourMu), right: Some(MathSpacing::FourMu), }), @@ -858,7 +854,7 @@ mod tests { assert_eq!( render(&Node::Operator { op: symbol::COLON.as_op(), - attr: None, + attrs: OpAttrs::empty(), left: Some(MathSpacing::FourMu), right: Some(MathSpacing::Zero), }), @@ -867,7 +863,7 @@ mod tests { assert_eq!( render(&Node::Operator { op: symbol::IDENTICAL_TO.as_op(), - attr: None, + attrs: OpAttrs::empty(), left: Some(MathSpacing::Zero), right: None, }), @@ -876,7 +872,7 @@ mod tests { assert_eq!( render(&Node::Operator { op: symbol::PLUS_SIGN.as_op(), - attr: Some(OpAttr::FormPrefix), + attrs: OpAttrs::FORM_PREFIX, left: None, right: None, }), @@ -885,7 +881,7 @@ mod tests { assert_eq!( render(&Node::Operator { op: symbol::N_ARY_SUMMATION.as_op(), - attr: Some(OpAttr::NoMovableLimits), + attrs: OpAttrs::NO_MOVABLE_LIMITS, left: None, right: None, }), @@ -897,7 +893,7 @@ mod tests { fn render_pseudo_operator() { assert_eq!( render(&Node::PseudoOp { - attr: None, + attrs: OpAttrs::empty(), left: Some(MathSpacing::ThreeMu), right: Some(MathSpacing::ThreeMu), name: "sin" @@ -961,7 +957,7 @@ mod tests { assert_eq!( render(&Node::OverAccent( symbol::MACRON.as_op(), - Some(OpAttr::StretchyFalse), + OpAttrs::STRETCHY_FALSE, &Node::IdentifierChar('x', LetterAttr::Default), )), "x¯" @@ -969,7 +965,7 @@ mod tests { assert_eq!( render(&Node::OverAccent( symbol::OVERLINE.as_op(), - None, + OpAttrs::empty(), &Node::IdentifierChar('x', LetterAttr::Default), )), "x" @@ -993,13 +989,13 @@ mod tests { render(&Node::Over { symbol: &Node::Operator { op: symbol::EXCLAMATION_MARK, - attr: None, + attrs: OpAttrs::empty(), left: None, right: None }, target: &Node::Operator { op: symbol::EQUALS_SIGN.as_op(), - attr: None, + attrs: OpAttrs::empty(), left: None, right: None }, @@ -1014,7 +1010,7 @@ mod tests { render(&Node::Under { symbol: &Node::IdentifierChar('θ', LetterAttr::Default), target: &Node::PseudoOp { - attr: Some(OpAttr::ForceMovableLimits), + attrs: OpAttrs::FORCE_MOVABLE_LIMITS, left: Some(MathSpacing::ThreeMu), right: Some(MathSpacing::ThreeMu), name: "min", @@ -1151,7 +1147,7 @@ mod tests { &Node::IdentifierChar('x', LetterAttr::Default), &Node::Operator { op: symbol::EQUALS_SIGN.as_op(), - attr: None, + attrs: OpAttrs::empty(), left: None, right: None, }, diff --git a/crates/mathml-renderer/src/attribute.rs b/crates/mathml-renderer/src/attribute.rs index 9c52be78..7360aa29 100644 --- a/crates/mathml-renderer/src/attribute.rs +++ b/crates/mathml-renderer/src/attribute.rs @@ -23,21 +23,41 @@ impl MathVariant { } } -#[derive(Debug, Clone, Copy, PartialEq, IntoStaticStr)] -#[cfg_attr(feature = "serde", derive(Serialize))] -pub enum OpAttr { - #[strum(serialize = r#" stretchy="false""#)] - StretchyFalse = 1, - #[strum(serialize = r#" stretchy="true""#)] - StretchyTrue, - #[strum(serialize = r#" movablelimits="false""#)] - NoMovableLimits, - #[strum(serialize = r#" movablelimits="true""#)] - ForceMovableLimits, - #[strum(serialize = r#" form="prefix""#)] - FormPrefix, - #[strum(serialize = r#" form="postfix""#)] - FormPostfix, +bitflags! { + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[cfg_attr(feature = "serde", derive(Serialize))] + pub struct OpAttrs: u8 { + const STRETCHY_FALSE = 1; + const STRETCHY_TRUE = 1 << 1; + const NO_MOVABLE_LIMITS = 1 << 2; + const FORCE_MOVABLE_LIMITS = 1 << 3; + const FORM_PREFIX = 1 << 4; + const FORM_POSTFIX = 1 << 5; + } +} + +impl OpAttrs { + pub fn write_to(self, s: &mut String) { + if self.contains(OpAttrs::STRETCHY_FALSE) { + s.push_str(r#" stretchy="false""#); + } + if self.contains(OpAttrs::STRETCHY_TRUE) { + s.push_str(r#" stretchy="true""#); + } + if self.contains(OpAttrs::NO_MOVABLE_LIMITS) { + s.push_str(r#" movablelimits="false""#); + } + if self.contains(OpAttrs::FORCE_MOVABLE_LIMITS) { + s.push_str(r#" movablelimits="true""#); + } + if self.contains(OpAttrs::FORM_PREFIX) { + s.push_str(r#" form="prefix""#); + } + if self.contains(OpAttrs::FORM_POSTFIX) { + s.push_str(r#" form="postfix""#); + } + } } #[derive(Debug, Clone, Copy, PartialEq)] diff --git a/crates/mathml-renderer/src/lib.rs b/crates/mathml-renderer/src/lib.rs index 77405ccc..5edc09ce 100644 --- a/crates/mathml-renderer/src/lib.rs +++ b/crates/mathml-renderer/src/lib.rs @@ -7,14 +7,14 @@ //! ```rust //! use math_core_renderer_internal::ast::Node; //! use math_core_renderer_internal::symbol; -//! use math_core_renderer_internal::attribute::{MathSpacing, LetterAttr}; +//! use math_core_renderer_internal::attribute::{MathSpacing, LetterAttr, OpAttrs}; //! //! let ast = Node::Row { //! nodes: &[ //! &Node::Under { //! target: &Node::Operator { //! op: symbol::N_ARY_SUMMATION.as_op(), -//! attr: None, +//! attrs: OpAttrs::empty(), //! left: Some(MathSpacing::Zero), //! right: None, //! },