Add further nodes

Related code:
fb56991251/lib/nodes/

Bug: T312528
Change-Id: I8bd30bc45d2c23214b317ca0c04aa8e7d6a2da33
This commit is contained in:
Stegmujo 2022-08-29 12:15:58 +02:00
parent 1148dfc8da
commit d940161a3e
No known key found for this signature in database
GPG key ID: BB616B7CC84186BE
28 changed files with 1396 additions and 3 deletions

35
src/TexVC/Nodes/Big.php Normal file
View file

@ -0,0 +1,35 @@
<?php
declare( strict_types = 1 );
namespace MediaWiki\Extension\Math\TexVC\Nodes;
class Big extends TexNode {
/** @var string */
private $fname;
/** @var string */
private $arg;
public function __construct( string $fname, string $arg ) {
parent::__construct( $fname, $arg );
$this->fname = $fname;
$this->arg = $arg;
}
public function inCurlies() {
return $this->render();
}
public function render() {
return '{' . $this->fname . ' ' . $this->arg . '}';
}
public function extractIdentifiers( $args = null ) {
return [];
}
public function name() {
return 'BIG';
}
}

35
src/TexVC/Nodes/Box.php Normal file
View file

@ -0,0 +1,35 @@
<?php
declare( strict_types = 1 );
namespace MediaWiki\Extension\Math\TexVC\Nodes;
class Box extends TexNode {
/** @var string */
private $fname;
/** @var string */
private $arg;
public function __construct( string $fname, string $arg ) {
parent::__construct( $fname, $arg );
$this->fname = $fname;
$this->arg = $arg;
}
public function inCurlies() {
return $this->render();
}
public function render() {
return '{' . $this->fname . '{' . $this->arg . '}}';
}
public function extractIdentifiers( $args = null ) {
return [];
}
public function name() {
return 'BOX';
}
}

View file

@ -0,0 +1,38 @@
<?php
declare( strict_types = 1 );
namespace MediaWiki\Extension\Math\TexVC\Nodes;
class ChemFun2u extends TexNode {
/** @var string */
private $fname;
/** @var TexNode */
private $left;
/** @var TexNode */
private $right;
public function __construct( string $fname, TexNode $left, TexNode $right ) {
parent::__construct( $fname, $left, $right );
$this->fname = $fname;
$this->left = $left;
$this->right = $right;
}
public function inCurlies() {
return $this->render();
}
public function render() {
return $this->fname . $this->left->inCurlies() . '_' . $this->right->inCurlies();
}
public function extractIdentifiers( $args = null ) {
return [];
}
public function name() {
return 'CHEM_FUN2u';
}
}

View file

@ -0,0 +1,35 @@
<?php
declare( strict_types = 1 );
namespace MediaWiki\Extension\Math\TexVC\Nodes;
class ChemWord extends TexNode {
/** @var TexNode */
private $left;
/** @var TexNode */
private $right;
public function __construct( TexNode $left, TexNode $right ) {
parent::__construct( $left, $right );
$this->left = $left;
$this->right = $right;
}
public function inCurlies() {
return $this->render();
}
public function render() {
return $this->left->render() . $this->right->render();
}
public function extractIdentifiers( $args = null ) {
return [];
}
public function name() {
return 'CHEM_WORD';
}
}

36
src/TexVC/Nodes/Curly.php Normal file
View file

@ -0,0 +1,36 @@
<?php
declare( strict_types = 1 );
namespace MediaWiki\Extension\Math\TexVC\Nodes;
class Curly extends TexNode {
/** @var TexArray */
private $arg;
public function __construct( TexArray $arg ) {
parent::__construct( $arg );
$this->arg = $arg;
}
public function render() {
return $this->arg->inCurlies();
}
public function inCurlies() {
return $this->render();
}
public function extractSubscripts() {
return $this->arg->extractSubscripts();
}
public function getModIdent() {
return $this->arg->getModIdent();
}
public function name() {
return 'CURLY';
}
}

70
src/TexVC/Nodes/Declh.php Normal file
View file

@ -0,0 +1,70 @@
<?php
declare( strict_types = 1 );
namespace MediaWiki\Extension\Math\TexVC\Nodes;
class Declh extends TexNode {
/** @var string */
private $fname;
/** @var TexArray */
private $arg;
public function __construct( string $fname, TexArray $arg ) {
parent::__construct( $fname, $arg );
$this->fname = $fname;
$this->arg = $arg;
}
public function inCurlies() {
return $this->render();
}
public function render() {
return '{' . $this->fname . ' ' . $this->arg->inCurlies() . '}';
}
public function extractIdentifiers( $args = null ) {
if ( $args == null ) {
$args = [ $this->arg ];
}
$identifier = parent::extractIdentifiers( $args );
if ( isset( $identifier[0] ) ) {
return [ implode( '', $identifier ) ];
}
return $identifier;
}
public function extractSubscripts() {
$f = $this->fname;
// @see
// http://tex.stackexchange.com/questions/98406/which-command-should-i-use-for-textual-subscripts-in-math-mode
// cf https://phabricator.wikimedia.org/T56818 a is always RM
// for f there are only four cases
switch ( $f ) {
case '\\rm':
$f = '\\mathrm';
break;
case '\\it':
$f = '\\mathit';
break;
case '\\cal':
$f = '\\mathcal';
break;
case '\\bf':
$f = '\\mathbf';
}
$x = $this->arg->extractSubscripts();
if ( isset( $x[0] ) ) {
return [ $f . '{' . $x . '}' ];
}
return parent::extractSubscripts();
}
public function name() {
return 'DECLh';
}
}

View file

@ -0,0 +1,24 @@
<?php
declare( strict_types = 1 );
namespace MediaWiki\Extension\Math\TexVC\Nodes;
class Dollar extends TexNode {
public function __construct( TexArray $value ) {
parent::__construct( $value );
}
public function render() {
return '$' . parent::render() . '$';
}
public function extractIdentifiers( $args = null ) {
return [];
}
public function name() {
return 'DOLLAR';
}
}

74
src/TexVC/Nodes/Fun1.php Normal file
View file

@ -0,0 +1,74 @@
<?php
declare( strict_types = 1 );
namespace MediaWiki\Extension\Math\TexVC\Nodes;
use MediaWiki\Extension\Math\TexVC\TexUtil;
class Fun1 extends TexNode {
/** @var string */
protected $fname;
/** @var TexNode */
protected $arg;
/** @var TexUtil */
private $tu;
public function __construct( string $fname, TexNode $arg ) {
parent::__construct( $fname, $arg );
$this->fname = $fname;
$this->arg = $arg;
$this->tu = new TexUtil();
}
public function inCurlies() {
return $this->render();
}
public function render() {
return '{' . $this->fname . ' ' . $this->arg->inCurlies() . '}';
}
public function extractIdentifiers( $args = null ) {
if ( $args == null ) {
$args = [ $this->arg ];
}
$letterMods = array_keys( $this->tu->getBaseElements()['is_letter_mod'] );
if ( in_array( $this->fname, $letterMods ) ) {
$ident = $this->arg->getModIdent();
if ( !isset( $ident[0] ) ) {
return parent::extractIdentifiers( $args );
}
// in difference to javascript code: taking first element of array here.
return [ $this->fname . '{' . $ident[0] . '}' ];
} elseif ( array_key_exists( $this->fname, $this->tu->getBaseElements()['ignore_identifier'] ) ) {
return [];
}
return parent::extractIdentifiers( $args );
}
public function extractSubscripts() {
return $this->getSubs( $this->arg->extractSubscripts() );
}
public function getModIdent() {
return $this->getSubs( $this->arg->getModIdent() );
}
private function getSubs( $subs ) {
$letterMods = array_keys( $this->tu->getBaseElements()['is_letter_mod'] );
if ( isset( $subs[0] ) && in_array( $this->fname, $letterMods ) ) {
// in difference to javascript code: taking first element of array here.
return [ $this->fname . '{' . $subs[0] . '}' ];
}
return [];
}
public function name() {
return 'FUN1';
}
}

View file

@ -0,0 +1,20 @@
<?php
declare( strict_types = 1 );
namespace MediaWiki\Extension\Math\TexVC\Nodes;
class Fun1nb extends Fun1 {
public function name() {
return 'FUN1nb';
}
public function inCurlies() {
return '{' . $this->render() . '}';
}
public function render() {
return $this->fname . ' ' . $this->arg->inCurlies() . ' ';
}
}

82
src/TexVC/Nodes/Fun2.php Normal file
View file

@ -0,0 +1,82 @@
<?php
declare( strict_types = 1 );
namespace MediaWiki\Extension\Math\TexVC\Nodes;
class Fun2 extends TexNode {
/** @var string */
protected $fname;
/** @var TexNode */
protected $arg1;
/** @var TexNode */
protected $arg2;
public function __construct( string $fname, TexNode $arg1, TexNode $arg2 ) {
parent::__construct( $fname, $arg1, $arg2 );
$this->fname = $fname;
$this->arg1 = $arg1;
$this->arg2 = $arg2;
}
public function inCurlies() {
return $this->render();
}
public function render() {
return '{' . $this->fname . ' ' . $this->arg1->inCurlies() . $this->arg2->inCurlies() . '}';
}
public function extractIdentifiers( $args = null ) {
if ( $args == null ) {
$args = [ $this->arg1, $this->arg2 ];
}
return parent::extractIdentifiers( $args );
}
public function name() {
return 'FUN2';
}
}
/**
* 'use strict';
* const TexNode = require('./texnode');
* const assert = require('assert');
*
* class Fun2 extends TexNode {
* constructor(fname, arg1, arg2) {
* assert.strictEqual(
* arguments.length,
* 3,
* 'Incorrect number or arguments');
* assert.ok(
* (fname instanceof String || typeof fname === 'string') &&
* arg1 instanceof TexNode,
* arg2 instanceof TexNode,
* 'Incorrect argument type');
* super(fname, arg1, arg2);
* this.fname = fname;
* this.arg1 = arg1;
* this.arg2 = arg2;
* }
*
* inCurlies() {
* return this.render();
* }
*
* render() {
* return '{' + this.fname +
* ' ' + this.arg1.inCurlies() +
* this.arg2.inCurlies() + '}';
* }
*
* extractIdentifiers(args = [this.arg1, this.arg2]) {
* return super.extractIdentifiers(args);
* }
* get name() {
* return 'FUN2';
* }
* }
*/

View file

@ -0,0 +1,20 @@
<?php
declare( strict_types = 1 );
namespace MediaWiki\Extension\Math\TexVC\Nodes;
class Fun2nb extends Fun2 {
public function name() {
return 'FUN2nb';
}
public function inCurlies() {
return '{' . $this->render() . '}';
}
public function render() {
return $this->fname . ' ' . $this->arg1->inCurlies() . $this->arg2->inCurlies();
}
}

View file

@ -19,7 +19,7 @@ class Literal extends TexNode {
$tu = new TexUtil();
$this->literals = array_keys( $tu->getBaseElements()['is_literal'] );
$this->extendedLiterals = $this->literals;
array_push( $this->extendedLiterals, [ '\\infty', '\\emptyset' ] );
array_push( $this->extendedLiterals, '\\infty', '\\emptyset' );
}
public function extractIdentifiers( $args = null ) {
@ -39,7 +39,6 @@ class Literal extends TexNode {
private function getLiteral( $lit, $regexp ) {
$s = trim( $this->arg );
if ( preg_match( $regexp, $s ) == 1 ) {
return [ $s ];
} elseif ( in_array( $s, $lit ) ) {

View file

@ -0,0 +1,139 @@
<?php
declare( strict_types = 1 );
namespace MediaWiki\Extension\Math\TexVC\Nodes;
use InvalidArgumentException;
class TexArray extends TexNode {
public function __construct( ...$args ) {
$nargs = [];
self::checkInput( $args );
foreach ( $args as &$arg ) {
if ( $arg != null ) {
array_push( $nargs, $arg );
}
}
parent::__construct( ...$args );
}
public function inCurlies() {
if ( isset( $this->args[0] ) && count( $this->args ) == 1 ) {
return $this->args[0]->inCurlies();
} else {
return '{' . $this->render() . '}';
}
}
public function extractSubscripts() {
$y = [];
foreach ( $this->args as $x ) {
$y = array_merge( $y, $x->extractSubscripts() );
}
if ( isset( $this->args[0] ) && ( count( $this->args ) == count( $y ) ) ) {
return implode( '', $y );
}
return [];
}
public function extractIdentifiers( $args = null ) {
if ( $args == null ) {
$args = $this->args;
}
$list = parent::extractIdentifiers( $args );
$outpos = 0;
$offset = 0;
$int = 0;
for ( $inpos = 0; $inpos < count( $list ); $inpos++ ) {
$outpos = $inpos - $offset;
switch ( $list[$inpos] ) {
case '\'':
$list[$outpos - 1] .= '\'';
$offset++;
break;
case '\\int':
$int++;
$offset++;
break;
case '\\mathrm{d}':
case 'd':
if ( $int ) {
$int--;
$offset++;
break;
}
// no break
default:
if ( isset( $list[0] ) ) {
$list[$outpos] = $list[$inpos];
}
}
}
return array_slice( $list, 0, count( $list ) - $offset );
}
public function getModIdent() {
$y = [];
foreach ( $this->args as $x ) {
$y = array_merge( $y, $x->getModIdent() );
}
if ( isset( $this->args[0] ) && ( count( $this->args ) == count( $y ) ) ) {
return implode( "", $y );
}
return [];
}
public function push( ...$elements ) {
self::checkInput( $elements );
array_push( $this->args, ...$elements );
}
/**
* @throws InvalidArgumentException if first value not defined
* @return TexNode|string first value
*/
public function first() {
if ( isset( $this->args[0] ) ) {
return $this->args[0];
} else {
throw new InvalidArgumentException( 'Input arguments not have been filled.' );
}
}
/**
* @throws InvalidArgumentException if second value not defined
* @return TexNode|string second value
*/
public function second() {
if ( isset( $this->args[1] ) ) {
return $this->args[1];
} else {
throw new InvalidArgumentException( 'Input arguments not have been filled.' );
}
}
public function name() {
return 'ARRAY';
}
/**
* @throws InvalidArgumentException if args not of correct type
* @param TexNode[] $args input args
* @return void
*/
private static function checkInput( $args ): void {
foreach ( $args as $arg ) {
if ( !( $arg instanceof TexNode ) ) {
throw new InvalidArgumentException( 'Wrong input type specified in input elements.' );
}
}
}
}

View file

@ -9,7 +9,7 @@ use InvalidArgumentException;
class TexNode {
/** @var list<TexNode|string> */
private $args;
protected $args;
/**
* Creates a TexNode
@ -38,6 +38,14 @@ class TexNode {
return $child;
}
public function getLength(): ?int {
if ( isset( $this->args[0] ) ) {
return count( $this->args );
} else {
return 0;
}
}
/**
* Wraps the rendered result in curly brackets.
* @return string rendered result in curlies.

27
src/TexVC/Nodes/UQ.php Normal file
View file

@ -0,0 +1,27 @@
<?php
declare( strict_types = 1 );
namespace MediaWiki\Extension\Math\TexVC\Nodes;
class UQ extends TexNode {
/** @var TexNode */
private $base;
/** @var TexNode */
private $up;
public function __construct( TexNode $base, TexNode $down ) {
parent::__construct( $base, $down );
$this->base = $base;
$this->up = $down;
}
public function render() {
return $this->base->render() . '^' . $this->up->inCurlies();
}
public function name() {
return 'UQ';
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace MediaWiki\Extension\Math\Tests\TexVC\Nodes;
use ArgumentCountError;
use MediaWiki\Extension\Math\TexVC\Nodes\Big;
use MediaWiki\Extension\Math\TexVC\Nodes\Literal;
use MediaWikiUnitTestCase;
use RuntimeException;
use TypeError;
/**
* @covers \MediaWiki\Extension\Math\TexVC\Nodes\Big
*/
class BigTest extends MediaWikiUnitTestCase {
public function testEmptyBig() {
$this->expectException( ArgumentCountError::class );
new Big();
throw new ArgumentCountError( 'Should not create an empty big' );
}
public function testOneArgumentBig() {
$this->expectException( ArgumentCountError::class );
new Big( '\\big' );
throw new ArgumentCountError( 'Should not create a big with one argument' );
}
public function testIncorrectTypeBig() {
$this->expectException( TypeError::class );
new Big( '\\big', new Literal( 'a' ) );
throw new RuntimeException( 'Should not create a big with incorrect type' );
}
public function testBasicFunctionBig() {
$big = new Big( '\\big', 'a' );
$this->assertEquals( '{\\big a}', $big->render(), 'Should create a basic function' );
}
public function testExtractIdentifiersBig() {
$big = new Big( '\\big', 'a' );
$this->assertEquals( [], $big->extractIdentifiers(), 'Should extract identifiers' );
}
public function testCurliesBig() {
$big = new Big( '\\big', 'a' );
$this->assertEquals( '{\\big a}', $big->inCurlies(), 'Should create exactly one set of curlies' );
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace MediaWiki\Extension\Math\Tests\TexVC\Nodes;
use ArgumentCountError;
use MediaWiki\Extension\Math\TexVC\Nodes\Box;
use MediaWiki\Extension\Math\TexVC\Nodes\Literal;
use MediaWikiUnitTestCase;
use RuntimeException;
use TypeError;
/**
* @covers \MediaWiki\Extension\Math\TexVC\Nodes\Box
*/
class BoxTest extends MediaWikiUnitTestCase {
public function testEmptyBox() {
$this->expectException( ArgumentCountError::class );
new Box();
throw new ArgumentCountError( 'Should not create an empty box' );
}
public function testOneArgumentBox() {
$this->expectException( ArgumentCountError::class );
new Box( '\\hbox' );
throw new ArgumentCountError( 'Should not create a box with one argument' );
}
public function testIncorrectTypeBox() {
$this->expectException( TypeError::class );
new Box( '\\hbox', new Literal( 'a' ) );
throw new RuntimeException( 'Should not create a box with incorrect type' );
}
public function testBasicFunctionBox() {
$box = new Box( '\\hbox', 'a' );
$this->assertEquals( '{\\hbox{a}}', $box->render(), 'Should create a basic function' );
}
public function testExtractIdentifiersBox() {
$box = new Box( '\\hbox', 'a' );
$this->assertEquals( [], $box->extractIdentifiers(), 'Should extract identifiers' );
}
public function testCurliesBox() {
$box = new Box( '\\hbox', 'a' );
$this->assertEquals( '{\\hbox{a}}', $box->inCurlies(), 'Should create exactly one set of curlies' );
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace MediaWiki\Extension\Math\Tests\TexVC\Nodes;
use ArgumentCountError;
use MediaWiki\Extension\Math\TexVC\Nodes\ChemFun2u;
use MediaWiki\Extension\Math\TexVC\Nodes\Literal;
use MediaWikiUnitTestCase;
use TypeError;
/**
* @covers \MediaWiki\Extension\Math\TexVC\Nodes\ChemFun2u
*/
class ChemFun2uTest extends MediaWikiUnitTestCase {
public function testEmptyChemFun2u() {
$this->expectException( ArgumentCountError::class );
new ChemFun2u();
throw new ArgumentCountError( 'Should not create an empty ChemFun2u' );
}
public function testOneArgumentChemFun2u() {
$this->expectException( ArgumentCountError::class );
new ChemFun2u( 'a' );
throw new ArgumentCountError( 'Should not create a ChemFun2u with one argument' );
}
public function testIncorrectTypeChemFun2u() {
$this->expectException( TypeError::class );
new ChemFun2u( 'a', 'b', 'c' );
throw new TypeError( 'Should not create a ChemFun2u with incorrect type' );
}
public function testBasicChemFun2u() {
$fun2u = new ChemFun2u( 'a', new Literal( 'b' ), new Literal( 'c' ) );
$this->assertEquals( 'a{b}_{c}', $fun2u->render(), 'Should create a basic ChemFun2u' );
}
public function testExtractIdentifiers() {
$fun2u = new ChemFun2u( 'a', new Literal( 'b' ), new Literal( 'c' ) );
$this->assertEquals( [], $fun2u->extractIdentifiers(), 'Should extract identifiers' );
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace MediaWiki\Extension\Math\Tests\TexVC\Nodes;
use ArgumentCountError;
use MediaWiki\Extension\Math\TexVC\Nodes\ChemWord;
use MediaWiki\Extension\Math\TexVC\Nodes\Literal;
use MediaWikiUnitTestCase;
use RuntimeException;
use TypeError;
/**
* @covers \MediaWiki\Extension\Math\TexVC\Nodes\ChemWord
*/
class ChemWordTest extends MediaWikiUnitTestCase {
public function testEmptyChemWord() {
$this->expectException( ArgumentCountError::class );
new ChemWord();
throw new ArgumentCountError( 'Should not create an empty ChemWord' );
}
public function testOneArgumentChemWord() {
$this->expectException( ArgumentCountError::class );
new ChemWord( new Literal( 'a' ) );
throw new ArgumentCountError( 'Should not create a ChemWord with one argument' );
}
public function testIncorrectTypeChemWord() {
$this->expectException( TypeError::class );
new ChemWord( 'a', 'b' );
throw new RuntimeException( 'Should not create a ChemWord with incorrect type' );
}
public function testBasicFunctionChemWord() {
$chemWord = new ChemWord( new Literal( 'a' ), new Literal( 'b' ) );
$this->assertEquals( 'ab', $chemWord->render(), 'Should create a basic function' );
}
public function testExtractIdentifiersBox() {
$chemWord = new ChemWord( new Literal( 'a' ), new Literal( 'b' ) );
$this->assertEquals( [], $chemWord->extractIdentifiers(), 'Should extract identifiers' );
}
}

View file

@ -0,0 +1,72 @@
<?php
namespace MediaWiki\Extension\Math\Tests\TexVC\Nodes;
use ArgumentCountError;
use MediaWiki\Extension\Math\TexVC\Nodes\Curly;
use MediaWiki\Extension\Math\TexVC\Nodes\DQ;
use MediaWiki\Extension\Math\TexVC\Nodes\Literal;
use MediaWiki\Extension\Math\TexVC\Nodes\TexArray;
use MediaWiki\Extension\Math\TexVC\Nodes\TexNode;
use MediaWikiUnitTestCase;
use RuntimeException;
use TypeError;
/**
* @covers \MediaWiki\Extension\Math\TexVC\Nodes\Curly
*/
class CurlyTest extends MediaWikiUnitTestCase {
public function testEmptyDollar() {
$this->expectException( ArgumentCountError::class );
new Curly();
throw new ArgumentCountError( 'Should not create an empty curly' );
}
public function testOneArgumentCurly() {
$this->expectException( ArgumentCountError::class );
new Curly( new TexArray( new TexNode( 'a' ) ), new TexArray( new TexNode( 'b' ) ) );
throw new ArgumentCountError( 'Should not create a curly with more than one argument' );
}
public function testIncorrectTypeCurly() {
$this->expectException( TypeError::class );
new Curly( new TexNode() );
throw new RuntimeException( 'Should not create a curly with incorrect type' );
}
public function testRenderTexCurly() {
$curly = new Curly( new TexArray() );
$this->assertEquals( '{}', $curly->render(), 'Should render a curly with empty tex array' );
}
public function testRenderListCurly() {
$curly = new Curly( new TexArray(
new Literal( 'hello' ),
new Literal( ' ' ),
new Literal( 'world' )
) );
$this->assertEquals( '{hello world}', $curly->render(), 'Should render a list' );
}
public function testNoExtraCurliesDQ() {
$dq = new DQ( new Literal( 'a' ),
new Curly( new TexArray( new Literal( 'b' ) ) ) );
$this->assertEquals( 'a_{b}', $dq->render(), 'Should not create extra curlies from dq' );
}
public function testNoExtraCurliesCurly() {
$curly = new Curly( new TexArray( new Literal( 'a' ) ) );
$this->assertEquals( '{a}', $curly->inCurlies(), 'Should not create extra curlies from curly' );
}
public function testExtractIdentifierModsCurly() {
$curly = new Curly( new TexArray( new Literal( 'b' ) ) );
$this->assertEquals( 'b', $curly->getModIdent(), 'Should extract identifier modifications' );
}
public function testExtractSubscirpts() {
$curly = new Curly( new TexArray( new Literal( 'b' ) ) );
$this->assertEquals( 'b', $curly->extractSubscripts(), 'Should extract subscripts' );
}
}

View file

@ -0,0 +1,78 @@
<?php
namespace MediaWiki\Extension\Math\Tests\TexVC\Nodes;
use ArgumentCountError;
use MediaWiki\Extension\Math\TexVC\Nodes\Declh;
use MediaWiki\Extension\Math\TexVC\Nodes\Literal;
use MediaWiki\Extension\Math\TexVC\Nodes\TexArray;
use MediaWikiUnitTestCase;
use RuntimeException;
use TypeError;
/**
* @covers \MediaWiki\Extension\Math\TexVC\Nodes\Declh
*/
class DeclhTest extends MediaWikiUnitTestCase {
public function testEmptyDeclh() {
$this->expectException( ArgumentCountError::class );
new Declh();
throw new ArgumentCountError( 'Should not create an empty Declh' );
}
public function testOneArgumentDeclh() {
$this->expectException( ArgumentCountError::class );
new Declh( '\\f' );
throw new ArgumentCountError( 'Should not create a Declh with one argument' );
}
public function testIncorrectTypeDeclh() {
$this->expectException( TypeError::class );
new Declh( '\\f', 'x' );
throw new RuntimeException( 'Should not create a Declh with incorrect type' );
}
public function testBasicFunctionDeclh() {
$f = new Declh( '\\rm', new TexArray( new Literal( 'a' ) ) );
$this->assertEquals( '{\\rm {a}}', $f->render(), 'Should create a basic function' );
}
public function testTwoArgsFunctionDeclh() {
$f = new Declh( '\\rm',
new TexArray( new Literal( 'a' ), new Literal( 'b' ) ) );
$this->assertEquals( '{\\rm {ab}}',
$f->render(), 'Should create a function with two arguments' );
}
public function testCurliesDeclh() {
$f = new Declh( '\\f', new TexArray( new Literal( 'a' ) ) );
$this->assertEquals( '{\\f {a}}', $f->inCurlies(), 'Should create exactly one set of curlies' );
}
public function testExtractIdentifiersDeclh() {
$f = new Declh( '\\rm', new TexArray( new Literal( 'a' ) ) );
$this->assertEquals( [ 'a' ], $f->extractIdentifiers(), 'Should extract identifiers' );
}
public function testExtractIdentifiersMultiDeclh() {
$f = new Declh( '\\rm', new TexArray( new Literal( 'a' ), new Literal( 'b' ) ) );
$this->assertEquals( [ 'ab' ], $f->extractIdentifiers(), 'Should extract multiple identifiers' );
}
public function testNotExtractSomeSubscripts() {
$f = new Declh( '\\bf', new TexArray( new Literal( '' ) ) );
$this->assertEquals( [], $f->extractSubscripts(),
'Should not extract empty font modifier subscripts identifiers' );
}
public function testSubscriptsForFontMod() {
$mods = [ 'rm','it','cal','bf' ];
foreach ( $mods as $mod ) {
$f = new Declh( "\\${mod}", new TexArray( new Literal( 'a' ) ) );
$this->assertEquals( [ "\\math${mod}{a}" ], $f->extractSubscripts(),
"Should extract subscripts for ${mod} font modification" );
}
}
}

View file

@ -0,0 +1,59 @@
<?php
namespace MediaWiki\Extension\Math\Tests\TexVC\Nodes;
use ArgumentCountError;
use MediaWiki\Extension\Math\TexVC\Nodes\Dollar;
use MediaWiki\Extension\Math\TexVC\Nodes\Literal;
use MediaWiki\Extension\Math\TexVC\Nodes\TexArray;
use MediaWiki\Extension\Math\TexVC\Nodes\TexNode;
use MediaWikiUnitTestCase;
use RuntimeException;
use TypeError;
/**
* @covers \MediaWiki\Extension\Math\TexVC\Nodes\Dollar
*/
class DollarTest extends MediaWikiUnitTestCase {
public function testEmptyDollar() {
$this->expectException( ArgumentCountError::class );
new Dollar();
throw new ArgumentCountError( 'Should not create an empty dollar' );
}
public function testOneArgumentDollar() {
$this->expectException( ArgumentCountError::class );
new Dollar( new TexArray(), new TexArray() );
throw new ArgumentCountError( 'Should not create a dollar with more than one argument' );
}
public function testIncorrectTypeDollar() {
$this->expectException( TypeError::class );
new Dollar( new TexNode() );
throw new RuntimeException( 'Should not create a dollar with incorrect type' );
}
public function testRenderTexDollar() {
$dollar = new Dollar( new TexArray() );
$this->assertEquals( '$$', $dollar->render(), 'Should render a dollar with empty tex array' );
}
public function testRenderListDollar() {
$dollar = new Dollar( new TexArray(
new Literal( 'hello' ),
new Literal( ' ' ),
new Literal( 'world' )
) );
$this->assertEquals( '$hello world$', $dollar->render(), 'Should render a list' );
}
public function testExtractIdentifiersDollar() {
$dollar = new Dollar( new TexArray(
new Literal( 'a' ),
new Literal( 'b' ),
new Literal( 'c' )
) );
$this->assertEquals( [], $dollar->extractIdentifiers(), 'Should extract identifiers' );
}
}

View file

@ -0,0 +1,95 @@
<?php
namespace MediaWiki\Extension\Math\Tests\TexVC\Nodes;
use ArgumentCountError;
use MediaWiki\Extension\Math\TexVC\Nodes\Fun1;
use MediaWiki\Extension\Math\TexVC\Nodes\Literal;
use MediaWikiUnitTestCase;
use RuntimeException;
use TypeError;
/**
* @covers \MediaWiki\Extension\Math\TexVC\Nodes\Fun1
*/
class Fun1Test extends MediaWikiUnitTestCase {
public function testEmptyFun1() {
$this->expectException( ArgumentCountError::class );
new Fun1();
throw new ArgumentCountError( 'Should not create an empty fun1' );
}
public function testOneArgumentFun1() {
$this->expectException( ArgumentCountError::class );
new Fun1( '\\f' );
throw new ArgumentCountError( 'Should not create a fun1 with one argument' );
}
public function testIncorrectTypeFun1() {
$this->expectException( TypeError::class );
new Fun1( '\\f', 'x' );
throw new RuntimeException( 'Should not create a fun1 with incorrect type' );
}
public function testBasicFunctionFun1() {
$f = new Fun1( '\\f', new Literal( 'a' ) );
$this->assertEquals( '{\\f {a}}', $f->render(),
'Should create a basic function' );
}
public function testCurliesFun1() {
$f = new Fun1( '\\f', new Literal( 'a' ) );
$this->assertEquals( '{\\f {a}}', $f->inCurlies(),
'Should create exactly one set of curlies' );
}
public function testExtractIdentifiersFun1() {
$f = new Fun1( '\\mathbf', new Literal( 'B' ) );
$this->assertEquals( [ '\\mathbf{B}' ], $f->extractIdentifiers(),
'Should extract identifiers' );
}
public function testExtractExtendedLiteralsFun1() {
$f = new Fun1( '\\mathbf', new Literal( '\\infty' ) );
$this->assertEquals( [], $f->extractIdentifiers(),
'Should not extract extended literals as identifiers.' );
}
public function testExtractPhantomIdentifiers() {
$f = new Fun1( '\\hphantom', new Literal( 'A' ) );
$this->assertEquals( [], $f->extractIdentifiers(),
'Should not extract phantom identifiers.' );
}
public function testIgnoreUnknownFunctions() {
$f = new Fun1( '\\unknown', new Literal( 'A' ) );
$this->assertEquals( [ 'A' ], $f->extractIdentifiers(),
'Should ignore unknown functions.' );
}
public function testExtractIdentifierMods() {
$f = new Fun1( '\\mathbf', new Literal( 'B' ) );
$this->assertEquals( [ '\\mathbf{B}' ], $f->getModIdent(),
'Should extract identifier modifications.' );
}
public function testExtractSubscripts() {
$f = new Fun1( '\\mathbf', new Literal( 'B' ) );
$this->assertEquals( [ '\\mathbf{B}' ],
$f->extractSubscripts(), 'Should extract subscripts.' );
}
public function testExtractSubscriptsExtendedLits() {
$f = new Fun1( '\\mathbf', new Literal( '\\infty' ) );
$this->assertEquals( [ '\\mathbf{\\infty}' ], $f->extractSubscripts(),
'Should extract subscripts for extended literals.' );
}
public function testExtractSubscriptsEmptyMods() {
$f = new Fun1( '\\mathbf', new Literal( '' ) );
$this->assertEquals( [], $f->extractSubscripts(),
'Should not extract subscripts for empty mods.' );
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace MediaWiki\Extension\Math\Tests\TexVC\Nodes;
use ArgumentCountError;
use MediaWiki\Extension\Math\TexVC\Nodes\Fun1nb;
use MediaWiki\Extension\Math\TexVC\Nodes\Literal;
use MediaWikiUnitTestCase;
use TypeError;
/**
* @covers \MediaWiki\Extension\Math\TexVC\Nodes\Fun1nb
*/
class Fun1nbTest extends MediaWikiUnitTestCase {
public function testEmptyFun1nb() {
$this->expectException( ArgumentCountError::class );
new Fun1nb();
throw new ArgumentCountError( 'Should not create an empty fun1nb' );
}
public function testOneArgumentFun1nb() {
$this->expectException( ArgumentCountError::class );
new Fun1nb( '\\f' );
throw new ArgumentCountError( 'Should not create a fun1nb with one argument' );
}
public function testIncorrectTypeFun1nb() {
$this->expectException( TypeError::class );
new Fun1nb( '\\f', 'x' );
throw new TypeError( 'Should not create a fun1nb with incorrect type' );
}
public function testBasicFunctionFun1nb() {
$fq = new Fun1nb( '\\f', new Literal( 'a' ) );
$this->assertEquals( '\\f {a} ', $fq->render(), 'Should create a basic function' );
}
public function testCurliesFun1nb() {
$f = new Fun1nb( '\\f', new Literal( 'a' ) );
$this->assertEquals( '{\\f {a} }', $f->inCurlies(),
'Should create exactly one set of curlies' );
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace MediaWiki\Extension\Math\Tests\TexVC\Nodes;
use ArgumentCountError;
use MediaWiki\Extension\Math\TexVC\Nodes\Fun2;
use MediaWiki\Extension\Math\TexVC\Nodes\Literal;
use MediaWikiUnitTestCase;
use RuntimeException;
use TypeError;
/**
* @covers \MediaWiki\Extension\Math\TexVC\Nodes\Fun2
*/
class Fun2Test extends MediaWikiUnitTestCase {
public function testEmptyFun2() {
$this->expectException( ArgumentCountError::class );
new Fun2();
throw new ArgumentCountError( 'Should not create an empty fun2' );
}
public function testOneArgumentFun2() {
$this->expectException( ArgumentCountError::class );
new Fun2( '\\f' );
throw new ArgumentCountError( 'Should not create a fun2 with one argument' );
}
public function testIncorrectTypeFun2() {
$this->expectException( TypeError::class );
new Fun2( '\\f', 'x', 'y' );
throw new RuntimeException( 'Should not create a fun2 with incorrect types' );
}
public function testBasicFunctionFun2() {
$f = new Fun2( '\\f', new Literal( 'a' ), new Literal( 'b' ) );
$this->assertEquals( '{\\f {a}{b}}', $f->render(),
'Should create a basic function' );
}
public function testCurliesFun2() {
$f = new Fun2( '\\f', new Literal( 'a' ), new Literal( 'b' ) );
$this->assertEquals( '{\\f {a}{b}}', $f->inCurlies(),
'Should create exactly one set of curlies' );
}
public function testExtractIdentifiersFun2() {
$f = new Fun2( '\\f', new Literal( 'a' ), new Literal( 'b' ) );
$this->assertEquals( [ 'a','b' ], $f->extractIdentifiers(),
'Should extract identifiers' );
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace MediaWiki\Extension\Math\Tests\TexVC\Nodes;
use ArgumentCountError;
use MediaWiki\Extension\Math\TexVC\Nodes\Fun2nb;
use MediaWiki\Extension\Math\TexVC\Nodes\Literal;
use MediaWikiUnitTestCase;
use TypeError;
/**
* @covers \MediaWiki\Extension\Math\TexVC\Nodes\Fun2nb
*/
class Fun2nbTest extends MediaWikiUnitTestCase {
public function testEmptyFun2nb() {
$this->expectException( ArgumentCountError::class );
new Fun2nb();
throw new ArgumentCountError( 'Should not create an empty fun2nb' );
}
public function testOneArgumentFun2nb() {
$this->expectException( ArgumentCountError::class );
new Fun2nb( '\\f' );
throw new ArgumentCountError( 'Should not create a fun2nb with one argument' );
}
public function testIncorrectTypeFun2nb() {
$this->expectException( TypeError::class );
new Fun2nb( '\\f', 'x', 'y' );
throw new TypeError( 'Should not create a fun2nb with incorrect type' );
}
public function testBasicFunctionFun2nb() {
$fq = new Fun2nb( '\\f', new Literal( 'a' ), new Literal( 'b' ) );
$this->assertEquals( '\\f {a}{b}', $fq->render(), 'Should create a basic function' );
}
public function testCurliesFun2nb() {
$f = new Fun2nb( '\\f', new Literal( 'a' ), new Literal( 'b' ) );
$this->assertEquals( '{\\f {a}{b}}', $f->inCurlies(),
'Should create exactly one set of curlies' );
}
}

View file

@ -0,0 +1,77 @@
<?php
namespace MediaWiki\Extension\Math\Tests\TexVC\Nodes;
use MediaWiki\Extension\Math\TexVC\Nodes\Literal;
use MediaWiki\Extension\Math\TexVC\Nodes\TexArray;
use MediaWiki\Extension\Math\TexVC\Nodes\TexNode;
use MediaWikiUnitTestCase;
/**
* @covers \MediaWiki\Extension\Math\TexVC\Nodes\TexArray
*/
class TexArrayTest extends MediaWikiUnitTestCase {
public function testIsTexNode() {
$this->assertEquals( new TexArray() instanceof TexNode, true,
'Should create an instance of TexNode' );
}
public function testConcatOutput() {
$ta = new TexArray( new Literal( 'a' ), new Literal( 'b' ) );
$this->assertEquals( 'ab', $ta->render(), 'Should concatenate its input' );
}
public function testExtractCurlies() {
$n = new TexNode( new TexArray( new Literal( 'a' ) ) );
$this->assertEquals( '{a}', $n->inCurlies(),
'Should create exactly one pair of curlies' );
}
public function testExtractIdentifiers() {
$n = new TexArray( new Literal( 'd' ) );
$this->assertEquals( [ 'd' ], $n->extractIdentifiers(),
'Should extract identifiers' );
}
public function testExtractIdentifiersFromArg() {
$n = new TexArray();
$this->assertEquals( [ 'd' ],
$n->extractIdentifiers( [ new Literal( 'd' ) ] ),
'Should extract identifiers from the argument' );
}
public function testExtractSplitIdentifiers() {
$n = new TexArray( new Literal( 'a' ), new Literal( '\'' ) );
$this->assertEquals( [ 'a\'' ], $n->extractIdentifiers(),
'Should extract split identifiers' );
}
public function testNotConfuseIntegralsIdentifiers() {
$n = new TexArray( new Literal( 'd' ), new Literal( '\\int' ) );
$this->assertEquals( [ 'd' ], $n->extractIdentifiers(),
'Should not confuse integrals and identifiers' );
}
public function testNotConfuseIntegralD() {
$n = new TexArray( new Literal( '\\int' ), new Literal( 'd' ) );
$this->assertEquals( [], $n->extractIdentifiers(), 'Should not confuse integral d with d identifier' );
}
public function testNotConfuseUprightIntegralD() {
$n = new TexArray( new Literal( '\\int' ), new Literal( '\\mathrm{d}' ) );
$this->assertEquals( [], $n->extractIdentifiers(),
'Should not confuse upright integral d with d identifier' );
}
public function testExtractIdentifierMods() {
$n = new TexArray( new TexNode( '' ) );
$this->assertEquals( [], $n->getModIdent(), 'Should extract identifier modifications' );
}
public function testExtractSubscripts() {
$n = new TexArray( new TexNode( '' ) );
$this->assertEquals( [], $n->extractSubscripts(), 'Should extract subscripts' );
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace MediaWiki\Extension\Math\Tests\TexVC\Nodes;
use ArgumentCountError;
use MediaWiki\Extension\Math\TexVC\Nodes\Literal;
use MediaWiki\Extension\Math\TexVC\Nodes\TexNode;
use MediaWiki\Extension\Math\TexVC\Nodes\UQ;
use MediaWikiUnitTestCase;
use RuntimeException;
use TypeError;
/**
* @covers \MediaWiki\Extension\Math\TexVC\Nodes\UQ
*/
class UQTest extends MediaWikiUnitTestCase {
public function testEmptyUQ() {
$this->expectException( ArgumentCountError::class );
new UQ();
throw new ArgumentCountError( 'Should not create an empty uq' );
}
public function testOneArgumentUQ() {
$this->expectException( ArgumentCountError::class );
new UQ( new Literal( 'a' ) );
throw new ArgumentCountError( 'Should not create a uq with one argument' );
}
public function testIncorrectTypeUQ() {
$this->expectException( TypeError::class );
new UQ( 'a', 'b' );
throw new RuntimeException( 'Should not create a uq with incorrect type' );
}
public function testBasicUQ() {
$uq = new UQ( new Literal( 'a' ), new Literal( 'b' ) );
$this->assertEquals( 'a^{b}', $uq->render(), 'Should create a basic uq' );
}
public function testEmptyBaseUQ() {
$uq = new UQ( new TexNode(), new Literal( 'b' ) );
$this->assertEquals( '^{b}', $uq->render(), 'Should create an empty base uq' );
}
}