// Copyright (c) 2009-2022 The MathJax Consortium // SPDX-License-Identifier: Apache-2.0 // Sources: BaseMethods.Array/AlignedArray, BaseItems.ArrayItem/BeginItem, // MathtoolsMethods.MtMatrix/MtSmallMatrix/SpreadLines, and Stack.Pop.
package tex import ( ) // Array and its enclosing Begin are separate stack items. EndTable removes // Array and replays the same End to Begin; a direct Pop removes only Array // and leaves Begin executing with the pending cell's raw nodes. // A live standalone Matrix uses the same Pop result carrier with begin=nil; // its pending nodes return directly to the caller without a continued Begin. // Cases uses this same ArrayItem result carrier for EqnArray while retaining // its distinct shared CasesBegin and two-End decoration sequence. type ordinaryArrayItem struct { begin *environmentFrame end *environmentEndItem popped bool nodes []*mml.Node } // These are precisely the pinned Array/AlignedArray registrations and the // MtMatrix/MtSmallMatrix wrappers that delegate to Array. Other table owners // retain their own scanners, EndTable callbacks, and closing-item contracts. func isOrdinaryArrayEnvironment( string) bool { switch { case "array", "matrix", "pmatrix", "bmatrix", "Bmatrix", "vmatrix", "Vmatrix", "cases", "subarray", "crampedsubarray", "dcases", "rcases", "drcases", "smallmatrix", "matrix*", "pmatrix*", "bmatrix*", "Bmatrix*", "vmatrix*", "Vmatrix*", "smallmatrix*", "psmallmatrix", "psmallmatrix*", "bsmallmatrix", "bsmallmatrix*", "Bsmallmatrix", "Bsmallmatrix*", "vsmallmatrix", "vsmallmatrix*", "Vsmallmatrix", "Vsmallmatrix*": return true } return false } // Begin copies the caller's lexical environment before Array clears its own. // Keep the full environment for resuming Begin after Array is directly popped. type ordinaryBeginEnvironment struct { multiLetterFont, activeFont, activeColor string identifierPattern identifierPattern operatorLetters, noAutoOP bool fontExplicitEmpty, inRoot bool vectorFont string vectorStar bool } func ( *parser) () ordinaryBeginEnvironment { return ordinaryBeginEnvironment{ multiLetterFont: .multiLetterFont, activeFont: .activeFont, activeColor: .activeColor, identifierPattern: .identifierPattern, operatorLetters: .operatorLetters, noAutoOP: .noAutoOP, fontExplicitEmpty: .fontExplicitEmpty, inRoot: .inRoot, vectorFont: .vectorFont, vectorStar: .vectorStar, } } func ( ordinaryBeginEnvironment) ( *parser) { .multiLetterFont, .activeFont, .activeColor = .multiLetterFont, .activeFont, .activeColor .identifierPattern, .operatorLetters, .noAutoOP = .identifierPattern, .operatorLetters, .noAutoOP .fontExplicitEmpty, .inRoot = .fontExplicitEmpty, .inRoot .vectorFont, .vectorStar = .vectorFont, .vectorStar } func ( *parser) ( string, *environmentFrame) ([]*mml.Node, error) { := .ordinaryBeginEnvironment() defer .restore() // Retain the existing Array setup scope; individual cells additionally // clear font and identifier state in matrixCellParser. .inRoot, .activeColor = false, "" // Ordinary Array owns its EOF check independently of legacy streaming // frames, which can be inherited by captured table child parsers. .ordinaryArray = true := &ordinaryArrayItem{begin: } := "c" := "" var error if == "array" { , = .readArrayAlignment() if != nil { return nil, } } if == "array" || == "subarray" || == "crampedsubarray" { := "begin{" + + "}" if == "array" { = "begin" } , _, = .readArgument(, false) if != nil { return nil, } } := strings.Contains(, "smallmatrix") if strings.HasSuffix(, "*") { := "c" if { = .mathtoolsOption("smallmatrix-align") } , _, = .readBrackets(&) if != nil { return nil, } } if || strings.HasSuffix(, "*") { , = .completeArrayAlignment() if != nil { return nil, } } , := "T", "4pt" := == "subarray" || == "crampedsubarray" := == "cases" || == "dcases" || == "rcases" || == "drcases" if || { = "S" } if || { = ".2em" } if { = "0.1em" } if && strings.HasPrefix(, "d") { = "D" } , := .parseTableWithOrdinaryArray("", , true, , ) if != nil { return nil, } if .popped { .restore() return .continueOrdinaryBegin(, .nodes) } // Only a real EndTable result receives table metadata, rules and fences. // Keep each registration's existing setup while replacing raw capture. if { resetTableAttributes(, "columnalign", "center", "columnspacing", ".333em", "rowspacing", tableRowSpacing(), "displaystyle", false, ) .SetProperty("useHeight", false) .SetProperty("scriptlevel", 1) } else if { resetTableAttributes(, "columnspacing", "0em", "rowspacing", tableRowSpacing(), ) .SetProperty("useHeight", false) .SetProperty("scriptlevel", 1) .Attributes.Set("displaystyle", false) if == "crampedsubarray" { .Attributes.Set("data-cramped", true) } } else if strings.Contains(, "matrix") { .Attributes.Set("columnalign", "center") } if { resetTableAttributes(, "columnalign", "left left", "columnspacing", "1em", "rowspacing", tableRowSpacing(), ) .Attributes.Set("displaystyle", == "D") , := "{", "" if strings.Contains(, "rcases") { , = "", "}" } = .leftRightFenced(, finishArrayRules(), , true) } else { if == "array" { setArrayAlign(, ) } = applyColumnSpec(, ) , := matrixDelimiters() if != "" || != "" { = .leftRightFenced(, , , true) } if { = finishArrayRules() } } if .end == nil { // Stop first reduces pending items, then Array, and finally Begin. return nil, .missing() } if := .closeEnvironment(, .end); != nil { return nil, } return []*mml.Node{}, nil } func ( *parser) ( *environmentFrame, []*mml.Node) ([]*mml.Node, error) { , := .environmentRow, .ordinaryArray , , , := .arrayCell, .matrixClose, .cdArrayEntry, .braketOwner .environmentRow, .ordinaryArray = , nil .arrayCell, .matrixClose, .cdArrayEntry, .braketOwner = nil, false, false, nil defer func() { .environmentRow, .ordinaryArray = , .arrayCell, .matrixClose, .cdArrayEntry, .braketOwner = , , , }() , , := .parseRowContinuation(0, false, false, nil, "", ) if != nil { return nil, } if !.closed { return nil, .missing() } return , nil }