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