Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
58.88% |
378 / 642 |
|
34.04% |
16 / 47 |
CRAP | |
0.00% |
0 / 1 |
| BaseParsing | |
58.88% |
378 / 642 |
|
34.04% |
16 / 47 |
4740.15 | |
0.00% |
0 / 1 |
| accent | |
93.75% |
15 / 16 |
|
0.00% |
0 / 1 |
5.01 | |||
| array | |
80.00% |
12 / 15 |
|
0.00% |
0 / 1 |
13.15 | |||
| alignAt | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
3 | |||
| amsEqnArray | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
3 | |||
| boldsymbol | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| cancel | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
| cancelTo | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
| chemCustom | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
| customLetters | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| cFrac | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
1 | |||
| crLaTeX | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| dots | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| genFrac | |
74.36% |
29 / 39 |
|
0.00% |
0 / 1 |
15.85 | |||
| frac | |
41.67% |
5 / 12 |
|
0.00% |
0 / 1 |
9.96 | |||
| hline | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| hskip | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
42 | |||
| handleOperatorName | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
| macro | |
46.43% |
39 / 84 |
|
0.00% |
0 / 1 |
96.41 | |||
| matrix | |
100.00% |
48 / 48 |
|
100.00% |
1 / 1 |
19 | |||
| namedOp | |
60.00% |
3 / 5 |
|
0.00% |
0 / 1 |
2.26 | |||
| over | |
55.56% |
15 / 27 |
|
0.00% |
0 / 1 |
18.78 | |||
| oint | |
40.00% |
6 / 15 |
|
0.00% |
0 / 1 |
21.82 | |||
| overset | |
66.67% |
4 / 6 |
|
0.00% |
0 / 1 |
2.15 | |||
| phantom | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
| raiseLower | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
72 | |||
| underset | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
| underOver | |
92.31% |
12 / 13 |
|
0.00% |
0 / 1 |
6.02 | |||
| mathFont | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| mathChoice | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
132 | |||
| makeBig | |
90.62% |
29 / 32 |
|
0.00% |
0 / 1 |
14.16 | |||
| machine | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| namedFn | |
60.00% |
3 / 5 |
|
0.00% |
0 / 1 |
2.26 | |||
| limits | |
94.44% |
17 / 18 |
|
0.00% |
0 / 1 |
7.01 | |||
| setFont | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| sideset | |
60.00% |
24 / 40 |
|
0.00% |
0 / 1 |
18.74 | |||
| spacer | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| smash | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
72 | |||
| texAtom | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
42 | |||
| intent | |
0.00% |
0 / 31 |
|
0.00% |
0 / 1 |
240 | |||
| hBox | |
51.52% |
17 / 33 |
|
0.00% |
0 / 1 |
36.34 | |||
| setStyle | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| not | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| vbox | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
| sqrt | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
4 | |||
| tilde | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| xArrow | |
100.00% |
22 / 22 |
|
100.00% |
1 / 1 |
2 | |||
| getApplyFct | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
| 1 | <?php |
| 2 | namespace MediaWiki\Extension\Math\WikiTexVC\MMLmappings; |
| 3 | |
| 4 | use IntlChar; |
| 5 | use MediaWiki\Extension\Math\WikiTexVC\MMLmappings\TexConstants\Misc; |
| 6 | use MediaWiki\Extension\Math\WikiTexVC\MMLmappings\TexConstants\Sizes; |
| 7 | use MediaWiki\Extension\Math\WikiTexVC\MMLmappings\TexConstants\Tag; |
| 8 | use MediaWiki\Extension\Math\WikiTexVC\MMLmappings\TexConstants\TexClass; |
| 9 | use MediaWiki\Extension\Math\WikiTexVC\MMLmappings\TexConstants\Variants; |
| 10 | use MediaWiki\Extension\Math\WikiTexVC\MMLmappings\Util\MMLParsingUtil; |
| 11 | use MediaWiki\Extension\Math\WikiTexVC\MMLmappings\Util\MMLutil; |
| 12 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLarray; |
| 13 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLbase; |
| 14 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmenclose; |
| 15 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmerror; |
| 16 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmfrac; |
| 17 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmi; |
| 18 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmmultiscripts; |
| 19 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmo; |
| 20 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmover; |
| 21 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmpadded; |
| 22 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmphantom; |
| 23 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmroot; |
| 24 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmrow; |
| 25 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmspace; |
| 26 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmsqrt; |
| 27 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmstyle; |
| 28 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmsub; |
| 29 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmsup; |
| 30 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmtable; |
| 31 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmtd; |
| 32 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmtext; |
| 33 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmtr; |
| 34 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmunder; |
| 35 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmunderover; |
| 36 | use MediaWiki\Extension\Math\WikiTexVC\Nodes\DQ; |
| 37 | use MediaWiki\Extension\Math\WikiTexVC\Nodes\FQ; |
| 38 | use MediaWiki\Extension\Math\WikiTexVC\Nodes\Fun1; |
| 39 | use MediaWiki\Extension\Math\WikiTexVC\Nodes\Fun2; |
| 40 | use MediaWiki\Extension\Math\WikiTexVC\Nodes\Fun2sq; |
| 41 | use MediaWiki\Extension\Math\WikiTexVC\Nodes\Fun4; |
| 42 | use MediaWiki\Extension\Math\WikiTexVC\Nodes\Literal; |
| 43 | use MediaWiki\Extension\Math\WikiTexVC\Nodes\Matrix; |
| 44 | use MediaWiki\Extension\Math\WikiTexVC\Nodes\TexArray; |
| 45 | use MediaWiki\Extension\Math\WikiTexVC\Nodes\TexNode; |
| 46 | use MediaWiki\Extension\Math\WikiTexVC\Nodes\UQ; |
| 47 | use MediaWiki\Extension\Math\WikiTexVC\TexUtil; |
| 48 | use MediaWiki\Extension\Math\WikiTexVC\TexVC; |
| 49 | |
| 50 | /** |
| 51 | * Parsing functions for specific recognized mappings. |
| 52 | * Usually the parsing functions are invoked from the BaseMethods classes. |
| 53 | */ |
| 54 | class BaseParsing { |
| 55 | |
| 56 | public static function accent( $node, $passedArgs, $name, $operatorContent, $accent, $stretchy = null ): MMLbase { |
| 57 | // Currently this is own implementation from Fun1.php |
| 58 | // TODO The first if-clause is mathjax specific (and not necessary by generic parsers) |
| 59 | // and will most probably removed (just for running all tc atm) |
| 60 | if ( $accent == "00B4" || $accent == "0060" ) { |
| 61 | $attrs = [ Tag::SCRIPTTAG => "true" ]; |
| 62 | } else { |
| 63 | if ( $stretchy == null ) { |
| 64 | // $attrs = [ "stretchy" => "false" ]; // not mention explicit stretchy |
| 65 | $attrs = []; |
| 66 | } else { |
| 67 | $attrs = [ "stretchy" => "true" ]; |
| 68 | } |
| 69 | } |
| 70 | // Fetching entity from $accent key tbd |
| 71 | $entity = MMLutil::createEntity( $accent ); |
| 72 | if ( !$entity ) { |
| 73 | $entity = $accent; |
| 74 | } |
| 75 | |
| 76 | return new MMLmrow( TexClass::ORD, [], |
| 77 | new MMLmrow( TexClass::ORD, [], |
| 78 | MMLmover::newSubtree( |
| 79 | $node->getArg()->toMMLtree( $passedArgs ), |
| 80 | ( new MMLmo( "", $attrs, $entity ) ) |
| 81 | ) |
| 82 | ) |
| 83 | ); |
| 84 | } |
| 85 | |
| 86 | public static function array( $node, $passedArgs, $operatorContent, $name, $begin = null, $open = null, |
| 87 | $close = null, $align = null, $spacing = null, |
| 88 | $vspacing = null, $style = null, $raggedHeight = null ): MMLbase { |
| 89 | $output = []; |
| 90 | if ( $open != null ) { |
| 91 | $resDelimiter = TexUtil::getInstance()->delimiter( trim( $open ) ) ?? false; |
| 92 | if ( $resDelimiter ) { |
| 93 | // $retDelim = $bm->checkAndParseDelimiter($open, $node,$passedArgs,true); |
| 94 | $output[] = ( new MMLmo( TexClass::OPEN, [], $resDelimiter[0] ) ); |
| 95 | } |
| 96 | } |
| 97 | if ( $name == "Bmatrix" || $name == "bmatrix" || $name == "Vmatrix" |
| 98 | || $name == "vmatrix" || $name == "smallmatrix" || $name == "pmatrix" || $name == "matrix" ) { |
| 99 | // This is a workaround and might be improved mapping BMatrix to Matrix directly instead of array |
| 100 | return self::matrix( $node, $passedArgs, $operatorContent, $name, |
| 101 | $open, $close, null, null, null, null, true ); |
| 102 | |
| 103 | } else { |
| 104 | $output[] = new MMLmrow( TexClass::ORD, [], $node->getMainarg()->toMMLtree() ); |
| 105 | } |
| 106 | |
| 107 | if ( $close != null ) { |
| 108 | $resDelimiter = TexUtil::getInstance()->delimiter( trim( $close ) ) ?? false; |
| 109 | if ( $resDelimiter ) { |
| 110 | $output[] = new MMLmo( TexClass::CLOSE, [], $resDelimiter[0] ); |
| 111 | } |
| 112 | } |
| 113 | return new MMLarray( ...$output ); |
| 114 | } |
| 115 | |
| 116 | public static function alignAt( Matrix $node, $passedArgs, $operatorContent, $name, $smth, |
| 117 | $smth2 = null ): MMLbase { |
| 118 | // Parsing is very similar to AmsEQArray, maybe extract function ... tcs: 178 |
| 119 | $mtable = new MMLmtable( "" ); |
| 120 | $inner = []; |
| 121 | |
| 122 | foreach ( $node as $tableRow ) { |
| 123 | $mtds = []; |
| 124 | foreach ( $tableRow->getArgs() as $tableCell ) { |
| 125 | $mtds[] = new MMLmtd( "", [], $tableCell->toMMLtree() ); |
| 126 | } |
| 127 | $inner[] = new MMLmtr( "", [], ...$mtds ); |
| 128 | } |
| 129 | $mtable->addChild( ...$inner ); |
| 130 | return new MMLmrow( TexClass::ORD, [], $mtable ); |
| 131 | } |
| 132 | |
| 133 | public static function amsEqnArray( $node, $passedArgs, $operatorContent, $name, $smth, $smth2 = null ): MMLbase { |
| 134 | $mtable = new MMLmtable( '' ); |
| 135 | $renderedInner = []; |
| 136 | foreach ( $node as $tableRow ) { |
| 137 | $mtrs = []; |
| 138 | foreach ( $tableRow->getArgs() as $tableCell ) { |
| 139 | $mtrs[] = new MMLmtd( "", [], $tableCell->toMMLtree() ); // pass args here ? |
| 140 | } |
| 141 | $renderedInner[] = new MMLmtr( "", [], ...$mtrs ); |
| 142 | } |
| 143 | $mtable->addChild( ...$renderedInner ); |
| 144 | return new MMLmrow( TexClass::ORD, [], $mtable ); |
| 145 | } |
| 146 | |
| 147 | public static function boldsymbol( $node, $passedArgs, $operatorContent, $name, $smth = null, |
| 148 | $smth2 = null ): MMLbase { |
| 149 | $passedArgs = array_merge( [ 'mathvariant' => Variants::BOLDITALIC ] ); |
| 150 | return new MMLmrow( TexClass::ORD, [], $node->getArg()->toMMLtree( $passedArgs ) ); |
| 151 | } |
| 152 | |
| 153 | public static function cancel( Fun1 $node, $passedArgs, $operatorContent, $name, $notation = '' ): MMLbase { |
| 154 | $bars = []; |
| 155 | foreach ( explode( ' ', $notation ) as $element ) { |
| 156 | $bars[] = ( new MMLmrow( '', [ 'class' => 'menclose-' . $element ] ) ); |
| 157 | } |
| 158 | |
| 159 | return new MMLmenclose( '', [ 'notation' => $notation, 'class' => 'menclose' ], |
| 160 | $node->getArg()->toMMLtree(), ...$bars ); |
| 161 | } |
| 162 | |
| 163 | public static function cancelTo( $node, $passedArgs, $operatorContent, $name, $notation = null ): MMLbase { |
| 164 | $mpAdded = new MMLmpadded( "", [ "depth" => "-.1em", "height" => "+.1em", "voffset" => ".1em" ], |
| 165 | $node->getArg1()->toMMLtree() ); |
| 166 | $menclose = new MMLmenclose( "", [ "notation" => $notation ], $node->getArg2()->toMMLtree() ); |
| 167 | return new MMLmrow( TexClass::ORD, [], MMLmsup::newSubtree( $menclose, $mpAdded ) ); |
| 168 | } |
| 169 | |
| 170 | public static function chemCustom( $node, $passedArgs, $operatorContent, $name, $translation = null ) { |
| 171 | return $translation ?: 'tbd chemCustom'; |
| 172 | } |
| 173 | |
| 174 | public static function customLetters( $node, $passedArgs, $operatorContent, $name, $char, |
| 175 | $isOperator = false ): MMLbase { |
| 176 | if ( $isOperator ) { |
| 177 | return new MMLmrow( TexClass::ORD, [], new MMLmo( "", [], $char ) ); |
| 178 | } |
| 179 | return new MMLmrow( TexClass::ORD, [], new MMLmi( "", [ 'mathvariant' => Variants::NORMAL ], $char ) ); |
| 180 | } |
| 181 | |
| 182 | public static function cFrac( $node, $passedArgs, $operatorContent, $name ): MMLbase { |
| 183 | $mstyle1 = new MMLmstyle( "", [ "displaystyle" => "false", "scriptlevel" => "0" ], |
| 184 | new MMLmrow( TexClass::ORD, [], $node->getArg1()->toMMLtree() ) ); |
| 185 | $mstyle2 = new MMLmstyle( "", [ "displaystyle" => "false", "scriptlevel" => "0" ], |
| 186 | new MMLmrow( TexClass::ORD, [], $node->getArg2()->toMMLtree() ) ); |
| 187 | // See TexUtilMMLTest testcase 81 |
| 188 | // (mml3 might be erronous here, but this element seems to be rendered correctly) |
| 189 | $whatIsThis = new MMLmrow( TexClass::ORD, [], |
| 190 | new MMLmpadded( "", [ "depth" => "3pt", "height" => "8.6pt", "width" => "0" ] ) ); |
| 191 | $inner = new MMLmrow( TexClass::ORD, [], $whatIsThis, $mstyle2 ); |
| 192 | $mfrac = MMLmfrac::newSubtree( |
| 193 | new MMLmrow( TexClass::ORD, [], $whatIsThis, $mstyle1 ), $inner ); |
| 194 | return new MMLmrow( TexClass::ORD, [], $mfrac ); |
| 195 | } |
| 196 | |
| 197 | public static function crLaTeX( $node, $passedArgs, $operatorContent, $name ): MMLbase { |
| 198 | return new MMLmspace( "", [ "linebreak" => "newline" ] ); |
| 199 | } |
| 200 | |
| 201 | public static function dots( $node, $passedArgs, $operatorContent, $name, $smth = null, $smth2 = null ): MMLbase { |
| 202 | // lowerdots || centerdots seems aesthetical, just using lowerdots atm s |
| 203 | return new MMLmo( "", $passedArgs, "…" ); |
| 204 | } |
| 205 | |
| 206 | public static function genFrac( $node, $passedArgs, $operatorContent, $name, |
| 207 | $left = null, $right = null, $thick = null, $style = null ): MMLbase { |
| 208 | // Actually this is in AMSMethods, consider refactoring left, right, thick, style |
| 209 | $bm = new BaseMethods(); |
| 210 | $ret = $bm->checkAndParseDelimiter( $name, $node, $passedArgs, $operatorContent, true ); |
| 211 | if ( $ret ) { |
| 212 | // TBD |
| 213 | if ( $left == null ) { |
| 214 | $left = $ret; |
| 215 | } |
| 216 | if ( $right == null ) { |
| 217 | $right = $ret; |
| 218 | } |
| 219 | if ( $thick == null ) { |
| 220 | $thick = $ret; |
| 221 | } |
| 222 | if ( $style == null ) { |
| 223 | $style = trim( $ret ); |
| 224 | } |
| 225 | } |
| 226 | $attrs = []; |
| 227 | $displayStyle = "false"; |
| 228 | if ( in_array( $thick, [ 'thin', 'medium', 'thick', '0' ], true ) ) { |
| 229 | $attrs = array_merge( $attrs, [ "linethickness" => $thick ] ); |
| 230 | } |
| 231 | if ( $style !== '' ) { |
| 232 | $styleDigit = intval( $style, 10 ); |
| 233 | $styleAlpha = [ 'D', 'T', 'S', 'SS' ][$styleDigit]; |
| 234 | if ( $styleAlpha == null ) { |
| 235 | return new MMLmrow( TexClass::ORD, [], new MMLmtext( "", [], "Bad math style" ) ); |
| 236 | } |
| 237 | |
| 238 | if ( $styleAlpha === 'D' ) { |
| 239 | // NodeUtil_js_1.default.setProperties(frac, { displaystyle: true, scriptlevel: 0 }); |
| 240 | |
| 241 | // tbd add props |
| 242 | $displayStyle = "true"; |
| 243 | $styleAttr = [ "minsize" => "2.047em" ]; |
| 244 | |
| 245 | } else { |
| 246 | $styleAttr = [ "minsize" => "1.2em" ]; |
| 247 | } |
| 248 | } else { |
| 249 | // NodeUtil_js_1.default.setProperties(frac, { displaystyle: false, |
| 250 | // scriptlevel: styleDigit - 1 }); |
| 251 | // tbd add props |
| 252 | $styleAttr = [ "maxsize" => "1.2em", "minsize" => "1.2em" ]; |
| 253 | |
| 254 | } |
| 255 | $output = []; |
| 256 | if ( $left ) { |
| 257 | $mrowOpen = new MMLmrow( TexClass::OPEN, [], new MMLmo( "", $styleAttr, $left ) ); |
| 258 | $output[] = $mrowOpen; |
| 259 | } |
| 260 | $mrow1 = new MMLmrow( TexClass::ORD, [], $node->getArg1()->toMMLtree() ); |
| 261 | $mrow2 = new MMLmrow( TexClass::ORD, [], $node->getArg2()->toMMLtree() ); |
| 262 | |
| 263 | $output[] = MMLmfrac::newSubtree( $mrow1, $mrow2, "", $attrs ); |
| 264 | if ( $right ) { |
| 265 | $mrowClose = new MMLmrow( TexClass::CLOSE, [], new MMLmo( "", $styleAttr, $right ) ); |
| 266 | $output[] = $mrowClose; |
| 267 | } |
| 268 | $output = new MMLmrow( TexClass::ORD, [], ...$output ); |
| 269 | if ( $style !== '' ) { |
| 270 | $output = new MMLmstyle( "", [ "displaystyle" => $displayStyle, "scriptlevel" => "0" ], $output ); |
| 271 | } |
| 272 | |
| 273 | return new MMLmrow( TexClass::ORD, [], $output ); |
| 274 | } |
| 275 | |
| 276 | public static function frac( $node, $passedArgs, $operatorContent, $name ): MMLbase { |
| 277 | if ( $node instanceof Fun2 ) { |
| 278 | $inner = [ new MMLmrow( TexClass::ORD, [], $node->getArg1()->toMMLtree() ), |
| 279 | new MMLmrow( TexClass::ORD, [], $node->getArg2()->toMMLtree() ) ]; |
| 280 | } elseif ( $node instanceof DQ ) { |
| 281 | $inner = [ new MMLmrow( TexClass::ORD, [], $node->getBase()->toMMLtree() ), |
| 282 | new MMLmrow( TexClass::ORD, [], $node->getDown()->toMMLtree() ) ]; |
| 283 | } else { |
| 284 | $inner = []; |
| 285 | foreach ( $node->getArgs() as $arg ) { |
| 286 | $rendered = is_string( $arg ) ? $arg : $arg->toMMLtree(); |
| 287 | $inner[] = new MMLmrow( TexClass::ORD, [], $rendered ); |
| 288 | } |
| 289 | } |
| 290 | $mfrac = MMLmfrac::newSubtree( $inner[0], $inner[1] ); |
| 291 | return new MMLmrow( TexClass::ORD, [], $mfrac ); |
| 292 | } |
| 293 | |
| 294 | public static function hline( $node, $passedArgs, $operatorContent, $name, |
| 295 | $smth1 = null, $smth2 = null, $smth3 = null, $smth4 = null ): MMLbase { |
| 296 | // HLine is most probably not parsed this way, since only parsed in Matrix context |
| 297 | return new MMLmrow( "tbd", [], new MMLmtext( "", [], "HLINE TBD" ) ); |
| 298 | } |
| 299 | |
| 300 | public static function hskip( $node, $passedArgs, $operatorContent, $name ): ?MMLbase { |
| 301 | if ( $node->getArg()->isCurly() ) { |
| 302 | $unit = MMLutil::squashLitsToUnit( $node->getArg() ); |
| 303 | if ( !$unit ) { |
| 304 | return null; |
| 305 | } |
| 306 | $em = MMLutil::dimen2em( $unit ); |
| 307 | } else { |
| 308 | // Prevent parsing in unmapped cases |
| 309 | return null; |
| 310 | } |
| 311 | // Added kern j4t |
| 312 | if ( $name == "mskip" || $name == "mkern" || "kern" ) { |
| 313 | $args = [ "width" => $em ]; |
| 314 | } else { |
| 315 | return null; |
| 316 | } |
| 317 | |
| 318 | return new MMLmspace( "", $args ); |
| 319 | } |
| 320 | |
| 321 | public static function handleOperatorName( $node, $passedArgs, $operatorContent, $name ): MMLbase { |
| 322 | // In example "\\operatorname{a}" |
| 323 | $applyFct = self::getApplyFct( $operatorContent ); |
| 324 | $mmlNot = ""; |
| 325 | if ( isset( $operatorContent['not'] ) && $operatorContent['not'] ) { |
| 326 | $mmlNot = MMLParsingUtil::createNot(); |
| 327 | } |
| 328 | $passedArgs = array_merge( $passedArgs, [ Tag::CLASSTAG => TexClass::OP, 'mathvariant' => Variants::NORMAL ] ); |
| 329 | $state = [ 'squashLiterals' => true ]; |
| 330 | return new MMLarray( $mmlNot, $node->getArg()->toMMLtree( $passedArgs, $state ), $applyFct ); |
| 331 | } |
| 332 | |
| 333 | public static function macro( $node, $passedArgs, $operatorContent, $name, |
| 334 | $macro = '', $argcount = null, $def = null ) { |
| 335 | // Parse the Macro |
| 336 | if ( $macro == "\\text{ }" ) { |
| 337 | return new MMLmtext( "", [], ' ' ); |
| 338 | } |
| 339 | switch ( trim( $name ) ) { |
| 340 | case "\\mod": |
| 341 | // @phan-suppress-next-line PhanUndeclaredMethod |
| 342 | $inner = $node->getArg() instanceof TexNode ? $node->getArg()->toMMLtree() : ""; |
| 343 | return new MMLmrow( TexClass::ORD, [], |
| 344 | new MMLmo( "", [ "lspace" => "2.5pt", "rspace" => "2.5pt" ], "mod" ), $inner ); |
| 345 | case "\\pmod": |
| 346 | // tbd indicate in mapping that this is composed within php |
| 347 | // @phan-suppress-next-line PhanUndeclaredMethod |
| 348 | $inner = $node->getArg() instanceof TexNode ? $node->getArg()->toMMLtree() : ""; |
| 349 | |
| 350 | return new MMLmrow( TexClass::ORD, [], ( new MMLmspace( "", [ "width" => "0.444em" ] ) ), |
| 351 | new MMLmo( "", [ "stretchy" => "false" ], "(" ), |
| 352 | new MMLmi( "", [], "mod" ), |
| 353 | new MMLmspace( "", [ "width" => "0.333em" ] ), |
| 354 | $inner, |
| 355 | new MMLmo( "", [ "stretchy" => "false" ], ")" ) |
| 356 | ); |
| 357 | case "\\varlimsup": |
| 358 | case "\\varliminf": |
| 359 | // hardcoded macro in php (there is also a dynamic mapping which is not completely resolved atm) |
| 360 | if ( trim( $name ) === "\\varlimsup" ) { |
| 361 | $movu = MMLmover::newSubtree( ( |
| 362 | new MMLmi( "", [], "lim" ) ), |
| 363 | new MMLmo( "", [ "accent" => "true" ], "―" ) ); |
| 364 | } else { |
| 365 | $movu = MMLmunder::newSubtree( ( |
| 366 | new MMLmi( "", [], "lim" ) ), |
| 367 | new MMLmo( "", [ "accent" => "true" ], "―" ) ); |
| 368 | } |
| 369 | return new MMLmrow( TexClass::OP, [], $movu ); |
| 370 | |
| 371 | case "\\varinjlim": |
| 372 | return new MMLmrow( TexClass::OP, [], |
| 373 | MMLmunder::newSubtree( new MMLmi( "", [], "lim" ), |
| 374 | new MMLmo( "", [], "→" ) ) ); |
| 375 | case "\\varprojlim": |
| 376 | return new MMLmrow( TexClass::OP, [], |
| 377 | MMLmunder::newSubtree( new MMLmi( "", [], "lim" ), |
| 378 | new MMLmo( "", [], "←" ) ) ); |
| 379 | case "\\stackrel": |
| 380 | // hardcoded macro in php (there is also a dynamic mapping which is not not completely resolved atm) |
| 381 | if ( $node instanceof DQ ) { |
| 382 | $inner = MMLmover::newSubtree( new MMLmrow( TexClass::OP, [], |
| 383 | $node->getBase()->toMMLtree() ), |
| 384 | new MMLmrow( TexClass::ORD, [], $node->getDown()->toMMLtree() ) |
| 385 | ); |
| 386 | } else { |
| 387 | $inner = MMLmover::newSubtree( new MMLmrow( TexClass::OP, [], |
| 388 | // @phan-suppress-next-line PhanUndeclaredMethod |
| 389 | $node->getArg2()->toMMLtree() ), |
| 390 | // @phan-suppress-next-line PhanUndeclaredMethod |
| 391 | new MMLmrow( TexClass::ORD, [], $node->getArg1()->toMMLtree() ) |
| 392 | ); |
| 393 | } |
| 394 | return new MMLmrow( TexClass::ORD, [], new MMLmrow( TexClass::REL, [], $inner ) ); |
| 395 | case "\\bmod": |
| 396 | $mspace = new MMLmspace( "", [ "width" => "0.167em" ] ); |
| 397 | // @phan-suppress-next-line PhanUndeclaredMethod |
| 398 | $inner = $node->getArg() instanceof TexNode ? |
| 399 | // @phan-suppress-next-line PhanUndeclaredMethod |
| 400 | new MMLmrow( TexClass::ORD, [], $node->getArg()->toMMLtree() ) : ""; |
| 401 | return new MMLmrow( TexClass::ORD, [], |
| 402 | new MMLmo( "", [ "lspace" => Sizes::THICKMATHSPACE, "rspace" => Sizes::THICKMATHSPACE ], "mod" ), |
| 403 | $inner, new MMLmrow( TexClass::ORD, [], $mspace ) ); |
| 404 | case "\\implies": |
| 405 | $mstyle = new MMLmstyle( "", [ "scriptlevel" => "0" ], |
| 406 | new MMLmspace( "", [ "width" => "0.278em" ] ) ); |
| 407 | return new MMLarray( $mstyle, ( new MMLmo( "", [], "⟹" ) ), $mstyle ); |
| 408 | case "\\iff": |
| 409 | $mstyle = new MMLmstyle( "", [ "scriptlevel" => "0" ], |
| 410 | new MMLmspace( "", [ "width" => "0.278em" ] ) ); |
| 411 | return new MMLarray( $mstyle, ( new MMLmo( "", [], "⟺" ) ), $mstyle ); |
| 412 | case "\\tripledash": |
| 413 | // Using emdash for rendering here. |
| 414 | return new MMLmo( "", [], "—" ); |
| 415 | case "\\longrightleftharpoons": |
| 416 | case "\\longLeftrightharpoons": |
| 417 | case "\\longRightleftharpoons": |
| 418 | $texvc = new TexVC(); |
| 419 | $warnings = []; |
| 420 | $checkRes = $texvc->check( $macro, [ "usemhchem" => true, "usemhchemtexified" => true ], |
| 421 | $warnings, true ); |
| 422 | return $checkRes["input"]->toMMLtree(); |
| 423 | case "\\longleftrightarrows": |
| 424 | // The tex-cmds used in makro are not supported, just use a hardcoded mml macro here. |
| 425 | $mover = MMLmover::newSubtree( |
| 426 | new MMLmrow( TexClass::OP, [], |
| 427 | new MMLmrow( TexClass::ORD, [], |
| 428 | new MMLmpadded( "", [ "height" => "0", "depth" => "0" ], |
| 429 | new MMLmo( "", [ "stretchy" => "false" ], "⟵" ) ) ), |
| 430 | new MMLmspace( "", [ "width" => "0px", "height" => ".25em", "depth" => "0px", |
| 431 | "mathbackground" => "black" ] ) ), |
| 432 | new MMLmrow( TexClass::ORD, [], |
| 433 | new MMLmo( "", [ "stretchy" => "false" ], "⟶" ) ) ); |
| 434 | return new MMLarray( |
| 435 | new MMLmtext( "", [], " " ), |
| 436 | new MMLmrow( TexClass::REL, [], $mover ) ); |
| 437 | } |
| 438 | |
| 439 | // Removed all token based parsing, since macro resolution for the supported macros can be hardcoded in php |
| 440 | return new MMLmerror( "", [], new MMLmtext( "", [], "macro not resolved: " . $macro ) ); |
| 441 | } |
| 442 | |
| 443 | public static function matrix( Matrix $node, $passedArgs, $operatorContent, |
| 444 | $name, $open = null, $close = null, $align = null, $spacing = null, |
| 445 | $vspacing = null, $style = null, $cases = null, $numbered = null ): MMLbase { |
| 446 | $resInner = []; |
| 447 | $tableArgs = [ "columnspacing" => "1em", "rowspacing" => "4pt" ]; |
| 448 | $boarder = $node->getBoarder(); |
| 449 | if ( $align ) { |
| 450 | $tableArgs['columnalign'] = $align; |
| 451 | } elseif ( $node->hasColumnInfo() ) { |
| 452 | $tableArgs['columnalign'] = $node->getAlignInfo(); |
| 453 | } |
| 454 | $rowNo = 0; |
| 455 | $lines = $node->getLines(); |
| 456 | foreach ( $node as $row ) { |
| 457 | $innerInnter = []; |
| 458 | $colNo = 0; |
| 459 | foreach ( $row as $cell ) { |
| 460 | $usedArg = clone $cell; |
| 461 | if ( $usedArg instanceof TexArray && |
| 462 | $usedArg->getLength() >= 1 && |
| 463 | $usedArg[0] instanceof Literal && |
| 464 | $usedArg[0]->getArg() === '\\hline ' |
| 465 | ) { |
| 466 | $usedArg->pop(); |
| 467 | |
| 468 | } |
| 469 | $mtdAttributes = []; |
| 470 | $texclass = $lines[$rowNo] ? TexClass::TOP : ''; |
| 471 | $texclass .= $lines[$rowNo + 1] ?? false ? ' ' . TexClass::BOTTOM : ''; |
| 472 | $texclass .= $boarder[$colNo] ?? false ? ' ' . TexClass::LEFT : ''; |
| 473 | $texclass .= $boarder[$colNo + 1 ] ?? false ? ' ' . TexClass::RIGHT : ''; |
| 474 | $texclass = trim( $texclass ); |
| 475 | if ( $texclass ) { |
| 476 | $mtdAttributes['class'] = $texclass; |
| 477 | } |
| 478 | $state = [ 'inMatrix' => true ]; |
| 479 | $innerInnter[] = new MMLmtd( "", $mtdAttributes, $usedArg->toMMLtree( $passedArgs, $state ) ); |
| 480 | $colNo++; |
| 481 | } |
| 482 | $resInner[] = new MMLmtr( "", [], ...$innerInnter ); |
| 483 | $rowNo++; |
| 484 | } |
| 485 | $mtable = new MMLmtable( "", $tableArgs ); |
| 486 | if ( $cases || ( $open != null && $close != null ) ) { |
| 487 | $bm = new BaseMethods(); |
| 488 | $mmlMoOpen = $bm->checkAndParseDelimiter( $open, $node, [], [], |
| 489 | true, TexClass::OPEN ); |
| 490 | if ( $mmlMoOpen == null ) { |
| 491 | $mmlMoOpen = new MMLmo( TexClass::OPEN, [], $open ?? '' ); |
| 492 | } |
| 493 | |
| 494 | $closeAtts = [ "fence" => "true", "stretchy" => "true", "symmetric" => "true" ]; |
| 495 | $mmlMoClose = $bm->checkAndParseDelimiter( $close, $node, $closeAtts, |
| 496 | null, true, TexClass::CLOSE ); |
| 497 | if ( $mmlMoClose == null ) { |
| 498 | $mmlMoClose = ( new MMLmo( TexClass::CLOSE, $closeAtts, $close ?? '' ) ); |
| 499 | } |
| 500 | $mtable->addChild( ...$resInner ); |
| 501 | return new MMLmrow( TexClass::ORD, [], $mmlMoOpen, $mtable, $mmlMoClose ); |
| 502 | } |
| 503 | $mtable->addChild( ...$resInner ); |
| 504 | return $mtable; |
| 505 | } |
| 506 | |
| 507 | public static function namedOp( $node, $passedArgs, $operatorContent, $name, $id = null ): MMLbase { |
| 508 | /* Determine whether the named function should have an added apply function. The operatorContent is defined |
| 509 | as state in parsing of TexArray */ |
| 510 | $applyFct = self::getApplyFct( $operatorContent ); |
| 511 | |
| 512 | if ( $node instanceof Literal ) { |
| 513 | return new MMLarray( new MMLmi( "", $passedArgs, $id ?? ltrim( $name, '\\' ) ), $applyFct ); |
| 514 | } |
| 515 | return MMLmsub::newSubtree( $node->getBase()->toMMLtree() . $applyFct, |
| 516 | new MMLmrow( TexClass::ORD, [], $node->getDown()->toMMLtree() ), "", $passedArgs ); |
| 517 | } |
| 518 | |
| 519 | public static function over( $node, $passedArgs, $operatorContent, $name, $id = null ): MMLbase { |
| 520 | $attributes = []; |
| 521 | $start = null; |
| 522 | $tail = null; |
| 523 | if ( trim( $name ) === "\\atop" ) { |
| 524 | $attributes = [ "linethickness" => "0" ]; |
| 525 | } elseif ( trim( $name ) == "\\choose" ) { |
| 526 | $start = new MMLmrow( TexClass::OPEN, [], |
| 527 | ( new MMLmo( "", [ "maxsize" => "1.2em", "minsize" => "1.2em" ], "(" ) ) ); |
| 528 | $tail = new MMLmrow( TexClass::CLOSE, [], |
| 529 | ( new MMLmo( "", [ "maxsize" => "1.2em", "minsize" => "1.2em" ], ")" ) ) ); |
| 530 | $attributes = [ "linethickness" => "0" ]; |
| 531 | } |
| 532 | if ( $node instanceof Fun2 ) { |
| 533 | $mfrac = MMLmfrac::newSubtree( new MMLmrow( "", [], $node->getArg1()->toMMLtree() ), |
| 534 | new MMLmrow( "", [], $node->getArg2()->toMMLtree() ), "", $attributes ); |
| 535 | if ( $start === null ) { |
| 536 | return $mfrac; |
| 537 | } |
| 538 | return new MMLmrow( TexClass::ORD, [], $start, $mfrac, $tail ); |
| 539 | } |
| 540 | $inner = []; |
| 541 | foreach ( $node->getArgs() as $arg ) { |
| 542 | if ( is_string( $arg ) && str_contains( $arg, $name ) ) { |
| 543 | continue; |
| 544 | } |
| 545 | $rendered = $arg instanceof TexNode ? $arg->toMMLtree() : $arg; |
| 546 | $inner[] = new MMLmrow( "", [], $rendered ); |
| 547 | } |
| 548 | $mfrac = MMLmfrac::newSubtree( $inner[0], $inner[1], "", $attributes ); |
| 549 | if ( $start === null ) { |
| 550 | return $mfrac; |
| 551 | } |
| 552 | return new MMLmrow( TexClass::ORD, [], $start, $mfrac, $tail ); |
| 553 | } |
| 554 | |
| 555 | public static function oint( $node, $passedArgs, $operatorContent, |
| 556 | $name, $uc = null, $attributes = null, $smth2 = null ): MMLbase { |
| 557 | // This is a custom mapping not in js. |
| 558 | switch ( trim( $name ) ) { |
| 559 | case "\\oint": |
| 560 | return new MMLmstyle( "", [ "displaystyle" => "true" ], |
| 561 | new MMLmo( "", [], MMLutil::uc2xNotation( $uc ) ) ); |
| 562 | case "\\P": |
| 563 | return new MMLmo( "", [], MMLutil::uc2xNotation( $uc ) ); |
| 564 | case "\\oiint": |
| 565 | case "\\oiiint": |
| 566 | case "\\ointctrclockwise": |
| 567 | case "\\varointclockwise": |
| 568 | return new MMLmrow( TexClass::ORD, [], |
| 569 | new MMLmstyle( "", [ "mathsize" => "2.07em" ], |
| 570 | new MMLmtext( "", $attributes, MMLutil::uc2xNotation( $uc ) ), |
| 571 | new MMLmspace( "", [ "width" => Sizes::THINMATHSPACE ] ) ) ); |
| 572 | default: |
| 573 | return new MMLmerror( "", [], new MMLmtext( "", [], "not found in OintMethod" ) ); |
| 574 | } |
| 575 | } |
| 576 | |
| 577 | public static function overset( $node, $passedArgs, $operatorContent, $name, $id = null ): MMLbase { |
| 578 | if ( $node instanceof DQ ) { |
| 579 | return new MMLmrow( TexClass::ORD, [], MMLmover::newSubtree( new MMLmrow( "", [], |
| 580 | $node->getDown()->toMMLtree() ), $node->getDown()->toMMLtree() ) ); |
| 581 | } |
| 582 | return new MMLmrow( TexClass::ORD, [], |
| 583 | MMLmover::newSubtree( new MMLmrow( "", [], |
| 584 | $node->getArg2()->toMMLtree() ), $node->getArg1()->toMMLtree() ) ); |
| 585 | } |
| 586 | |
| 587 | public static function phantom( $node, $passedArgs, $operatorContent, |
| 588 | $name, $vertical = null, $horizontal = null, $smh3 = null ): MMLbase { |
| 589 | $attrs = []; |
| 590 | if ( $vertical ) { |
| 591 | $attrs = array_merge( $attrs, [ "width" => "0" ] ); |
| 592 | } |
| 593 | if ( $horizontal ) { |
| 594 | $attrs = array_merge( $attrs, [ "depth" => "0", "height" => "0" ] ); |
| 595 | } |
| 596 | return new MMLmrow( TexClass::ORD, [], new MMLmrow( TexClass::ORD, [], |
| 597 | new MMLmpadded( "", $attrs, new MMLmphantom( "", [], $node->getArg()->toMMLtree() ) ) ) ); |
| 598 | } |
| 599 | |
| 600 | public static function raiseLower( $node, $passedArgs, $operatorContent, $name ): ?MMLbase { |
| 601 | if ( !$node instanceof Fun2 ) { |
| 602 | return null; |
| 603 | } |
| 604 | |
| 605 | $arg1 = $node->getArg1(); |
| 606 | // the second check is to avoid a false positive for PhanTypeMismatchArgumentSuperType |
| 607 | if ( $arg1->isCurly() && $arg1 instanceof TexArray ) { |
| 608 | $unit = MMLutil::squashLitsToUnit( $arg1 ); |
| 609 | if ( !$unit ) { |
| 610 | return null; |
| 611 | } |
| 612 | $em = MMLutil::dimen2em( $unit ); |
| 613 | if ( !$em ) { |
| 614 | return null; |
| 615 | } |
| 616 | } else { |
| 617 | return null; |
| 618 | } |
| 619 | |
| 620 | if ( trim( $name ) === "\\raise" ) { |
| 621 | $args = [ "height" => MMLutil::addPreOperator( $em, "+" ), |
| 622 | "depth" => MMLutil::addPreOperator( $em, "-" ), |
| 623 | "voffset" => MMLutil::addPreOperator( $em, "+" ) ]; |
| 624 | } elseif ( trim( $name ) === "\\lower" ) { |
| 625 | $args = [ "height" => MMLutil::addPreOperator( $em, "-" ), |
| 626 | "depth" => MMLutil::addPreOperator( $em, "+" ), |
| 627 | "voffset" => MMLutil::addPreOperator( $em, "-" ) ]; |
| 628 | } else { |
| 629 | // incorrect name, should not happen, prevent erroneous mappings from getting rendered. |
| 630 | return null; |
| 631 | } |
| 632 | return new MMLmrow( "", [], new MMLmpadded( "", $args, $node->getArg2()->toMMLtree() ) ); |
| 633 | } |
| 634 | |
| 635 | public static function underset( $node, $passedArgs, $operatorContent, $name, $smh = null ): MMLbase { |
| 636 | $inrow = $node->getArg2()->toMMLtree(); |
| 637 | $arg1 = $node->getArg1()->toMMLtree(); |
| 638 | // MMLarray may be empty |
| 639 | if ( $inrow->hasChildren() && $arg1->hasChildren() ) { |
| 640 | return new MMLmrow( TexClass::ORD, [], MMLmunder::newSubtree( $inrow, $arg1 ) ); |
| 641 | } |
| 642 | |
| 643 | // If there are no two elements in munder, not render munder |
| 644 | return new MMLmrow( TexClass::ORD, [], $inrow, $arg1 ); |
| 645 | } |
| 646 | |
| 647 | public static function underOver( Fun1 $node, $passedArgs, $operatorContent, |
| 648 | $name, $operatorId = null, $stack = null, $nonHex = false ): MMLbase { |
| 649 | // tbd verify if stack interpreted correctly ? |
| 650 | $texClass = $stack ? TexClass::OP : TexClass::ORD; // ORD or "" |
| 651 | |
| 652 | $fname = $node->getFname(); |
| 653 | if ( str_starts_with( $fname, '\\over' ) ) { |
| 654 | $movun = new MMLmover(); |
| 655 | } elseif ( str_starts_with( $fname, '\\under' ) ) { |
| 656 | $movun = new MMLmunder(); |
| 657 | } else { |
| 658 | // incorrect name, should not happen, prevent erroneous mappings from getting rendered. |
| 659 | return new MMLmerror( "", [], |
| 660 | new MMLmtext( "", [], 'underOver rendering requires macro to start with either \\under or \\over.' ) ); |
| 661 | } |
| 662 | |
| 663 | $inner = $nonHex ? $operatorId : MMLutil::number2xNotation( $operatorId ); |
| 664 | if ( $operatorId == 2015 ) { // eventually move such cases to mapping |
| 665 | $mo = new MMLmo( "", [ "accent" => "true" ], $inner ); |
| 666 | } else { |
| 667 | $mo = new MMLmo( "", [], $inner ); |
| 668 | } |
| 669 | return new MMLmrow( $texClass, [], $movun::newSubtree( $node->getArg()->toMMLtree( $passedArgs ), $mo ) ); |
| 670 | } |
| 671 | |
| 672 | public static function mathFont( $node, $passedArgs, $operatorContent, $name, $mathvariant = null ): MMLbase { |
| 673 | $args = MMLParsingUtil::getFontArgs( $name, $mathvariant, $passedArgs ); |
| 674 | $state = []; |
| 675 | |
| 676 | return new MMLmrow( TexClass::ORD, [], $node->getArg()->toMMLtree( $args, $state ) ); |
| 677 | } |
| 678 | |
| 679 | public static function mathChoice( $node, $passedArgs, $operatorContent, $name, $smth = null ) { |
| 680 | if ( !$node instanceof Fun4 ) { |
| 681 | return new MMLmerror( "", [], new MMLmtext( "", [], "Wrong node type in mathChoice" ) ); |
| 682 | } |
| 683 | |
| 684 | /** |
| 685 | * Parametrization for mathchoice: |
| 686 | * \mathchoice |
| 687 | * {<material for display style>} |
| 688 | * {<material for text style>} |
| 689 | * {<material for script style>} |
| 690 | * {<material for scriptscript style>} |
| 691 | */ |
| 692 | |
| 693 | if ( isset( $operatorContent["styleargs"] ) ) { |
| 694 | $styleArgs = $operatorContent["styleargs"]; |
| 695 | $displayStyle = $styleArgs["displaystyle"] ?? "true"; |
| 696 | $scriptLevel = $styleArgs["scriptlevel"] ?? "0"; |
| 697 | |
| 698 | if ( $displayStyle == "true" && $scriptLevel == "0" ) { |
| 699 | // This is displaystyle |
| 700 | return $node->getArg1()->toMMLtree( $passedArgs, $operatorContent ); |
| 701 | } elseif ( $displayStyle == "false" && $scriptLevel == "0" ) { |
| 702 | // This is textstyle |
| 703 | return $node->getArg2()->toMMLtree( $passedArgs, $operatorContent ); |
| 704 | } elseif ( $displayStyle == "false" && $scriptLevel == "1" ) { |
| 705 | // This is scriptstyle |
| 706 | return $node->getArg3()->toMMLtree( $passedArgs, $operatorContent ); |
| 707 | } elseif ( $displayStyle == "false" && $scriptLevel == "2" ) { |
| 708 | // This is scriptscriptstyle |
| 709 | return $node->getArg4()->toMMLtree( $passedArgs, $operatorContent ); |
| 710 | } |
| 711 | } |
| 712 | // By default render displaystyle |
| 713 | return $node->getArg1()->toMMLtree( $passedArgs, $operatorContent ); |
| 714 | } |
| 715 | |
| 716 | public static function makeBig( $node, $passedArgs, $operatorContent, $name, $texClass = null, |
| 717 | $size = null ): ?MMLbase { |
| 718 | // Create the em format and shorten commas |
| 719 | $size *= Misc::P_HEIGHT; |
| 720 | $sizeShortened = MMLutil::size2em( strval( $size ) ); |
| 721 | $passedArgs = array_merge( $passedArgs, [ "maxsize" => $sizeShortened, "minsize" => $sizeShortened ] ); |
| 722 | // Sieve arg if it is a delimiter (it seems args are not applied here |
| 723 | $bm = new BaseMethods(); |
| 724 | $argcurrent = trim( $node->getArg() ); |
| 725 | switch ( $argcurrent ) { |
| 726 | case "\\|": |
| 727 | case "|": |
| 728 | $passedArgs = array_merge( $passedArgs, [ "stretchy" => "true", "symmetric" => "true" ] ); |
| 729 | break; |
| 730 | case "\\uparrow": |
| 731 | case "\\downarrow": |
| 732 | case "\\Uparrow": |
| 733 | case "\\Downarrow": |
| 734 | case "\\updownarrow": |
| 735 | case "/": |
| 736 | case "\\backslash": |
| 737 | case "\\Updownarrow": |
| 738 | $passedArgs = array_merge( |
| 739 | [ "fence" => "true" ], |
| 740 | $passedArgs, |
| 741 | [ "stretchy" => "true", "symmetric" => "true" ] ); |
| 742 | break; |
| 743 | } |
| 744 | |
| 745 | if ( in_array( $name, [ "\\bigl", "\\Bigl", "\\biggl", "\\Biggl" ] ) ) { |
| 746 | $passedArgs = array_merge( $passedArgs, [ Tag::CLASSTAG => TexClass::OPEN ] ); |
| 747 | } |
| 748 | |
| 749 | if ( in_array( $name, [ "\\bigr", "\\Bigr", "\\biggr", "\\Biggr" ] ) ) { |
| 750 | $passedArgs = array_merge( $passedArgs, [ Tag::CLASSTAG => TexClass::CLOSE ] ); |
| 751 | } |
| 752 | |
| 753 | $ret = $bm->checkAndParseDelimiter( $node->getArg(), $node, $passedArgs, $operatorContent, true ); |
| 754 | if ( $ret ) { |
| 755 | return $ret; |
| 756 | } |
| 757 | |
| 758 | $argPrep = $node->getArg(); |
| 759 | return new MMLmrow( TexClass::ORD, [], |
| 760 | new MMLmrow( $texClass, [], new MMLmo( "", $passedArgs, $argPrep ) ) ); |
| 761 | } |
| 762 | |
| 763 | public static function machine( $node, $passedArgs, $operatorContent, $name, $type = null ): MMLbase { |
| 764 | // this could also be shifted to MhChem.php renderMML for ce |
| 765 | // For parsing chem (ce) or ??? (pu) |
| 766 | return new MMLmrow( "", [], $node->getArg()->toMMLtree() ); |
| 767 | } |
| 768 | |
| 769 | public static function namedFn( $node, $passedArgs, $operatorContent, $name, $smth = null ): MMLbase { |
| 770 | // Determine wether the named function should have an added apply function. The state is defined in |
| 771 | // parsing of TexArray |
| 772 | $applyFct = self::getApplyFct( $operatorContent ); |
| 773 | if ( $node instanceof Literal ) { |
| 774 | return new MMLarray( new MMLmi( "", [], ltrim( $name, '\\' ) ), $applyFct ); |
| 775 | } |
| 776 | return MMLmsub::newSubtree( $node->getBase()->toMMLtree() . $applyFct, |
| 777 | new MMLmrow( TexClass::ORD, [], $node->getDown()->toMMLtree() ) ); |
| 778 | } |
| 779 | |
| 780 | public static function limits( $node, $passedArgs, $operatorContent, $name, $smth = null ): ?MMLbase { |
| 781 | $argsOp = [ 'form' => 'prefix' ]; |
| 782 | if ( isset( $operatorContent['styleargs'] ) ) { |
| 783 | $displaystyle = $operatorContent['styleargs']['displaystyle'] ?? 'true'; |
| 784 | if ( $displaystyle === 'false' ) { |
| 785 | $argsOp['movablelimits'] = 'true'; |
| 786 | } |
| 787 | if ( $node->containsFunc( '\\nolimits' ) ) { |
| 788 | $argsOp['movablelimits'] = 'false'; |
| 789 | } |
| 790 | } |
| 791 | $opParsed = ( $operatorContent["limits"] ?? false ) |
| 792 | ? $operatorContent["limits"]->toMMLtree( $argsOp ) : ""; |
| 793 | |
| 794 | if ( $node instanceof DQ ) { |
| 795 | return MMLmunder::newSubtree( $opParsed, |
| 796 | new MMLmrow( TexClass::ORD, [], $node->getDown()->toMMLtree() ) ); |
| 797 | } elseif ( $node instanceof FQ ) { |
| 798 | $munderOver = MMLmunderover::newSubtree( |
| 799 | $opParsed, new MMLmrow( TexClass::ORD, [], $node->getDown()->toMMLtree() ), |
| 800 | new MMLmrow( TexClass::ORD, [], $node->getUp()->toMMLtree() ) ); |
| 801 | return $munderOver; |
| 802 | } |
| 803 | // Don't render limits |
| 804 | return null; |
| 805 | } |
| 806 | |
| 807 | public static function setFont( $node, $passedArgs, $operatorContent, $name, $variant = null ): MMLbase { |
| 808 | return self::mathFont( $node, $passedArgs, $operatorContent, $name, $variant ); |
| 809 | } |
| 810 | |
| 811 | public static function sideset( $node, $passedArgs, $operatorContent, $name ): MMLbase { |
| 812 | if ( !array_key_exists( "sideset", $operatorContent ) ) { |
| 813 | return new MMLmerror( "", [], |
| 814 | new MMLmerror( "", [], "Error parsing sideset expression, no succeeding operator found" ) ); |
| 815 | } |
| 816 | |
| 817 | if ( $operatorContent["sideset"] instanceof Literal ) { |
| 818 | $bm = new BaseMethods(); |
| 819 | $opParsed = $bm->checkAndParseOperator( $operatorContent["sideset"]->getArg(), null, [], [], null ); |
| 820 | if ( $opParsed === null ) { |
| 821 | throw new \LogicException( "null is not a valid base for MMLmmultiscripts." ); |
| 822 | } |
| 823 | $in1 = $node->getArg1()->toMMLtree(); |
| 824 | $in2 = $node->getArg2()->toMMLtree(); |
| 825 | return new MMLmrow( TexClass::OP, [], |
| 826 | MMLmmultiscripts::newSubtree( $opParsed, $in2, null, $in1, null, |
| 827 | "", [ Tag::ALIGN => "left" ] |
| 828 | ) ); |
| 829 | } |
| 830 | |
| 831 | if ( $operatorContent["sideset"] instanceof FQ || |
| 832 | $operatorContent["sideset"] instanceof DQ || |
| 833 | $operatorContent["sideset"] instanceof UQ ) { |
| 834 | $bm = new BaseMethods(); |
| 835 | if ( count( $operatorContent["sideset"]->getBase()->getArgs() ) == 1 ) { |
| 836 | $baseOperator = $operatorContent["sideset"]->getBase()->getArgs()[0]; |
| 837 | $opParsed = $bm->checkAndParseOperator( $baseOperator, |
| 838 | null, [ "largeop" => "true", "movablelimits" => "false", "symmetric" => "true" ], [], null ); |
| 839 | if ( $opParsed == null ) { |
| 840 | $opParsed = $operatorContent["sideset"]->getBase()->toMMLtree() ?? ""; |
| 841 | } |
| 842 | } else { |
| 843 | $opParsed = new MMLmerror( "", [], |
| 844 | new MMLmtext( "", [], "Sideset operator parsing not implemented yet" ) ); |
| 845 | } |
| 846 | $state = [ 'sideset' => true ]; |
| 847 | $in1 = $node->getArg1()->toMMLtree( [], $state ); |
| 848 | $in2 = $node->getArg2()->toMMLtree( [], $state ); |
| 849 | |
| 850 | $down = $operatorContent["sideset"] instanceof UQ ? '<mrow />' : |
| 851 | $operatorContent["sideset"]->getDown()->toMMLtree(); |
| 852 | $end1 = new MMLmrow( "", [], $down ); |
| 853 | $up = $operatorContent["sideset"] instanceof DQ ? '<mrow />' : |
| 854 | $operatorContent["sideset"]->getUp()->toMMLtree(); |
| 855 | $end2 = new MMLmrow( "", [], $up ); |
| 856 | |
| 857 | return new MMLmrow( TexClass::OP, [], |
| 858 | MMLmunderover::newSubtree( new MMLmstyle( "", [ "displaystyle" => "true" ], |
| 859 | MMLmmultiscripts::newSubtree( $opParsed, $in2, null, $in1 ) ), $end1, $end2 ) ); |
| 860 | } |
| 861 | |
| 862 | return new MMLmerror( "", [], |
| 863 | new MMLmtext( "", [], "Error parsing sideset expression, no valid succeeding operator found" ) ); |
| 864 | } |
| 865 | |
| 866 | public static function spacer( $node, $passedArgs, $operatorContent, $name, $withIn = null, $smth2 = null |
| 867 | ): MMLbase { |
| 868 | return new MMLmspace( "", [ "width" => MMLutil::round2em( $withIn ) ] ); |
| 869 | } |
| 870 | |
| 871 | public static function smash( $node, $passedArgs, $operatorContent, $name ): MMLbase { |
| 872 | $mpArgs = []; |
| 873 | $inner = ""; |
| 874 | if ( $node instanceof Fun2sq ) { |
| 875 | $arg1 = $node->getArg1(); |
| 876 | $arg1i = ""; |
| 877 | if ( $arg1->isCurly() ) { |
| 878 | $arg1i = $arg1->render(); |
| 879 | } |
| 880 | |
| 881 | if ( str_contains( $arg1i, "{b}" ) ) { |
| 882 | $mpArgs = [ "depth" => "0" ]; |
| 883 | } |
| 884 | if ( str_contains( $arg1i, "{t}" ) ) { |
| 885 | $mpArgs = [ "height" => "0" ]; |
| 886 | } |
| 887 | if ( str_contains( $arg1i, "{tb}" ) || str_contains( $arg1i, "{bt}" ) ) { |
| 888 | $mpArgs = [ "height" => "0", "depth" => "0" ]; |
| 889 | } |
| 890 | |
| 891 | $inner = $node->getArg2()->toMMLtree() ?? ""; |
| 892 | } elseif ( $node instanceof Fun1 ) { |
| 893 | // Implicitly assume "tb" as default mode |
| 894 | $mpArgs = [ "height" => "0", "depth" => "0" ]; |
| 895 | $inner = $node->getArg()->toMMLtree() ?? ""; |
| 896 | } |
| 897 | return new MMLmrow( TexClass::ORD, [], new MMLmpadded( "", $mpArgs, $inner ) ); |
| 898 | } |
| 899 | |
| 900 | public static function texAtom( $node, $passedArgs, $operatorContent, $name, $texClass = null ): MMLbase { |
| 901 | switch ( $name ) { |
| 902 | case "mathclose": |
| 903 | $inner = $node->getArg()->toMMLtree(); |
| 904 | return new MMLmrow( TexClass::ORD, [], new MMLmrow( $texClass, [], $inner ) ); |
| 905 | case "mathbin": |
| 906 | // no break |
| 907 | case "mathop": |
| 908 | // no break |
| 909 | case "mathrel": |
| 910 | $inner = $node->getArg()->toMMLtree(); |
| 911 | return new MMLmrow( $texClass, [], $inner ); |
| 912 | default: |
| 913 | $inner = $node->getArg()->toMMLtree(); |
| 914 | return new MMLmrow( TexClass::ORD, [], new MMLmrow( $texClass, [], $inner ) ); |
| 915 | } |
| 916 | } |
| 917 | |
| 918 | public static function intent( $node, $passedArgs, $operatorContent, $name, $smth = null ) { |
| 919 | if ( !$node instanceof Fun2 ) { |
| 920 | return null; |
| 921 | } |
| 922 | // if there is intent annotation add intent to root element |
| 923 | // match args in row of subargs, unless an element has explicit annotations |
| 924 | // nested annotations ? |
| 925 | $arg1 = $node->getArg1(); |
| 926 | $arg2 = $node->getArg2(); |
| 927 | if ( !$arg2->isCurly() ) { |
| 928 | return null; |
| 929 | } |
| 930 | // tbd refactor intent form and fiddle in mml or tree |
| 931 | $intentStr = MMLutil::squashLitsToUnitIntent( $arg2 ); |
| 932 | $intentContent = MMLParsingUtil::getIntentContent( $intentStr ); |
| 933 | $intentParams = MMLParsingUtil::getIntentParams( $intentContent ); |
| 934 | // Sometimes the intent has additioargs = {array[3]} nal args in the same string |
| 935 | $intentArg = MMLParsingUtil::getIntentArgs( $intentStr ); |
| 936 | if ( !$intentContent && !$intentParams && $intentArg !== null ) { |
| 937 | // explicit args annotation parsing in literal |
| 938 | // return $arg1->renderMML([],["intent-params-expl"=>$intentArg]); |
| 939 | // alternative just add the arg here |
| 940 | return $arg1->toMMLtree( [ "arg" => $intentArg ] ); |
| 941 | } |
| 942 | $intentContentAtr = [ "intent" => $intentContent ]; |
| 943 | if ( $intentArg !== null ) { |
| 944 | $intentContentAtr["arg"] = $intentArg; |
| 945 | } |
| 946 | // tbd refine intent params and operator content merging (does it overwrite ??) |
| 947 | $intentParamsState = $intentParams ? [ "intent-params" => $intentParams ] : $operatorContent; |
| 948 | // Here are some edge cases, they might go into renderMML in the related element |
| 949 | if ( str_contains( $intentContent ?? '', "matrix" ) || |
| 950 | ( $arg1->isCurly() && $arg1->getArgs()[0] instanceof Matrix ) ) { |
| 951 | $element = $arg1->getArgs()[0]; |
| 952 | $rendered = $element->toMMLtree( [], $intentParamsState ); |
| 953 | $hackyXML = MMLParsingUtil::forgeIntentToSpecificElement( $rendered, |
| 954 | $intentContentAtr, "mtable" ); |
| 955 | return $hackyXML; |
| 956 | } elseif ( $arg1->isCurly() && count( $arg1->getArgs() ) >= 2 ) { |
| 957 | // Create a surrounding element which holds the intents |
| 958 | return new MMLmrow( "", $intentContentAtr, $arg1->toMMLtree( [], $intentParamsState ) ); |
| 959 | } elseif ( $arg1->isCurly() && count( $arg1->getArgs() ) >= 1 ) { |
| 960 | // Forge the intent attribute to the top-level element after MML rendering |
| 961 | $element = $arg1->getArgs()[0]; |
| 962 | $rendered = $element->toMMLtree( [], $intentParamsState ); |
| 963 | $hackyXML = MMLParsingUtil::forgeIntentToTopElement( $rendered, $intentContentAtr ); |
| 964 | return $hackyXML; |
| 965 | } else { |
| 966 | // This is the default case |
| 967 | return $arg1->toMMLtree( $intentContentAtr, $intentParamsState ); |
| 968 | } |
| 969 | } |
| 970 | |
| 971 | public static function hBox( $node, $passedArgs, $operatorContent, $name, $smth = null ): MMLbase { |
| 972 | switch ( trim( $name ) ) { |
| 973 | case "\\mbox": |
| 974 | if ( isset( $operatorContent['foundOC'] ) ) { |
| 975 | $op = $operatorContent['foundOC']; |
| 976 | $macro = TexUtil::getInstance()->nullary_macro_in_mbox( $op ) ? |
| 977 | /* tested in \MediaWiki\Extension\Math\Tests\WikiTexVC\TexUtilTest::testUnicodeDefined */ |
| 978 | [ '&#x' . TexUtil::getInstance()->unicode_char( $op ) . ';' ] : |
| 979 | TexUtil::getInstance()->identifier( $op ); |
| 980 | $input = $macro[0] ?? $op; |
| 981 | // @phan-suppress-next-line PhanTypeMismatchArgumentNullable - false positive see above |
| 982 | return new MMLmrow( TexClass::ORD, [], new MMLmo( "", [], MMLutil::uc2xNotation( $input ) ) ); |
| 983 | } else { |
| 984 | return new MMLmrow( TexClass::ORD, [], new MMLmtext( "", [], "\mbox" ) ); |
| 985 | } |
| 986 | case "\\hbox": |
| 987 | $inner = $node->getArg() instanceof TexNode ? $node->getArg()->toMMLtree() : $node->getArg(); |
| 988 | return new MMLmrow( TexClass::ORD, [], |
| 989 | new MMLmstyle( "", [ "displaystyle" => "false", "scriptlevel" => "0" ], |
| 990 | new MMLmtext( "", [], $inner ) |
| 991 | ) |
| 992 | ); |
| 993 | case "\\text": |
| 994 | $inner = $node->getArg() instanceof TexNode ? $node->getArg()->toMMLtree() : $node->getArg(); |
| 995 | return new MMLmrow( TexClass::ORD, [], new MMLmtext( "", [], $inner ) ); |
| 996 | case "\\textbf": |
| 997 | // no break |
| 998 | case "\\textit": |
| 999 | // no break |
| 1000 | case "\\textrm": |
| 1001 | // no break |
| 1002 | case "\\textsf": |
| 1003 | // no break |
| 1004 | case "\\texttt": |
| 1005 | $state = [ "inHBox" => true, 'squashLiterals' => true ]; |
| 1006 | $inner = $node->getArg()->isCurly() ? $node->getArg()->toMMLtree( |
| 1007 | [], $state ) |
| 1008 | : $node->getArg()->toMMLtree( [ "fromHBox" => true ] ); |
| 1009 | return new MMLmtext( "", |
| 1010 | MMLParsingUtil::getFontArgs( $name, null, null ), |
| 1011 | $inner ?? '' ); |
| 1012 | |
| 1013 | } |
| 1014 | |
| 1015 | return new MMLmerror( "", [], new MMLmtext( "", [], "undefined hbox" ) ); |
| 1016 | } |
| 1017 | |
| 1018 | public static function setStyle( $node, $passedArgs, $operatorContent, $name, |
| 1019 | $smth = null, $smth1 = null, $smth2 = null ) { |
| 1020 | // Just discard setstyle since they are captured in TexArray now} |
| 1021 | return " "; |
| 1022 | } |
| 1023 | |
| 1024 | public static function not( $node, $passedArgs, $operatorContent, $name, $smth = null, |
| 1025 | $smth1 = null, $smth2 = null ): MMLbase { |
| 1026 | // This is only tested for \not statement without follow-up parameters |
| 1027 | if ( $node instanceof Literal ) { |
| 1028 | return MMLParsingUtil::createNot(); |
| 1029 | } |
| 1030 | return new MMLmerror( "", [], new MMLmtext( "", [], "TBD implement not" ) ); |
| 1031 | } |
| 1032 | |
| 1033 | public static function vbox( $node, $passedArgs, $operatorContent, $name, $smth = null ): MMLbase { |
| 1034 | // This is only example functionality for vbox("ab"). |
| 1035 | // TBD: it should be discussed if vbox is supported since it |
| 1036 | // does not seem to be supported by mathjax |
| 1037 | if ( is_string( $node->getArg() ) ) { |
| 1038 | $arr1 = str_split( $node->getArg() ); |
| 1039 | $inner = []; |
| 1040 | foreach ( $arr1 as $char ) { |
| 1041 | $inner[] = new MMLmrow( TexClass::ORD, [], $char ); |
| 1042 | } |
| 1043 | return MMLmover::newSubtree( $inner[0], $inner[1] ); |
| 1044 | } |
| 1045 | return new MMLmerror( "", [], new MMLmtext( "", [], "no implemented vbox" ) ); |
| 1046 | } |
| 1047 | |
| 1048 | public static function sqrt( $node, $passedArgs, $operatorContent, $name ): MMLbase { |
| 1049 | // There is an additional argument for the root |
| 1050 | if ( $node instanceof Fun2sq ) { |
| 1051 | // In case of an empty curly add an mrow |
| 1052 | $arg2Rendered = $node->getArg2()->toMMLtree( $passedArgs ); |
| 1053 | if ( trim( $arg2Rendered ) === "" || $arg2Rendered === null ) { |
| 1054 | $arg2Rendered = new MMLmrow( TexClass::ORD, [] ); |
| 1055 | } |
| 1056 | return new MMLmrow( TexClass::ORD, [], |
| 1057 | MMLmroot::newSubtree( |
| 1058 | $arg2Rendered, |
| 1059 | new MMLmrow( TexClass::ORD, [], |
| 1060 | $node->getArg1()->toMMLtree( $passedArgs ) |
| 1061 | ) |
| 1062 | ) |
| 1063 | ); |
| 1064 | } |
| 1065 | // Currently this is own implementation from Fun1.php |
| 1066 | return new MMLmrow( TexClass::ORD, [], // assuming that this is always encapsulated in mrow |
| 1067 | new MMLmsqrt( "", [], |
| 1068 | $node->getArg()->toMMLtree( $passedArgs ) |
| 1069 | ) |
| 1070 | ); |
| 1071 | } |
| 1072 | |
| 1073 | public static function tilde( $node, $passedArgs, $operatorContent, $name ): MMLbase { |
| 1074 | return new MMLmspace( "", [ "width" => "0.5em" ] ); |
| 1075 | } |
| 1076 | |
| 1077 | public static function xArrow( $node, $passedArgs, $operatorContent, $name, $chr = null, $l = null, |
| 1078 | $r = null ): MMLbase { |
| 1079 | $defWidth = "+" . MMLutil::round2em( ( $l + $r ) / 18 ); |
| 1080 | $defLspace = MMLutil::round2em( $l / 18 ); |
| 1081 | |
| 1082 | $char = IntlChar::chr( $chr ); |
| 1083 | |
| 1084 | $mpaddedArgs = [ "height" => "-.2em", "lspace" => $defLspace, "voffset" => "-.2em", "width" => $defWidth ]; |
| 1085 | $mspace = new MMLmspace( "", [ "depth" => ".25em" ] ); |
| 1086 | if ( $node instanceof Fun2sq ) { |
| 1087 | return new MMLmrow( TexClass::ORD, [], MMLmunderover::newSubtree( |
| 1088 | new MMLmstyle( "", [ "scriptlevel" => "0" ], new MMLmo( Texclass::REL, [], $char ) ), |
| 1089 | new MMLmpadded( "", $mpaddedArgs, |
| 1090 | new MMLmrow( TexClass::ORD, [], |
| 1091 | $node->getArg1()->toMMLtree() |
| 1092 | ), |
| 1093 | $mspace |
| 1094 | ), |
| 1095 | new MMLmpadded( "", $mpaddedArgs, |
| 1096 | $node->getArg2()->toMMLtree() |
| 1097 | ) |
| 1098 | ) ); |
| 1099 | |
| 1100 | } |
| 1101 | return MMLmover::newSubtree( |
| 1102 | new MMLmstyle( "", [ "scriptlevel" => "0" ], new MMLmo( Texclass::REL, [], $char ) ), |
| 1103 | new MMLmpadded( "", $mpaddedArgs, $node->getArg()->toMMLtree(), $mspace ) |
| 1104 | ); |
| 1105 | } |
| 1106 | |
| 1107 | private static function getApplyFct( array $operatorContent ): ?MMLbase { |
| 1108 | $applyFct = null; |
| 1109 | if ( array_key_exists( "foundNamedFct", $operatorContent ) ) { |
| 1110 | $hasNamedFct = $operatorContent['foundNamedFct'][0]; |
| 1111 | $hasValidParameters = $operatorContent["foundNamedFct"][1]; |
| 1112 | if ( $hasNamedFct && $hasValidParameters ) { |
| 1113 | $applyFct = MMLParsingUtil::renderApplyFunction(); |
| 1114 | } |
| 1115 | } |
| 1116 | return $applyFct; |
| 1117 | } |
| 1118 | } |