Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 90
0.00% covered (danger)
0.00%
0 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
MMLutil
0.00% covered (danger)
0.00%
0 / 90
0.00% covered (danger)
0.00%
0 / 15
2256
0.00% covered (danger)
0.00%
0 / 1
 initalParseLiteralExpression
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 uc2xNotation
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 x2uNotation
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 number2xNotation
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 round2em
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 size2em
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 squashLitsToUnit
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 addPreOperator
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
90
 dimen2em
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
30
 inputPreparation
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
 createEntity
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMappingByKey
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 getMappingByKeySimple
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 squashLitsToUnitIntent
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
56
 removeDollarEscaping
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2namespace MediaWiki\Extension\Math\WikiTexVC\MMLmappings\Util;
3
4use IntlChar;
5use MediaWiki\Extension\Math\WikiTexVC\Nodes\Curly;
6use MediaWiki\Extension\Math\WikiTexVC\Nodes\DQ;
7use MediaWiki\Extension\Math\WikiTexVC\Nodes\Literal;
8use MediaWiki\Extension\Math\WikiTexVC\Nodes\TexNode;
9
10/**
11 * Utility Methods for parsing Tex to MathML
12 * @author Johannes Stegmüller
13 */
14class MMLutil {
15    /**
16     * Splits a regular expression in the form '\operatorname {someparams}
17     * Also recognizes succeeding parentheses '\operatorname (' as params
18     * @param string $input tex expression
19     * @return array|null found groups or null
20     */
21    public static function initalParseLiteralExpression( $input ): ?array {
22        $pattern = "/([\\a-zA-Z\s]+)\{([^}]+)\}/";
23        $matches = [];
24        $matched = preg_match_all( $pattern, $input, $matches );
25        return $matched ? $matches : null;
26    }
27
28    /**
29     * Recognize if input is a unicode string "\\u1235"
30     * If yes converts it to notation "&#x123;", if no just returns the input.
31     * @param string $input input to be checked
32     * @return string modified input or input
33     */
34    public static function uc2xNotation( string $input ): string {
35        if ( str_starts_with( $input, "\\u" ) ) {
36            return str_replace( "\\u", "&#x", $input ) . ";";
37        }
38        return $input;
39    }
40
41    /**
42     * If an input string is in "&#x12..:" notation
43     * then convert it to a string of notation \\u12..
44     * @param string $input input to be checked
45     * @return string modified input or input
46     */
47    public static function x2uNotation( string $input ): string {
48        if ( str_starts_with( $input, "&#x" ) ) {
49            return rtrim( str_replace( "&#x", "\\u", $input ), ";" );
50        }
51        return $input;
52    }
53
54    public static function number2xNotation( $input ): string {
55        return "&#x" . $input . ";";
56    }
57
58    /**
59     * Rounds a floating point input to three digits precision
60     * and returns it as string with succeeding "em".
61     * @param float $size input to be processed
62     * @return string rounded digits with em
63     */
64    public static function round2em( float $size ) {
65        $rounded = round( $size, 3 );
66        return $rounded . "em";
67    }
68
69    /**
70     * In a floating point digit as string, set input to precision of three digits
71     * without rounding.
72     * @param string $size input to be checked
73     * @return string digits of precision three with em
74     */
75    public static function size2em( string $size ): string {
76        return preg_replace( "/(\.\d\d\d).+/", '$1', $size ) . "em";
77    }
78
79    /**
80     * Assumes the input curly contains an TexArray of literals, squashes the TexArray characters to a string.
81     * @param Curly $node curly containing a TexArray of literals
82     * @return ?string squashed string in example "2mu", "-3mu" etc. Null if no TexArray inside curly.
83     */
84    public static function squashLitsToUnit( Curly $node ): ?string {
85        $unit = "";
86        foreach ( $node->getArg()->getArgs() as $literal ) {
87            if ( !$literal instanceof Literal ) {
88                continue;
89            }
90            $unit .= $literal->getArg();
91        }
92
93        return $unit;
94    }
95
96    /**
97     * em or other dimensional unit gets multiplied by pre-operator.
98     * @param string $size input size i.e-123em
99     * @param string $operator "plus (+) or minus (-)
100     * @return string ++ => + , -- => +, -+ => -
101     */
102    public static function addPreOperator( string $size, string $operator ): string {
103        $emtr = trim( $size );
104
105        $ok = preg_match( "/^([+\-])$/", $operator );
106        if ( !$ok ) {
107            return '';
108        }
109        switch ( $emtr[0] ) {
110            case "-":
111                if ( $operator == "+" ) {
112                    return $emtr;
113                } elseif ( $operator == "-" ) {
114                    $emtr[0] = "+";
115                    return $emtr;
116                }
117                break;
118            case "+":
119                if ( $operator == "+" ) {
120                    return $emtr;
121                } elseif ( $operator == "-" ) {
122                    $emtr[0] = "-";
123                    return $emtr;
124                }
125                break;
126            default:
127                return $operator . $emtr;
128        }
129        return $emtr;
130    }
131
132    /**
133     * Convert a length dimension to em format
134     * currently supports "mu: math unit and forwards em"
135     * @param string $dimen input for length dimension  like "-2mu" or "3 em"
136     * @return string|null converted string i.e. "0.333em"  or null if error
137     */
138    public static function dimen2em( string $dimen ): ?string {
139        $matches = [];
140        $matched = preg_match( '/([+-]?)(\d*\.*\d+)\s*(mu|em)/', $dimen, $matches );
141
142        if ( !$matched ) {
143            return null;
144        }
145        if ( $matches[3] == "mu" ) {
146            $ret = self::size2em( strval( intval( $matches[2] ) / 18 ) );
147        } elseif ( $matches[3] == "em" ) {
148            $ret = $matches[2] . "em";
149        } else {
150            return null;
151        }
152
153        return ( $matches[1] == "-" ? "-" : "" ) . $ret;
154    }
155
156    /**
157     * Some common steps of processing an input string before passing it as a key to the mappings.
158     * @param string $input string to be processed
159     * @return string prepared input string
160     */
161    public static function inputPreparation( $input ): string {
162        if ( $input === null ) {
163            return "";
164        }
165        $input = trim( $input );
166        if ( str_starts_with( $input, "\\" ) && strlen( $input ) >= 2 ) {
167            $input = substr( $input, 1 );
168            // These are edge cases where input can be a Literal OR an Operator
169            $edgeCases = [ "S", "P", ";", ",", "!", "'", ">" ];
170            if ( in_array( $input, $edgeCases, true ) ) {
171                $input = "\\" . $input;
172            }
173        }
174        return $input;
175    }
176
177    public static function createEntity( $code ): ?string {
178        return IntlChar::chr( intval( $code, 16 ) );
179    }
180
181    /**
182     * From a defined mapping table get the value by key.
183     * Do some optional common conversion steps for the values.
184     * @param string $key key to find entry in table
185     * @param array $mappingTable table which the value is found by key
186     * @param bool $convertUc2X convert first value of the found entry in array to specific "&#x123;" notation
187     * @return mixed|string[]|null the found entry in the table
188     */
189    public static function getMappingByKey( string $key, array $mappingTable, bool $convertUc2X = false ) {
190        if ( isset( $mappingTable[$key] ) ) {
191            $found = $mappingTable[$key];
192            if ( is_string( $found ) ) {
193                $found = [ $found ];
194            }
195            if ( $convertUc2X ) {
196                $found[0] = self::uc2xNotation( $found[0] );
197            }
198            return $found;
199        }
200        return null;
201    }
202
203    /**
204     * From a defined mapping table get the value by key.
205     * @param string $key key to find entry in table
206     * @param array $mappingTable table which the value is found by key
207     * @return mixed|string[]|null the found entry in the table
208     */
209    public static function getMappingByKeySimple( string $key, array $mappingTable ) {
210        if ( isset( $mappingTable[$key] ) ) {
211            return $mappingTable[$key];
212        }
213        return null;
214    }
215
216    /**
217     * Assumes the input curly contains an TexArray of literals, squashes the TexArray characters to a string.
218     * It checks for dollar escaping and removes a backslash, also renders DQ args with underscores
219     * @param TexNode $node curly containing a TexArray of literals
220     * @return ?string squashed string in example "2mu", "-3mu" etc. Null if no TexArray inside curly.
221     */
222    public static function squashLitsToUnitIntent( TexNode $node ): ?string {
223        if ( !$node instanceof Curly ) {
224            return null;
225        }
226        $unit = "";
227        foreach ( $node->getArg()->getArgs() as $literal ) {
228            if ( $literal instanceof DQ ) {
229                $args = $literal->getArgs();
230                if ( !$args[0] instanceof Literal || !$args[1] instanceof Literal ) {
231                    continue;
232                }
233                $arg = self::removeDollarEscaping( $args[0]->getArgs()[0] ) . "_"
234                    . self::removeDollarEscaping( $args[1]->getArgs()[0] );
235            } else {
236                if ( !$literal instanceof Literal ) {
237                    continue;
238                }
239                $arg = self::removeDollarEscaping( $literal->getArg() );
240            }
241            $unit .= $arg;
242        }
243        return $unit;
244    }
245
246    public static function removeDollarEscaping( $input ) {
247        if ( $input == "\\$" ) {
248            return "\$";
249        }
250        return $input;
251    }
252
253}