Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4098277
TexArray.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
15 KB
Referenced Files
None
Subscribers
None
TexArray.php
View Options
<?php
declare
(
strict_types
=
1
);
namespace
MediaWiki\Extension\Math\WikiTexVC\Nodes
;
use
Generator
;
use
InvalidArgumentException
;
use
MediaWiki\Extension\Math\WikiTexVC\MMLmappings\Util\MMLParsingUtil
;
use
MediaWiki\Extension\Math\WikiTexVC\MMLmappings\Util\MMLutil
;
use
MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmo
;
use
MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmrow
;
use
MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmstyle
;
use
MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmsup
;
use
MediaWiki\Extension\Math\WikiTexVC\TexUtil
;
/**
*
*/
class
TexArray
extends
TexNode
implements
\ArrayAccess
,
\IteratorAggregate
{
protected
bool
$curly
=
false
;
private
?
LengthSpec
$rowSpecs
=
null
;
/**
* @param TexNode|string ...$args
* @return self
*/
public
static
function
newCurly
(
...
$args
)
{
$node
=
new
self
(
...
$args
);
$node
->
curly
=
true
;
return
$node
;
}
/** @inheritDoc */
public
function
__construct
(
...
$args
)
{
$nargs
=
[];
foreach
(
$args
as
&
$arg
)
{
if
(
$arg
!==
null
)
{
array_push
(
$nargs
,
$arg
);
}
}
self
::
checkInput
(
$nargs
);
parent
::
__construct
(
...
$nargs
);
}
public
function
checkForStyleArgs
(
TexNode
$node
):
?
array
{
if
(
$node
instanceof
Literal
)
{
$name
=
trim
(
$node
->
getArg
()
);
switch
(
$name
)
{
case
"
\\
displaystyle"
:
return
[
"displaystyle"
=>
"true"
,
"scriptlevel"
=>
"0"
];
case
"
\\
scriptstyle"
:
return
[
"displaystyle"
=>
"false"
,
"scriptlevel"
=>
"1"
];
case
"
\\
scriptscriptstyle"
:
return
[
"displaystyle"
=>
"false"
,
"scriptlevel"
=>
"2"
];
case
"
\\
textstyle"
:
return
[
"displaystyle"
=>
"false"
,
"scriptlevel"
=>
"0"
];
}
}
return
null
;
}
/**
* Checks if an TexNode of Literal contains color information (color, pagecolor)
* and returns info how to continue with the parsing.
* @param TexNode $node node to check if it contains color info
* @return array index 0: (bool) was color element found, index 1: (string) specified color
*/
public
function
checkForColor
(
TexNode
$node
)
{
if
(
$node
instanceof
Literal
)
{
$name
=
trim
(
$node
->
getArg
()
);
if
(
str_contains
(
$name
,
"
\\
color"
)
)
{
$foundOperatorContent
=
MMLutil
::
initalParseLiteralExpression
(
$node
->
getArg
()
);
if
(
!
$foundOperatorContent
)
{
// discarding color elements which not specify color
return
[
true
,
null
];
}
else
{
return
[
true
,
$foundOperatorContent
[
2
][
0
]
];
}
}
elseif
(
str_contains
(
$name
,
"
\\
pagecolor"
)
)
{
return
[
true
,
null
];
}
}
return
[
false
,
null
];
}
public
function
checkForColorDefinition
(
TexNode
$node
):
?
array
{
if
(
$node
instanceof
Literal
)
{
$name
=
trim
(
$node
->
getArg
()
);
if
(
str_contains
(
$name
,
"
\\
definecolor"
)
)
{
return
MMLParsingUtil
::
parseDefineColorExpression
(
$node
->
getArg
()
);
}
}
return
null
;
}
/**
* Checks two sequential nodes in TexArray if they contain information on sideset expressions.
* @param TexNode $currentNode first node in array to check (for sideset expression)
* @param TexNode|null $nextNode second node in array to check (for succeeding operator)
* @return TexNode|null the succeeding operator for further Parsing or null if sideset not found or invalid
*/
public
function
checkForSideset
(
TexNode
$currentNode
,
?
TexNode
$nextNode
):
?
TexNode
{
if
(
!(
$currentNode
instanceof
Fun2nb
&&
$currentNode
->
getFname
()
==
"
\\
sideset"
)
)
{
return
null
;
}
if
(
$nextNode
instanceof
Literal
||
$nextNode
instanceof
DQ
||
$nextNode
instanceof
UQ
||
$nextNode
instanceof
FQ
)
{
return
$nextNode
;
}
return
null
;
}
public
function
checkForLimits
(
TexNode
$currentNode
,
?
TexNode
$nextNode
):
array
{
// Preceding 'lim' in example: "\\lim_{x \\to 2}"
if
(
(
$currentNode
instanceof
DQ
||
$currentNode
instanceof
FQ
)
&&
$currentNode
->
containsFunc
(
"
\\
lim"
)
)
{
if
(
$currentNode
->
getBase
()
instanceof
TexArray
)
{
return
[
$currentNode
->
getBase
()->
getArgs
()[
0
],
false
];
}
else
{
return
[
$currentNode
->
getBase
(),
false
];
}
}
/** Find cases which have preceding Literals with nullary_macro-type operators i.e.:
* "\iint\limits_D \, dx\,dy"
*/
$tu
=
TexUtil
::
getInstance
();
// Check whether the current node is a possible preceding literal
if
(
!(
// logically superfluous brackets were inserted to improve readability
(
$currentNode
instanceof
Literal
&&
// Check if the current node is a nullary macro such as \iint, \sum, \prod, etc.
(
$tu
->
nullary_macro
(
trim
(
$currentNode
->
getArg
()
)
)
// or a limit operator
||
(
trim
(
$currentNode
->
getArg
()
)
==
"
\\
lim"
)
)
)
||
// or the special case of \operatorname
(
$currentNode
instanceof
Fun1nb
&&
$currentNode
->
getFname
()
==
"
\\
operatorname"
)
)
)
{
return
[
null
,
false
];
}
// Check whether the next node is a possible limits construct
if
(
!(
(
$nextNode
instanceof
DQ
||
$nextNode
instanceof
FQ
)
&&
$nextNode
->
getBase
()
instanceof
Literal
&&
(
$nextNode
->
containsFunc
(
"
\\
limits"
)
||
$nextNode
->
containsFunc
(
"
\\
nolimits"
)
)
)
)
{
return
[
null
,
false
];
}
return
[
$currentNode
,
true
];
}
public
function
checkForNot
(
TexNode
$currentNode
):
bool
{
if
(
$currentNode
instanceof
Literal
&&
trim
(
$currentNode
->
getArg
()
)
==
"
\\
not"
)
{
return
true
;
}
return
false
;
}
public
function
checkForDerivatives
(
int
$iStart
,
array
$args
):
int
{
$ctr
=
0
;
for
(
$i
=
$iStart
,
$count
=
count
(
$this
->
args
);
$i
<
$count
;
$i
++
)
{
$followUp
=
$args
[
$i
];
if
(
$followUp
instanceof
Literal
&&
$followUp
->
getArg
()
===
"'"
)
{
$ctr
++;
}
else
{
break
;
}
}
return
$ctr
;
}
public
function
checkForNamedFctArgs
(
TexNode
$currentNode
,
?
TexNode
$nextNode
):
array
{
// Check if current node is named function
$hasNamedFct
=
false
;
if
(
$currentNode
instanceof
TexArray
&&
count
(
$currentNode
->
args
)
==
2
)
{
$tu
=
TexUtil
::
getInstance
();
$currentNodeContent
=
$currentNode
[
0
];
if
(
$currentNodeContent
instanceof
Literal
&&
$tu
->
latex_function_names
(
$currentNodeContent
->
getArg
()
)
)
{
$hasNamedFct
=
true
;
}
}
elseif
(
$currentNode
instanceof
Fun1nb
&&
$currentNode
->
getFname
()
===
'
\\
operatorname'
)
{
$hasNamedFct
=
true
;
}
// Check if there is a valid argument as next parameter
$hasValidParameters
=
false
;
if
(
!
$hasNamedFct
)
{
return
[
$hasNamedFct
,
$hasValidParameters
];
}
if
(
$nextNode
&&
!(
$nextNode
instanceof
Literal
&&
$nextNode
->
getArg
()
===
"'"
)
)
{
$hasValidParameters
=
true
;
}
return
[
$hasNamedFct
,
$hasValidParameters
];
}
private
function
squashLiterals
()
{
$tmp
=
''
;
foreach
(
$this
->
args
as
$arg
)
{
if
(
!(
$arg
instanceof
Literal
)
)
{
return
;
}
// Don't squash if there is a macro in the literal
if
(
preg_match
(
"/[
\\\\
]/"
,
$arg
->
getArg
()
)
)
{
return
;
}
$tmp
.=
$arg
->
getArg
();
}
$this
->
args
=
[
new
Literal
(
$tmp
)
];
$this
->
curly
=
false
;
}
/** @inheritDoc */
public
function
renderMML
(
$arguments
=
[],
&
$state
=
[]
)
{
// Everything here is for parsing displaystyle, probably refactored to WikiTexVC grammar later
$fullRenderedArray
=
""
;
$mmlStyles
=
[];
$currentColor
=
null
;
if
(
array_key_exists
(
'squashLiterals'
,
$state
)
)
{
$this
->
squashLiterals
();
}
for
(
$i
=
0
,
$count
=
count
(
$this
->
args
);
$i
<
$count
;
$i
++
)
{
$current
=
$this
->
args
[
$i
];
if
(
isset
(
$this
->
args
[
$i
+
1
]
)
)
{
$next
=
$this
->
args
[
$i
+
1
];
}
else
{
$next
=
null
;
}
// Check for sideset
$foundSideset
=
$this
->
checkForSideset
(
$current
,
$next
);
if
(
$foundSideset
)
{
$state
[
"sideset"
]
=
$foundSideset
;
// Skipping the succeeding Literal
$i
++;
}
// Check for limits
$foundLimits
=
$this
->
checkForLimits
(
$current
,
$next
);
if
(
$foundLimits
[
0
]
)
{
$state
[
"limits"
]
=
$foundLimits
[
0
];
if
(
$foundLimits
[
1
]
)
{
continue
;
}
}
// Check for Not
$foundNot
=
$this
->
checkForNot
(
$current
);
if
(
$foundNot
)
{
$state
[
"not"
]
=
true
;
continue
;
}
// Check for derivatives
$foundDeriv
=
$this
->
checkForDerivatives
(
$i
+
1
,
$this
->
args
);
if
(
$foundDeriv
>
0
)
{
// skip the next indices which are derivative characters
$i
+=
$foundDeriv
;
$state
[
"deriv"
]
=
$foundDeriv
;
}
// Check if there is a new color definition and add it to state
$foundColorDef
=
$this
->
checkForColorDefinition
(
$current
);
if
(
$foundColorDef
)
{
$state
[
"colorDefinitions"
][
$foundColorDef
[
"name"
]]
=
$foundColorDef
;
continue
;
}
// Pass preceding color info to state
$foundColor
=
$this
->
checkForColor
(
$current
);
if
(
$foundColor
[
0
]
)
{
$currentColor
=
$foundColor
[
1
];
// Skipping the color element itself for rendering
continue
;
}
$styleArguments
=
$this
->
checkForStyleArgs
(
$current
);
$foundNamedFct
=
$this
->
checkForNamedFctArgs
(
$current
,
$next
);
if
(
$foundNamedFct
[
0
]
)
{
$state
[
"foundNamedFct"
]
=
$foundNamedFct
;
}
if
(
$styleArguments
)
{
$state
[
"styleargs"
]
=
$styleArguments
;
$mmlStyle
=
new
MMLmstyle
(
""
,
$styleArguments
);
$fullRenderedArray
.=
$mmlStyle
->
getStart
();
if
(
$next
instanceof
TexNode
&&
$next
->
isCurly
()
)
{
// Wrap with style-tags when the next element is a Curly which determines start and end tag.
$fullRenderedArray
.=
$this
->
createMMLwithContext
(
$currentColor
,
$next
,
$state
,
$arguments
);
$fullRenderedArray
.=
$mmlStyle
->
getEnd
();
$mmlStyle
=
null
;
unset
(
$state
[
"styleargs"
]
);
$i
++;
}
else
{
// Start the style indicator in cases like \textstyle abc
$mmlStyles
[]
=
$mmlStyle
->
getEnd
();
}
}
else
{
$fullRenderedArray
.=
$this
->
createMMLwithContext
(
$currentColor
,
$current
,
$state
,
$arguments
);
}
if
(
array_key_exists
(
"not"
,
$state
)
)
{
unset
(
$state
[
"not"
]
);
}
if
(
array_key_exists
(
"limits"
,
$state
)
)
{
unset
(
$state
[
"limits"
]
);
}
if
(
array_key_exists
(
"deriv"
,
$state
)
)
{
unset
(
$state
[
"deriv"
]
);
}
}
foreach
(
array_reverse
(
$mmlStyles
)
as
$mmlStyleEnd
)
{
$fullRenderedArray
.=
$mmlStyleEnd
;
}
if
(
$this
->
curly
&&
$this
->
getLength
()
>
1
)
{
$mmlRow
=
new
MMLmrow
();
return
$mmlRow
->
encapsulateRaw
(
$fullRenderedArray
);
}
return
$fullRenderedArray
;
}
private
function
createMMLwithContext
(
?
string
$currentColor
,
TexNode
$currentNode
,
array
&
$state
,
array
$arguments
):
string
{
if
(
$currentColor
)
{
if
(
array_key_exists
(
"colorDefinitions"
,
$state
)
&&
is_array
(
$state
[
"colorDefinitions"
]
)
&&
array_key_exists
(
$currentColor
,
$state
[
"colorDefinitions"
]
??
[]
)
&&
is_array
(
$state
[
"colorDefinitions"
][
$currentColor
]
)
&&
array_key_exists
(
"hex"
,
$state
[
"colorDefinitions"
][
$currentColor
]
)
)
{
$displayedColor
=
$state
[
"colorDefinitions"
][
$currentColor
][
"hex"
];
}
else
{
$resColor
=
TexUtil
::
getInstance
()->
color
(
ucfirst
(
$currentColor
)
);
$displayedColor
=
$resColor
?:
$currentColor
;
}
$mmlStyleColor
=
new
MMLmstyle
(
""
,
[
"mathcolor"
=>
$displayedColor
]
);
$ret
=
$mmlStyleColor
->
encapsulateRaw
(
$currentNode
->
renderMML
(
$arguments
,
$state
)
);
}
else
{
$ret
=
$currentNode
->
renderMML
(
$arguments
,
$state
);
}
return
$this
->
addDerivativesContext
(
$state
,
$ret
);
}
/**
* If derivative was recognized, add the corresponding derivative math operator
* to the mml and wrap with msup element.
* @param array &$state state indicator which indicates derivative
* @param string $mml mathml input
* @return string mml with additional mml-elements for derivatives
*/
public
function
addDerivativesContext
(
array
&
$state
,
string
$mml
):
string
{
if
(
array_key_exists
(
"deriv"
,
$state
)
&&
$state
[
"deriv"
]
>
0
)
{
$msup
=
new
MMLmsup
();
$moDeriv
=
new
MMLmo
();
if
(
$state
[
"deriv"
]
==
1
)
{
$derInfo
=
"′"
;
}
elseif
(
$state
[
"deriv"
]
==
2
)
{
$derInfo
=
"″"
;
}
elseif
(
$state
[
"deriv"
]
==
3
)
{
$derInfo
=
"‴"
;
}
elseif
(
$state
[
"deriv"
]
==
4
)
{
$derInfo
=
"⁗"
;
}
else
{
$derInfo
=
str_repeat
(
"′"
,
$state
[
"deriv"
]
);
}
$mml
=
$msup
->
encapsulateRaw
(
$mml
.
$moDeriv
->
encapsulateRaw
(
$derInfo
)
);
if
(
(
$state
[
'foundNamedFct'
][
0
]
??
false
)
&&
!(
$state
[
'foundNamedFct'
][
1
]
??
true
)
)
{
$mml
.=
MMLParsingUtil
::
renderApplyFunction
();
}
}
return
$mml
;
}
/** @inheritDoc */
public
function
inCurlies
()
{
if
(
isset
(
$this
->
args
[
0
]
)
&&
count
(
$this
->
args
)
==
1
)
{
return
$this
->
args
[
0
]->
inCurlies
();
}
else
{
return
'{'
.
parent
::
render
()
.
'}'
;
}
}
/** @inheritDoc */
public
function
extractSubscripts
()
{
$y
=
[];
foreach
(
$this
->
args
as
$x
)
{
$y
=
array_merge
(
$y
,
$x
->
extractSubscripts
()
);
}
if
(
isset
(
$this
->
args
[
0
]
)
&&
(
count
(
$this
->
args
)
==
count
(
$y
)
)
)
{
return
implode
(
''
,
$y
);
}
return
[];
}
/** @inheritDoc */
public
function
extractIdentifiers
(
$args
=
null
)
{
if
(
$args
==
null
)
{
$args
=
$this
->
args
;
}
$list
=
parent
::
extractIdentifiers
(
$args
);
$outpos
=
0
;
$offset
=
0
;
$int
=
0
;
for
(
$inpos
=
0
;
$inpos
<
count
(
$list
);
$inpos
++
)
{
$outpos
=
$inpos
-
$offset
;
switch
(
$list
[
$inpos
]
)
{
case
'
\'
'
:
$list
[
$outpos
-
1
]
.=
'
\'
'
;
$offset
++;
break
;
case
'
\\
int'
:
$int
++;
$offset
++;
break
;
case
'
\\
mathrm{d}'
:
case
'd'
:
if
(
$int
)
{
$int
--;
$offset
++;
break
;
}
// no break
default
:
if
(
isset
(
$list
[
0
]
)
)
{
$list
[
$outpos
]
=
$list
[
$inpos
];
}
}
}
return
array_slice
(
$list
,
0
,
count
(
$list
)
-
$offset
);
}
/** @inheritDoc */
public
function
getModIdent
()
{
$y
=
[];
foreach
(
$this
->
args
as
$x
)
{
$y
=
array_merge
(
$y
,
$x
->
getModIdent
()
);
}
if
(
isset
(
$this
->
args
[
0
]
)
&&
(
count
(
$this
->
args
)
==
count
(
$y
)
)
)
{
return
implode
(
""
,
$y
);
}
return
[];
}
/**
* @param TexNode|string ...$elements
*/
public
function
push
(
...
$elements
):
TexArray
{
self
::
checkInput
(
$elements
);
array_push
(
$this
->
args
,
...
$elements
);
return
$this
;
}
public
function
pop
()
{
array_splice
(
$this
->
args
,
0
,
1
);
}
/**
* @return TexNode|null first value
*/
public
function
first
()
{
return
$this
->
args
[
0
]
??
null
;
}
/**
* @return TexNode|null second value
*/
public
function
second
()
{
return
$this
->
args
[
1
]
??
null
;
}
/**
* @param TexNode|string ...$elements
*/
public
function
unshift
(
...
$elements
):
TexArray
{
array_unshift
(
$this
->
args
,
...
$elements
);
return
$this
;
}
/**
* @throws InvalidArgumentException if args not of correct type
* @param TexNode[] $args input args
* @return void
*/
private
static
function
checkInput
(
$args
):
void
{
foreach
(
$args
as
$arg
)
{
if
(
!(
$arg
instanceof
TexNode
)
)
{
throw
new
InvalidArgumentException
(
'Wrong input type specified in input elements.'
);
}
}
}
public
function
render
()
{
if
(
$this
->
curly
)
{
return
$this
->
inCurlies
();
}
return
parent
::
render
();
}
public
function
isCurly
():
bool
{
return
$this
->
curly
;
}
public
function
setCurly
(
bool
$curly
=
true
):
TexArray
{
$this
->
curly
=
$curly
;
return
$this
;
}
/**
* @return Generator<TexNode>
*/
public
function
getIterator
():
Generator
{
yield
from
$this
->
args
;
}
/**
* @return TexNode[]
*/
public
function
getArgs
():
array
{
return
parent
::
getArgs
();
}
/** @inheritDoc */
public
function
offsetExists
(
$offset
):
bool
{
return
isset
(
$this
->
args
[
$offset
]
);
}
/** @inheritDoc */
public
function
offsetGet
(
$offset
):
?
TexNode
{
return
$this
->
args
[
$offset
]
??
null
;
}
/** @inheritDoc */
public
function
offsetSet
(
$offset
,
$value
):
void
{
if
(
!(
$value
instanceof
TexNode
)
)
{
throw
new
InvalidArgumentException
(
'TexArray elements must be of type TexNode.'
);
}
$this
->
args
[
$offset
]
=
$value
;
}
/** @inheritDoc */
public
function
offsetUnset
(
$offset
):
void
{
unset
(
$this
->
args
[
$offset
]
);
}
public
function
setRowSpecs
(
?
LengthSpec
$r
)
{
$this
->
rowSpecs
=
$r
;
}
public
function
getRowSpecs
():
?
LengthSpec
{
return
$this
->
rowSpecs
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Tue, Aug 18, 15:17 (3 w, 5 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
30/d5/baae88c22be2403db3d45234b43e
Default Alt Text
TexArray.php (15 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment