package tex
import (
"fmt"
"github.com/d2lang/mathjax-go/internal/mml"
)
type commandResult struct {
nodes []*mml .Node
namedFunction bool
notItem bool
nonscriptItem bool
dotsItem *pendingDots
positionItem *positionItem
cellItem *cellItem
environmentEnd *environmentEndItem
casesLeft *environmentEndItem
afterNode *derivativeAutoOpen
}
func (p *parser ) commandEvent (name string ) (commandResult , error ) {
return p .commandEventWithCasesBegin (name , nil )
}
func (p *parser ) commandEventWithCasesBegin (name string , onBegin func () error ) (result commandResult , err error ) {
beforeBegin := p .commandCasesBegin
p .commandCasesBegin = onBegin
namedFunction , notItem := p .commandNamedFunction , p .commandNot
nonscript := p .commandNonscript
dotsItem , position := p .commandDots , p .commandPosition
cell := p .commandCell
environmentEnd := p .commandEnvironmentEnd
casesLeft := p .commandCasesLeft
p .commandNamedFunction , p .commandNot = false , false
p .commandNonscript = false
p .commandDots , p .commandPosition = nil , nil
p .commandCell = nil
p .commandEnvironmentEnd = nil
p .commandCasesLeft = nil
defer func () {
p .commandCasesBegin = beforeBegin
p .commandNamedFunction , p .commandNot = namedFunction , notItem
p .commandNonscript = nonscript
p .commandDots , p .commandPosition = dotsItem , position
p .commandCell = cell
p .commandEnvironmentEnd = environmentEnd
p .commandCasesLeft = casesLeft
}()
result .nodes , err = p .commandNodes (name , &result .afterNode )
result .namedFunction , result .notItem = p .commandNamedFunction , p .commandNot
result .nonscriptItem = p .commandNonscript
result .dotsItem , result .positionItem = p .commandDots , p .commandPosition
result .cellItem = p .commandCell
result .environmentEnd = p .commandEnvironmentEnd
result .casesLeft = p .commandCasesLeft
return result , err
}
func (p *parser ) command (name string ) ([]*mml .Node , error ) {
result , err := p .commandEvent (name )
if err != nil {
return nil , err
}
if result .casesLeft != nil {
return nil , fmt .Errorf ("Cases left block requires a pending node" )
}
if result .environmentEnd != nil {
return nil , result .environmentEnd .extra ()
}
if result .cellItem != nil {
if !result .cellItem .linebreak {
return nil , result .cellItem .misplaced ()
}
result .nodes = append (result .nodes , result .cellItem .spaces ()...)
}
if result .notItem {
result .nodes = append (result .nodes , notFallback ())
}
if result .dotsItem != nil {
result .nodes = append (result .nodes , result .dotsItem .finish ()...)
}
tail , err := result .afterNode .complete (p )
return append (result .nodes , tail ...), err
}
type derivativeAutoOpen struct {
open byte
closer byte
application *physicsApplicationArgument
quantity *physicsQuantityArgument
big string
bigOpen, bigClose *mml .Node
fencesParsed bool
ignore bool
openCount int
closed bool
openingConsumed bool
smash bool
right string
rightNode *mml .Node
rightParsed bool
trailing []finalItem
}
func (a *derivativeAutoOpen ) openingFence () byte {
if a .open == 0 {
return '('
}
return a .open
}
func (a *derivativeAutoOpen ) closingFence () byte {
if a .closer == 0 {
return ')'
}
return a .closer
}
func (a *derivativeAutoOpen ) start (p *parser ) bool {
if a == nil {
return false
}
if a .openingConsumed {
return true
}
for p .pos < len (p .source ) && isPrimeSpace (p .peekRune ()) {
p .consumeRune ()
}
if p .pos == len (p .source ) || p .source [p .pos ] != a .openingFence () {
return false
}
p .pos ++
return true
}
func (a *derivativeAutoOpen ) parseRight (p *parser ) error {
if a == nil {
return nil
}
if a .right != "" && !a .rightParsed {
right , err := p .parseChild (a .right )
if err != nil {
return err
}
a .rightNode , a .rightParsed = right , true
}
if a .big != "" && !a .fencesParsed {
left , err := p .parseChild ("\\" + a .big + "l" + string (a .openingFence ()))
if err != nil {
return err
}
right , err := p .parseChild ("\\" + a .big + "r" + string (a .closingFence ()))
if err != nil {
return err
}
a .bigOpen , a .bigClose , a .fencesParsed = left , right , true
}
return nil
}
func (a *derivativeAutoOpen ) complete (p *parser ) ([]*mml .Node , error ) {
nodes , err := a .completeAfter (p , nil )
if err != nil {
return nil , err
}
for _ , item := range a .takeTrailing () {
nodes = append (nodes , item .node )
}
return nodes , nil
}
func (a *derivativeAutoOpen ) takeTrailing () []finalItem {
if a == nil {
return nil
}
items := a .trailing
a .trailing = nil
return items
}
func (a *derivativeAutoOpen ) completeAfter (p *parser , before func ()) ([]*mml .Node , error ) {
if a != nil && a .quantity != nil {
return a .quantity .parse (p )
}
if a != nil && a .application != nil {
opened , err := a .application .prepare (p , a )
if err != nil || !opened {
return nil , err
}
}
if !a .start (p ) {
return nil , nil
}
if before != nil {
before ()
}
content , _ , err := p .parseRowWithAutoOpen (0 , false , false , a )
if err != nil {
return nil , err
}
popped := p .environmentPopped
p .environmentPopped = false
if a .ignore && !popped {
return nil , nil
}
if err := a .parseRight (p ); err != nil {
return nil , err
}
if a .smash {
padded := node ("mpadded" , row (content , true ))
padded .Attributes .Set ("height" , 0 )
padded .Attributes .Set ("depth" , 0 )
content = []*mml .Node {texAtom (padded , mml .TeXClassOrd )}
}
if a .rightNode != nil {
content = append (content , a .rightNode )
}
left , right := a .bigOpen , a .bigClose
if a .big == "" {
left = p .autoOpenFence (string (a .openingFence ()), mml .TeXClassOpen )
right = p .autoOpenFence (string (a .closingFence ()), mml .TeXClassClose )
}
children := append ([]*mml .Node {left }, content ...)
children = append (children , right )
result := forcedRow (children , false )
result .TeXClass = mml .TeXClassInner
if popped {
if err := mathtoolsSpreadPop (result , nil ); err != nil {
return nil , err
}
}
return []*mml .Node {result }, nil
}
func autoOpenFence(text string , class mml .TeXClass ) *mml .Node {
n := node ("mo" , mml .NewText (text ))
n .Attributes .Set ("fence" , true )
n .Attributes .Set ("stretchy" , true )
n .Attributes .Set ("symmetric" , true )
n .TeXClass = class
n .SetProperty ("texClass" , class )
return n
}
func (a *derivativeAutoOpen ) accept (item finalItem ) bool {
if item .node .Kind == "mo" && textContent (item .node ) == string (a .openingFence ()) {
a .openCount ++
}
return item .autoclose != 0 && item .autoclose == a .closingFence () && a .close ()
}
func (a *derivativeAutoOpen ) close () bool {
before := a .openCount
a .openCount --
if before == 0 {
a .closed = true
}
return a .closed
}
func (a *derivativeAutoOpen ) stopError () error {
return texError ("ExtraOrMissingDelims" , "Extra open or missing close delimiter" )
}
The pages are generated with Golds v0.8.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .