package tex
import (
"math"
"strconv"
"strings"
"github.com/d2lang/mathjax-go/internal/jscompat"
"github.com/d2lang/mathjax-go/internal/mml"
)
func (p *parser ) physicsCommand (name string , after **derivativeAutoOpen ) (nodes []*mml .Node , handled bool , err error ) {
if quantity , ok := physicsQuantities [name ]; ok {
nodes , err = p .physicsQuantity (name , quantity , after )
return nodes , true , err
}
if application , ok := physicsApplicationOperators [name ]; ok {
nodes , err = p .physicsOperatorApplication (name , application , after )
return nodes , true , err
}
switch name {
case "sin" , "sinh" , "arcsin" , "asin" , "cos" , "cosh" , "arccos" , "acos" , "tan" , "tanh" , "arctan" , "atan" ,
"csc" , "csch" , "arccsc" , "acsc" , "sec" , "sech" , "arcsec" , "asec" , "cot" , "coth" , "arccot" , "acot" ,
"exp" , "log" , "ln" , "det" , "Pr" , "tr" , "trace" , "Tr" , "Trace" , "erf" :
nodes , err = p .physicsExpression (name )
case "evaluated" , "eval" :
nodes , err = p .physicsEval (name , after )
case "outerproduct" , "dyad" , "ketbra" , "op" :
nodes , err = p .physicsKetBra (name )
case "matrixelement" , "matrixel" , "mel" :
nodes , err = p .physicsMatrixElement (name )
case "identitymatrix" , "imat" , "xmatrix" , "xmat" , "zeromatrix" , "zmat" , "paulimatrix" , "pmat" , "diagonalmatrix" , "dmat" , "antidiagonalmatrix" , "admat" :
var expansion string
expansion , err = p .physicsMatrixExpansion (name )
if err == nil && expansion != "" {
p .source = p .source [:p .pos ] + expansion + p .source [p .pos :]
}
default :
return nil , false , nil
}
return nodes , true , err
}
func (p *parser ) physicsEnvironment (name string ) (nodes []*mml .Node , handled bool , err error ) {
if !physicsMatrixEnvironment (name ) || !physicsBodyHasMatrixGenerator (p .source [p .pos :]) {
return nil , false , nil
}
columnSpec := "c"
if strings .HasSuffix (name , "*" ) {
defaultAlignment := "c"
columnSpec , _, err = p .readBrackets (&defaultAlignment )
if err != nil {
return nil , true , err
}
columnSpec , err = p .completeArrayAlignment (columnSpec )
if err != nil {
return nil , true , err
}
}
body , err := p .captureEnvironment (name )
if err != nil {
return nil , true , err
}
body , err = p .physicsRewriteMatrixBody (body )
if err != nil {
return nil , true , err
}
style := "T"
if strings .Contains (name , "small" ) {
style = "S"
}
table , err := p .parseTable (body , style )
if err != nil {
return nil , true , err
}
table = applyColumnSpec (table , columnSpec )
open , close := matrixDelimiters (name )
if open != "" || close != "" {
table = p .leftRightFenced (open , table , close , true )
}
return []*mml .Node {table }, true , nil
}
func (p *parser ) physicsExpression (name string ) ([]*mml .Node , error ) {
id := name
if name == "trace" {
id = "tr"
} else if name == "Trace" {
id = "Tr"
}
allowExponent := true
switch name {
case "exp" , "det" , "Pr" , "tr" , "trace" , "Tr" , "Trace" , "erf" :
allowExponent = false
}
exponent := ""
if allowExponent {
var err error
exponent , _, err = p .readBrackets (nil )
if err != nil {
return nil , err
}
}
function := token ("mi" , id )
function .TeXClass = mml .TeXClassOp
function .SetProperty ("texClass" , mml .TeXClassOp )
var base *mml .Node = function
if exponent != "" {
sup , err := p .parseArgumentString (exponent )
if err != nil {
return nil , err
}
base = node ("msup" , function , sup )
}
p .skipNextSpaces ()
if p .pos >= len (p .source ) || p .source [p .pos ] != '(' {
p .commandNamedFunction = true
return []*mml .Node {base }, nil
}
p .pos ++
raw , err := p .readUpToByte (')' )
if err != nil {
return nil , err
}
content , err := p .parseContinuationString (raw )
if err != nil {
return nil , err
}
return []*mml .Node {base , p .operator ("\u2061" , mml .TeXClassNone , nil ), p .fenced ("(" , content , ")" , true )}, nil
}
func (p *parser ) physicsEval (name string , after **derivativeAutoOpen ) ([]*mml .Node , error ) {
star := p .readStar ()
for p .pos < len (p .source ) && internalTextSpace (p .peekRune ()) {
p .consumeRune ()
}
if p .pos >= len (p .source ) {
return nil , texError ("MissingArgFor" , "Missing argument for \\%s" , name )
}
switch p .source [p .pos ] {
case '{' :
raw , _ , err := p .readArgument (name , false )
if err != nil {
return nil , err
}
if star {
raw = "\\smash{" + raw + "}"
}
expansion := "\\left. " + raw + " \\vphantom{\\int}\\right|"
p .source = p .source [:p .pos ] + expansion + p .source [p .pos :]
return nil , nil
case '(' , '[' :
open := p .source [p .pos ]
p .pos ++
*after = &derivativeAutoOpen {open : open , closer : '|' , openingConsumed : true ,
smash : star , right : "\\vphantom{\\int}" }
return nil , nil
default :
return nil , texError ("MissingArgFor" , "Missing argument for \\%s" , name )
}
}
func (p *parser ) physicsKetBra (name string ) ([]*mml .Node , error ) {
star := p .readStar ()
ket , _ , err := p .readArgument (name , false )
if err != nil {
return nil , err
}
for p .pos < len (p .source ) && internalTextSpace (p .peekRune ()) {
p .consumeRune ()
}
bra := ket
if p .pos < len (p .source ) && p .source [p .pos ] == '{' {
bra , _, err = p .readArgument (name , true )
if err != nil {
return nil , err
}
}
macro := "\\left\\vert{" + ket + "}\\middle\\rangle\\!\\middle\\langle{" + bra + "}\\right\\vert"
if star {
macro = "\\vert{" + ket + "}\\rangle\\!\\langle{" + bra + "}\\vert"
}
parsed , err := p .parseChild (macro )
if err != nil {
return nil , err
}
return unwrapInferred (parsed ), nil
}
func (p *parser ) physicsMatrixElement (name string ) ([]*mml .Node , error ) {
star1 := p .readStar ()
star2 := star1 && p .readStar ()
args := make ([]string , 3 )
for i := range args {
var err error
args [i ], _, err = p .readArgument (name , false )
if err != nil {
return nil , err
}
}
macro := "\\left\\langle{" + args [0 ] + "}\\right\\vert{" + args [1 ] + "}\\left\\vert{" + args [2 ] + "}\\right\\rangle"
if star1 && star2 {
macro = "\\left\\langle{" + args [0 ] + "}\\middle\\vert{" + args [1 ] + "}\\middle\\vert{" + args [2 ] + "}\\right\\rangle"
} else if star1 {
macro = "\\langle{" + args [0 ] + "}\\vert{" + args [1 ] + "}\\vert{" + args [2 ] + "}\\rangle"
}
parsed , err := p .parseChild (macro )
if err != nil {
return nil , err
}
return unwrapInferred (parsed ), nil
}
func physicsMatrixEnvironment(name string ) bool {
name = strings .TrimSuffix (name , "*" )
switch name {
case "matrix" , "smallmatrix" , "pmatrix" , "bmatrix" , "Bmatrix" , "vmatrix" , "Vmatrix" ,
"psmallmatrix" , "bsmallmatrix" , "Bsmallmatrix" , "vsmallmatrix" , "Vsmallmatrix" :
return true
}
return false
}
func physicsBodyHasMatrixGenerator(source string ) bool {
for _ , name := range []string {"imat" , "identitymatrix" , "xmat" , "xmatrix" , "zmat" , "zeromatrix" , "pmat" , "paulimatrix" , "dmat" , "diagonalmatrix" , "admat" , "antidiagonalmatrix" } {
if strings .Contains (source , "\\" +name ) {
return true
}
}
return false
}
func (p *parser ) physicsRewriteMatrixBody (body string ) (string , error ) {
var rewritten strings .Builder
start := 0
for index := 0 ; index < len (body ); {
if body [index ] != '\\' {
index ++
continue
}
scanner := &parser {source : body , pos : index + 1 , state : p .state , display : p .display }
name := scanner .readControlSequence ()
if !physicsMatrixGenerator (name ) {
index = scanner .pos
continue
}
expansion , err := scanner .physicsMatrixExpansion (name )
if err != nil {
return "" , err
}
rewritten .WriteString (body [start :index ])
rewritten .WriteString (expansion )
start = scanner .pos
index = scanner .pos
}
rewritten .WriteString (body [start :])
return rewritten .String (), nil
}
func physicsMatrixGenerator(name string ) bool {
switch name {
case "identitymatrix" , "imat" , "xmatrix" , "xmat" , "zeromatrix" , "zmat" , "paulimatrix" , "pmat" , "diagonalmatrix" , "dmat" , "antidiagonalmatrix" , "admat" :
return true
}
return false
}
func (p *parser ) physicsMatrixExpansion (name string ) (string , error ) {
switch name {
case "identitymatrix" , "imat" :
raw , _ , err := p .readArgument (name , false )
if err != nil {
return "" , err
}
number , ok := physicsParseInt (raw )
if !ok {
return "" , texError ("InvalidNumber" , "Invalid number" )
}
if number <= 1 {
return "1" , nil
}
size , ok := physicsMatrixDimension (number )
if !ok {
return "" , texError ("InvalidNumber" , "Invalid number" )
}
rows := make ([]string , size )
for i := 0 ; i < size ; i ++ {
cells := make ([]string , size )
for j := range cells {
cells [j ] = "0"
}
cells [i ] = "1"
rows [i ] = strings .Join (cells , " & " )
}
return strings .Join (rows , "\\\\ " ), nil
case "xmatrix" , "xmat" , "zeromatrix" , "zmat" :
star := p .readStar ()
value := "0"
var err error
if name == "xmatrix" || name == "xmat" {
value , _, err = p .readArgument (name , false )
if err != nil {
return "" , err
}
}
rawRows , _ , err := p .readArgument (name , false )
if err != nil {
return "" , err
}
rawColumns , _ , err := p .readArgument (name , false )
if err != nil {
return "" , err
}
rowNumber , rowOK := physicsParseInt (rawRows )
columnNumber , columnOK := physicsParseInt (rawColumns )
if !rowOK || !columnOK || jscompat .NumberString (rowNumber ) != rawRows || jscompat .NumberString (columnNumber ) != rawColumns {
return "" , texError ("InvalidNumber" , "Invalid number" )
}
rows , rowOK := physicsMatrixDimension (rowNumber )
columns , columnOK := physicsMatrixDimension (columnNumber )
if !rowOK || !columnOK {
return "" , texError ("InvalidNumber" , "Invalid number" )
}
matrixRows := make ([]string , rows )
for i := 1 ; i <= rows ; i ++ {
cells := make ([]string , columns )
for j := 1 ; j <= columns ; j ++ {
cell := value
if star && (rows > 1 || columns > 1 ) {
if rows == 1 {
cell += "_{" + strconv .Itoa (j ) + "}"
} else if columns == 1 {
cell += "_{" + strconv .Itoa (i ) + "}"
} else {
cell += "_{{" + strconv .Itoa (i ) + "}{" + strconv .Itoa (j ) + "}}"
}
}
cells [j -1 ] = cell
}
matrixRows [i -1 ] = strings .Join (cells , " & " )
}
return strings .Join (matrixRows , "\\\\ " ), nil
case "paulimatrix" , "pmat" :
raw , _ , err := p .readArgument (name , false )
if err != nil {
return "" , err
}
if raw == "" {
return "" , nil
}
matrix := raw [1 :]
switch raw [0 ] {
case '0' :
matrix += " 1 & 0\\\\ 0 & 1"
case '1' , 'x' :
matrix += " 0 & 1\\\\ 1 & 0"
case '2' , 'y' :
matrix += " 0 & -i\\\\ i & 0"
case '3' , 'z' :
matrix += " 1 & 0\\\\ 0 & -1"
}
return matrix , nil
case "diagonalmatrix" , "dmat" , "antidiagonalmatrix" , "admat" :
p .skipNextSpaces ()
if p .pos >= len (p .source ) || p .source [p .pos ] != '{' {
return "" , nil
}
raw , _ , err := p .readArgument (name , false )
if err != nil {
return "" , err
}
elements := splitTopLevel (raw , ',' )
anti := name == "antidiagonalmatrix" || name == "admat"
rows := make ([]string , len (elements ))
for i , element := range elements {
ampersands := i
if anti {
ampersands = len (elements ) - i - 1
}
rows [i ] = strings .Repeat ("&" , ampersands ) + "\\mqty{" + element + "}"
}
return strings .Join (rows , "\\\\ " ), nil
}
return "" , nil
}
func physicsParseInt(raw string ) (float64 , bool ) {
raw = strings .TrimLeftFunc (raw , internalTextSpace )
if raw == "" {
return 0 , false
}
end := 0
if raw [0 ] == '+' || raw [0 ] == '-' {
end ++
}
startDigits := end
for end < len (raw ) && raw [end ] >= '0' && raw [end ] <= '9' {
end ++
}
if end == startDigits {
return 0 , false
}
value , err := strconv .ParseFloat (raw [:end ], 64 )
return value , err == nil || math .IsInf (value , 0 )
}
func physicsMatrixDimension(number float64 ) (int , bool ) {
if number <= 1 {
return 1 , true
}
if number >= math .Ldexp (1 , strconv .IntSize -1 ) {
return 0 , false
}
return int (number ), true
}
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 .