// Copyright 2017-2022 The MathJax Consortium // SPDX-License-Identifier: Apache-2.0 // Based on BaseMethods and BaseItems.SubsupItem (MathJax 3.2.2).
package tex import // Superscript and Subscript call GetNext once on entry and insert a space // after an immediate ASCII digit, before checking the base's occupied slots. // Keep this out of the pending argument loop: font/comment/macro continuation // must still use the ordinary number scanner. Prime uses the same GetNext // whitespace set; later token delivery uses the separate character-map spaces. func ( *parser) () { for .pos < len(.source) && isPrimeSpace(.peekRune()) { .consumeRune() } if .pos < len(.source) && .source[.pos] >= '0' && .source[.pos] <= '9' { .source = .source[:.pos+1] + " " + .source[.pos+1:] } } func ( *scriptAttachment) () error { if .marker == '_' { return texError("MissingOpenForSub", "Missing open brace for subscript") } return texError("MissingOpenForSup", "Missing open brace for superscript") } // A SubsupItem stays pending across commands that emit no stack item. In // particular, a macro expansion and SetFont do not supply an empty argument. // Keep this event boundary local to scripts; GetArgument callers are separate. func ( *parser) ( *scriptAttachment, string) ( *mml.Node, string, []*mml.Node, *derivativeAutoOpen, error) { // SubsupItem is the recipient, so an outer Braket cannot own this token. := .braketOwner .braketOwner = nil defer func() { .braketOwner = }() = defer func() { if == nil && != "" { applyScopedMathVariant(, ) } }() for { .skipMathTokenSpaces() if .pos >= len(.source) { return nil, , nil, nil, texError("MissingScript", "Missing superscript or subscript argument") } if .source[.pos] == '%' { .skipComment() continue } if .source[.pos] == '}' { .pos++ return nil, , nil, nil, texError("ExtraCloseMissingOpen", "Extra close brace or missing open brace") } if .source[.pos] == '&' { return nil, , nil, nil, .missingOpen() } if .source[.pos] == '{' { .pos++ , , := .parseRow('}', false) if != nil { return nil, , nil, nil, } if .environmentPopped { .environmentPopped = false if len() == 0 { continue } return [0], , [1:], nil, nil } return texAtom(row(, true), mml.TeXClassOrd), , nil, nil, nil } if isPrimeRune(.peekRune()) { .consumeRune() if , := .startPrime(.pendingBase()); != nil { return nil, , nil, nil, } return nil, , nil, nil, .missingOpen() } if := .source[.pos]; == '^' || == '_' { .pos++ .scriptInitialLookahead() := .pendingBase() , := .Property("movesupsub") if , := prepareScriptAttachment(, , limitsTruthy()); != nil { return nil, , nil, nil, } return nil, , nil, nil, .missingOpen() } if .source[.pos] == '\\' { := .pos .pos++ := .readControlSequence() if , := .state.macros[]; ! { switch { case "matrix", "array", "pmatrix", "cases", "eqalign", "eqalignno", "leqalignno", "displaylines": if == "displaylines" { if , := .state.pairedDelimiters[]; { break // The declared delimiter runs before Matrix. } } if := .startMatrixBody(); != nil { return nil, , nil, nil, } return nil, , nil, nil, .missingOpen() } if , := fontDeclarations[]; { .activeFont, = , continue } if , := styleDeclarations[]; { return nil, , nil, nil, .missingOpen() } if , := sizeDeclarations[]; { return nil, , nil, nil, .missingOpen() } if == "right" { if , := .readDelimiter(, false); != nil { return nil, , nil, nil, } return nil, , nil, nil, texError("MissingLeftExtraRight", "Missing \\left or extra \\right") } } .pos = } , := .parseOneTokenEventWithCasesBegin(.missingOpen) if != nil { return nil, , nil, nil, } if .environmentEnd != nil { if !.environmentEnd.spread { return nil, , nil, nil, .environmentEnd.extra() } // SpreadLines pops this actual SubsupItem, whose nodes contain // the unfinished base, rather than feeding it a closing item. .environmentPopped = true return .publishPendingScript(), , nil, nil, nil } if .namedFunction || .notItem || .nonscriptItem || .dotsItem != nil || .positionItem != nil || .cellItem != nil { return nil, , nil, nil, .missingOpen() } if len(.nodes) == 0 { // Empty PushAll leaves the SubsupItem pending. A following auto // open item is rejected here, before its tail can be parsed. if .afterNode.start() { return nil, , nil, nil, .missingOpen() } continue } // SubsupItem accepts the first final MML item. TexParser.PushAll // delivers any remaining items after that script has completed. return .nodes[0], , .nodes[1:], .afterNode, nil } }