Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
74.70% covered (warning)
74.70%
62 / 83
58.33% covered (warning)
58.33%
7 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
Literal
74.70% covered (warning)
74.70%
62 / 83
58.33% covered (warning)
58.33%
7 / 12
56.99
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 changeUnicodeFontInput
55.56% covered (warning)
55.56%
5 / 9
0.00% covered (danger)
0.00%
0 / 1
7.19
 renderMML
70.83% covered (warning)
70.83%
34 / 48
0.00% covered (danger)
0.00%
0 / 1
26.04
 getArg
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setArg
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLiterals
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getExtendedLiterals
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 extractIdentifiers
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 extractSubscripts
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getModIdent
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getLiteral
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 createVlineElement
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare( strict_types = 1 );
4
5namespace MediaWiki\Extension\Math\WikiTexVC\Nodes;
6
7use MediaWiki\Extension\Math\WikiTexVC\MMLmappings\BaseMethods;
8use MediaWiki\Extension\Math\WikiTexVC\MMLmappings\Util\MMLParsingUtil;
9use MediaWiki\Extension\Math\WikiTexVC\MMLmappings\Util\MMLutil;
10use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmi;
11use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmn;
12use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmo;
13use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmpadded;
14use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmrow;
15use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmstyle;
16use MediaWiki\Extension\Math\WikiTexVC\TexUtil;
17
18class Literal extends TexNode {
19
20    /** @var string */
21    private $arg;
22    /** @var string[] */
23    private $literals;
24    /** @var string[] */
25    private $extendedLiterals;
26
27    public function __construct( string $arg ) {
28        parent::__construct( $arg );
29        $this->arg = $arg;
30        $this->literals = array_keys( TexUtil::getInstance()->getBaseElements()['is_literal'] );
31        $this->extendedLiterals = $this->literals;
32        array_push( $this->extendedLiterals, '\\infty', '\\emptyset' );
33    }
34
35    public function changeUnicodeFontInput( string $input, array &$state ): string {
36        /**
37         * In some font modifications, it is required to explicitly use Unicode
38         * characters instead of (only) attributes in MathML to indicate the font.
39         * This is mostly because of Chrome behaviour. See: https://phabricator.wikimedia.org/T352196
40         */
41        if ( isset( $state["double-struck-literals"] ) ) {
42            return MMLParsingUtil::mapToDoubleStruckUnicode( $input );
43        } elseif ( isset( $state["calligraphic"] ) ) {
44            return MMLParsingUtil::mapToCaligraphicUnicode( $input );
45        } elseif ( isset( $state["fraktur"] ) ) {
46            return MMLParsingUtil::mapToFrakturUnicode( $input );
47        } elseif ( isset( $state["bold"] ) ) {
48            return MMLParsingUtil::mapToBoldUnicode( $input );
49        }
50        return $input;
51    }
52
53    /** @inheritDoc */
54    public function renderMML( $arguments = [], &$state = [] ) {
55        if ( isset( $state["intent-params"] ) ) {
56            foreach ( $state["intent-params"] as $intparam ) {
57                if ( $intparam == $this->arg ) {
58                    $arguments["arg"] = $intparam;
59                }
60            }
61        }
62
63        if ( isset( $state["intent-params-expl"] ) ) {
64            $arguments["arg"] = $state["intent-params-expl"];
65        }
66
67        if ( $this->arg === " " ) {
68            // Fixes https://gerrit.wikimedia.org/r/c/mediawiki/extensions/Math/+/961711
69            // And they creation of empty mo elements.
70            return "";
71        }
72
73        if ( is_numeric( $this->arg ) ) {
74            $mn = new MMLmn( "", $arguments );
75            return $mn->encapsulateRaw( $this->changeUnicodeFontInput( $this->arg, $state ) );
76        }
77        // is important to split and find chars within curly and differentiate, see tc 459
78        $foundOperatorContent = MMLutil::initalParseLiteralExpression( $this->arg );
79        if ( !$foundOperatorContent ) {
80            $input = $this->arg;
81            $operatorContent = null;
82        } else {
83            $input = $foundOperatorContent[1][0];
84            $operatorContent = [ "foundOC" => $foundOperatorContent[2][0] ];
85        }
86
87        // This is rather a workaround:
88        // Sometimes literals from WikiTexVC contain complete \\operatorname {asd} hinted as bug tex-2-mml.json
89        if ( str_contains( $input, "\\operatorname" ) ) {
90            $mi = new MMLmi();
91            return $mi->encapsulateRaw( $operatorContent["foundOC"] );
92        }
93
94        $inputP = $input;
95
96        // Sieve for Operators
97        $bm = new BaseMethods();
98        $noStretchArgs = $arguments;
99        // Delimiters and operators should not be stretchy by default when used as literals
100        $noStretchArgs['stretchy'] ??= 'false';
101        $ret = $bm->checkAndParseOperator( $inputP, $this, $noStretchArgs, $operatorContent, $state, false );
102        if ( $ret ) {
103            return $ret;
104        }
105        // Sieve for mathchar07 chars
106        $bm = new BaseMethods();
107        $ret = $bm->checkAndParseMathCharacter( $inputP, $this, $arguments, $operatorContent, false );
108        if ( $ret ) {
109            return $ret;
110        }
111
112        // Sieve for Identifiers
113        $ret = $bm->checkAndParseIdentifier( $inputP, $this, $arguments, $operatorContent, false );
114        if ( $ret ) {
115            return $ret;
116        }
117        // Sieve for Delimiters
118        $ret = $bm->checkAndParseDelimiter( $input, $this, $noStretchArgs, $operatorContent );
119        if ( $ret ) {
120            return $ret;
121        }
122
123        // Sieve for Makros
124        $ret = BaseMethods::checkAndParse( $inputP, $arguments,
125            array_merge( $operatorContent ?? [], $state ?? [] ),
126            $this, false );
127        if ( $ret || $ret === '' ) {
128            return $ret;
129        }
130
131        // Specific
132        if ( !( empty( $state['inMatrix'] ) ) && trim( $this->arg ) === '\vline' ) {
133            return $this->createVlineElement();
134        }
135
136        if ( !( empty( $state['inHBox'] ) ) ) {
137            // No mi, if literal is from HBox
138            return $input;
139        }
140
141        // If falling through all sieves just create an MI element
142        $mi = new MMLmi( "", $arguments );
143        return $mi->encapsulateRaw( $this->changeUnicodeFontInput( $input, $state ) ); // $this->arg
144    }
145
146    /**
147     * @return string
148     */
149    public function getArg(): string {
150        return $this->arg;
151    }
152
153    public function setArg( string $arg ) {
154        $this->arg = $arg;
155    }
156
157    /**
158     * @return int[]|string[]
159     */
160    public function getLiterals(): array {
161        return $this->literals;
162    }
163
164    /**
165     * @return int[]|string[]
166     */
167    public function getExtendedLiterals(): array {
168        return $this->extendedLiterals;
169    }
170
171    /** @inheritDoc */
172    public function extractIdentifiers( $args = null ) {
173        return $this->getLiteral( $this->literals, '/^([a-zA-Z\']|\\\\int)$/' );
174    }
175
176    /** @inheritDoc */
177    public function extractSubscripts() {
178        return $this->getLiteral( $this->extendedLiterals, '/^([0-9a-zA-Z+\',-])$/' );
179    }
180
181    /** @inheritDoc */
182    public function getModIdent() {
183        if ( $this->arg === '\\ ' ) {
184            return [ '\\ ' ];
185        }
186        return $this->getLiteral( $this->literals, '/^([0-9a-zA-Z\'])$/' );
187    }
188
189    private function getLiteral( array $lit, string $regexp ): array {
190        $s = trim( $this->arg );
191        if ( preg_match( $regexp, $s ) == 1 ) {
192            return [ $s ];
193        } elseif ( in_array( $s, $lit, true ) ) {
194            return [ $s ];
195        } else {
196            return [];
197        }
198    }
199
200    /**
201     * @return string
202     */
203    public function createVlineElement(): string {
204        $mrow = new MMLmrow();
205        $mpAdded = new MMLmpadded( "", [ "depth" => "0", "height" => "0" ] );
206        $mStyle = new MMLmstyle( "", [ "mathsize" => "1.2em" ] );
207        $mo = new MMLmo( "", [ "fence" => "false", "stretchy" => "false" ] );
208        return $mrow->encapsulateRaw( $mpAdded->encapsulateRaw(
209            $mStyle->encapsulateRaw( $mo->encapsulateRaw( "|" ) ) ) );
210    }
211
212}