// Copyright 2017-2022 The MathJax Consortium
// SPDX-License-Identifier: Apache-2.0
// This file is a Go translation and modification of MathJax 3.2.2.

// Package mml defines MathJax's internal MathML tree representation.
package mml import ( ) // TeXClass is a TeX atom class used by MathJax's spacing rules. type TeXClass int8 const ( TeXClassNone TeXClass = -1 TeXClassOrd TeXClass = 0 TeXClassOp TeXClass = 1 TeXClassBin TeXClass = 2 TeXClassRel TeXClass = 3 TeXClassOpen TeXClass = 4 TeXClassClose TeXClass = 5 TeXClassPunct TeXClass = 6 TeXClassInner TeXClass = 7 TeXClassVCenter TeXClass = 8 ) var TeXClassNames = [...]string{ "ORD", "OP", "BIN", "REL", "OPEN", "CLOSE", "PUNCT", "INNER", "VCENTER", } var texSpaceLength = [...]string{"", "thinmathspace", "mediummathspace", "thickmathspace"} var texSpace = [8][8]int8{ {0, -1, 2, 3, 0, 0, 0, 1}, {-1, -1, 0, 3, 0, 0, 0, 1}, {2, 2, 0, 0, 2, 0, 0, 2}, {3, 3, 0, 0, 3, 0, 0, 3}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, -1, 2, 3, 0, 0, 0, 1}, {1, 1, 0, 1, 1, 1, 1, 1}, {1, -1, 2, 3, 1, 0, 1, 1}, } // TeXSpacing returns MathJax's named space between two TeX atom classes. // script suppresses entries marked as display/text-style-only. Negative table // entries are the ones retained in scripts in MathJax's TEXSPACE encoding. func (, TeXClass, bool) string { if < TeXClassOrd || > TeXClassInner || < TeXClassOrd || > TeXClassInner { return "" } := texSpace[][] if && >= 0 { return "" } if < 0 { = - } return texSpaceLength[] } // Flags contains node-kind behavior that MathJax expresses through its MML // node subclasses. type Flags struct { Token bool Embellished bool Spacelike bool LinebreakContainer bool HasNewline bool Inferred bool NotParent bool Arity int CoreIndex int } // FencedNodes are MmlMfenced's non-authored operator nodes. A non-nil value // distinguishes an initialized fence with no operators from one whose MML // inheritance pass has not run. These nodes do not participate in Walk/Find. type FencedNodes struct { Open, Close *Node Separators []*Node } // Node is one node in MathJax's internal MathML tree. Kind-specific state that // is not an attribute lives in Properties. type Node struct { Kind string Parent *Node Children []*Node Attributes *Attributes Properties *ordered.Map[Property] Text string Flags Flags TeXClass TeXClass PrevClass TeXClass PrevLevel int Fenced *FencedNodes // OperatorLspace/OperatorRspace are MmlMo's dictionary-derived defaults, // distinct from explicit/inherited lspace/rspace attributes (in ems). OperatorLspace, OperatorRspace float64 } // NewNode creates a node with empty attribute and property maps. func ( string, , *ordered.Map[Property], ...*Node) *Node { := &Node{ Kind: , Attributes: NewAttributes(, ), Properties: ordered.New[Property](), TeXClass: TeXClassNone, PrevClass: TeXClassNone, } if == "mo" { .OperatorLspace, .OperatorRspace = 5.0/18, 5.0/18 } .SetChildren() return } // NewText creates an internal text node. func ( string) *Node { := NewNode("text", nil, nil) .Text = return } // IsKind reports whether n has kind. func ( *Node) ( string) bool { return != nil && .Kind == } // SetProperty sets internal node state. func ( *Node) ( string, Property) { .Properties.Set(, ) } // Property returns internal node state. func ( *Node) ( string) (Property, bool) { return .Properties.Get() } // RemoveProperty removes internal node state. func ( *Node) ( string) bool { return .Properties.Delete() } // SetChildren replaces n's children and maintains parent pointers. func ( *Node) ( []*Node) { for , := range .Children { if != nil && .Parent == { .Parent = nil } } .Children = append(.Children[:0], ...) for , := range .Children { if != nil { .Parent = } } } // AppendChild appends child and returns it. func ( *Node) ( *Node) *Node { if != nil { .Parent = } .Children = append(.Children, ) return } // ChildIndex returns child's index, or -1 if child is absent. func ( *Node) ( *Node) int { for , := range .Children { if == { return } } return -1 } // ReplaceChild replaces oldChild and returns an error when it is absent. func ( *Node) (, *Node) error { := .ChildIndex() if < 0 { return fmt.Errorf("mml: child is not present in %s", .Kind) } if != nil && .Parent == { .Parent = nil } if != nil { .Parent = } .Children[] = return nil } // RemoveChild removes child and returns an error when it is absent. func ( *Node) ( *Node) error { := .ChildIndex() if < 0 { return fmt.Errorf("mml: child is not present in %s", .Kind) } if != nil && .Parent == { .Parent = nil } copy(.Children[:], .Children[+1:]) .Children[len(.Children)-1] = nil .Children = .Children[:len(.Children)-1] return nil } // ParentNode returns the semantic parent, skipping inferred/not-parent nodes. func ( *Node) () *Node { := .Parent for != nil && (.Flags.Inferred || .Flags.NotParent) { = .Parent } return } // Core returns the child containing an embellished operator, or n. func ( *Node) () *Node { if == nil || !.Flags.Embellished || .Flags.CoreIndex < 0 || .Flags.CoreIndex >= len(.Children) { return } return .Children[.Flags.CoreIndex] } // CoreMO follows embellished children to their core mo node. func ( *Node) () *Node { := for != nil && .Flags.Embellished { := .Core() if == { break } = } return } // Walk visits n and its descendants in pre-order. func ( *Node) ( func(*Node) bool) { if == nil || !() { return } for , := range .Children { .() } } // Find returns all descendants, including n, whose kind matches. func ( *Node) ( string) []*Node { var []*Node .Walk(func( *Node) bool { if .Kind == { = append(, ) } return true }) return } // Clone deep-copies n without retaining a parent pointer. func ( *Node) () *Node { if == nil { return nil } := &Node{ Kind: .Kind, Attributes: .Attributes.Clone(), Properties: .Properties.Clone(), Text: .Text, Flags: .Flags, TeXClass: .TeXClass, PrevClass: .PrevClass, PrevLevel: .PrevLevel, OperatorLspace: .OperatorLspace, OperatorRspace: .OperatorRspace, } := make([]*Node, len(.Children)) for , := range .Children { [] = .() } .SetChildren() if .Fenced != nil { .Fenced = &FencedNodes{Open: .Fenced.Open.(), Close: .Fenced.Close.()} for , := range .Fenced.Separators { .Fenced.Separators = append(.Fenced.Separators, .()) } for , := range append([]*Node{.Fenced.Open, .Fenced.Close}, .Fenced.Separators...) { if != nil { .Parent = } } } return }