Clean matrix arguments

Originally the parse tree was a binary tree, from that
time matrix element were put into that data structure.
This was partially undone, but not full which leads to some artifacts
in matrix cells.

One of the problem is that due to the binary parse
tree structure the postprocessing did not correctly
identfy \limits.

This change changes reduces nesting from matrix, and regards
matrix instances as two-dimensional TexArrays.

Bug: T362344
Change-Id: I66a31a09f204709a51d6b5c9ecefc083f7ee2d6d
This commit is contained in:
Moritz Schubotz (physikerwelt) 2024-04-12 17:43:24 +02:00 committed by Physikerwelt
parent 3bb26a3873
commit 5fa3d6bfe5
13 changed files with 219 additions and 131 deletions

View file

@ -118,7 +118,8 @@ class BaseParsing {
return $output;
}
public static function alignAt( $node, $passedArgs, $operatorContent, $name, $smth, $smth2 = null ) {
public static function alignAt( Matrix $node, $passedArgs, $operatorContent, $name, $smth,
$smth2 = null ) {
// Parsing is very similar to AmsEQArray, maybe extract function ... tcs: 178
$mrow = new MMLmrow();
// tbd how are the table args composed ?
@ -129,23 +130,10 @@ class BaseParsing {
$mtd = new MMLmtd();
$renderedInner = "";
$tableElements = array_slice( $node->getArgs(), 1 )[0];
$discarded = false;
foreach ( $tableElements->getArgs() as $tableRow ) {
foreach ( $node as $tableRow ) {
$renderedInner .= $mtr->getStart();
foreach ( $tableRow->getArgs() as $tableCell ) {
$renderedInner .= $mtd->getStart();
foreach ( $tableCell->getArgs() as $cellItem ) {
if ( !$discarded && $cellItem->isCurly() ) {
$discarded = true;
// Just discard the number of rows atm, it is in the first Curly
} else {
$renderedInner .= $cellItem->renderMML(); // pass args here ?
}
}
$renderedInner .= $mtd->getEnd();
$renderedInner .= $mtd->getStart() . $tableCell->renderMML() . $mtd->getEnd();
}
$renderedInner .= $mtr->getEnd();
}
@ -164,8 +152,7 @@ class BaseParsing {
$mtr = new MMLmtr();
$mtd = new MMLmtd();
$renderedInner = "";
$tableElements = array_slice( $node->getArgs(), 1 )[0];
foreach ( $tableElements->getArgs() as $tableRow ) {
foreach ( $node as $tableRow ) {
$renderedInner .= $mtr->getStart();
foreach ( $tableRow->getArgs() as $tableCell ) {
$renderedInner .= $mtd->encapsulateRaw( $tableCell->renderMML() ); // pass args here ?
@ -534,34 +521,25 @@ class BaseParsing {
return $mmlMrow->encapsulate( "macro not resolved: " . $macro );
}
public static function matrix( $node, $passedArgs, $operatorContent,
public static function matrix( Matrix $node, $passedArgs, $operatorContent,
$name, $open = null, $close = null, $align = null, $spacing = null,
$vspacing = null, $style = null, $cases = null, $numbered = null ) {
$resInner = "";
$mtr = new MMLmtr();
$mtd = new MMLmtd();
$addHlines = false;
$columnInfo = [];
// tbd hline element is the first literal element within second texarray -> resolve
foreach ( $node->getMainarg()->getArgs() as $mainarg ) {
$columnInfo = $node->getColumnSpecs()->render();
foreach ( $node as $row ) {
$resInner .= $mtr->getStart();
foreach ( $mainarg->getArgs() as $arg ) {
$usedArg = clone $arg;
if ( count( $arg->getArgs() ) >= 1 && $arg->getArgs()[0] instanceof Literal ) {
// Discarding the column information Curly at the moment
if ( $arg->getArgs()[0]->getArg() == "\\hline " ) {
// discarding the hline
// $usedArg->args[0] = null; // this does no work tbd
$usedArg->pop();
$addHlines = true;
}
}
if ( count( $arg->getArgs() ) >= 1 && $arg->getArgs()[0]->isCurly() ) {
// Discarding the column information Curly at the moment
// $usedArg->getArgs()[0] = null;
$columnInfo = $usedArg->getArgs()[0]->render();
foreach ( $row as $cell ) {
$usedArg = clone $cell;
if ( $usedArg instanceof TexArray &&
$usedArg->getLength() >= 1 &&
$usedArg[0] instanceof Literal &&
$usedArg[0]->getArg() === '\\hline '
) {
$usedArg->pop();
$addHlines = true;
}
$resInner .= $mtd->encapsulateRaw( $usedArg->renderMML( $passedArgs, [ 'inMatrix'
=> true ]
@ -1123,7 +1101,7 @@ class BaseParsing {
// tbd refine intent params and operator content merging (does it overwrite ??)
$intentParamsState = $intentParams ? [ "intent-params" => $intentParams ] : $operatorContent;
// Here are some edge cases, they might go into renderMML in the related element
if ( str_contains( $intentContent, "matrix" ) ||
if ( str_contains( $intentContent ?? '', "matrix" ) ||
( $arg1->isCurly() && $arg1->getArgs()[0] instanceof Matrix ) ) {
$element = $arg1->getArgs()[0];
$rendered = $element->renderMML( [], $intentParamsState );

View file

@ -83,7 +83,7 @@ class MMLutil {
*/
public static function squashLitsToUnit( TexArray $node ): ?string {
$unit = "";
foreach ( $node->getArgs() as $literal ) {
foreach ( $node as $literal ) {
if ( !$literal instanceof Literal ) {
continue;
}

View file

@ -4,14 +4,15 @@ declare( strict_types = 1 );
namespace MediaWiki\Extension\Math\WikiTexVC\Nodes;
use Generator;
use InvalidArgumentException;
class Matrix extends TexNode {
class Matrix extends TexArray {
/** @var string */
private $top;
/** @var TexArray */
private $mainarg;
private ?TexArray $columnSpecs = null;
/**
* @param string $top
@ -19,14 +20,18 @@ class Matrix extends TexNode {
* @throws InvalidArgumentException if nested arguments are not of type TexArray
*/
public function __construct( string $top, TexArray $mainarg ) {
foreach ( $mainarg->args as $arg ) {
if ( !$arg instanceof TexArray ) {
foreach ( $mainarg->args as $row ) {
if ( !$row instanceof TexArray ) {
throw new InvalidArgumentException( 'Nested arguments have to be type of TexArray' );
}
}
parent::__construct( $top, $mainarg );
if ( $mainarg instanceof Matrix ) {
$this->args = $mainarg->args;
$this->curly = $mainarg->curly;
} else {
parent::__construct( ...$mainarg->args );
}
$this->top = $top;
$this->mainarg = $mainarg;
}
/**
@ -36,11 +41,25 @@ class Matrix extends TexNode {
return $this->top;
}
public function setTop( string $top ): Matrix {
$this->top = $top;
return $this;
}
public function getColumnSpecs(): TexArray {
return $this->columnSpecs ?? new TexArray();
}
public function setColumnSpecs( TexArray $specs ): Matrix {
$this->columnSpecs = $specs;
return $this;
}
/**
* @return TexArray
*/
public function getMainarg(): TexArray {
return $this->mainarg;
return $this;
}
public function containsFunc( $target, $args = null ) {
@ -48,7 +67,7 @@ class Matrix extends TexNode {
$args = [
'\\begin{' . $this->top . '}',
'\\end{' . $this->top . '}',
$this->mainarg
...$this->args,
];
}
return parent::containsFunc( $target, $args );
@ -59,7 +78,9 @@ class Matrix extends TexNode {
}
public function render() {
return '{\\begin{' . $this->top . '}' . $this->renderMatrix( $this->mainarg ) . '\\end{' . $this->top . '}}';
$colSpecs = $this->columnSpecs !== null ? $this->columnSpecs->render() : '';
return '{\\begin{' . $this->top . '}' . $colSpecs . $this->renderMatrix( $this ) . '\\end{' .
$this->top . '}}';
}
public function renderMML( $arguments = [], $state = [] ): string {
@ -80,7 +101,7 @@ class Matrix extends TexNode {
public function extractIdentifiers( $args = null ) {
if ( $args == null ) {
$args = [ $this->mainarg ];
$args = $this->args;
}
$mapped = array_map( function ( $a ){
@ -110,4 +131,12 @@ class Matrix extends TexNode {
return array_merge( $acc, $fld );
}
/**
* @suppress PhanTypeMismatchReturn
* @return Generator<TexArray>
*/
public function getIterator(): Generator {
return parent::getIterator();
}
}

View file

@ -15,7 +15,10 @@ use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmstyle;
use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmsup;
use MediaWiki\Extension\Math\WikiTexVC\TexUtil;
class TexArray extends TexNode implements \IteratorAggregate {
/**
*
*/
class TexArray extends TexNode implements \ArrayAccess, \IteratorAggregate {
protected bool $curly = false;
public static function newCurly( ...$args ) {
@ -168,7 +171,7 @@ class TexArray extends TexNode implements \IteratorAggregate {
$hasNamedFct = false;
if ( $currentNode instanceof TexArray && count( $currentNode->args ) == 2 ) {
$tu = TexUtil::getInstance();
$currentNodeContent = $currentNode->getArgs()[0];
$currentNodeContent = $currentNode[0];
if ( $currentNodeContent instanceof Literal &&
$tu->latex_function_names( $currentNodeContent->getArg() ) ) {
$hasNamedFct = true;
@ -287,7 +290,7 @@ class TexArray extends TexNode implements \IteratorAggregate {
foreach ( array_reverse( $mmlStyles ) as $mmlStyleEnd ) {
$fullRenderedArray .= $mmlStyleEnd;
}
if ( $this->curly && count( $this->getArgs() ) > 1 ) {
if ( $this->curly && $this->getLength() > 1 ) {
$mmlRow = new MMLmrow();
return $mmlRow->encapsulateRaw( $fullRenderedArray );
}
@ -421,6 +424,7 @@ class TexArray extends TexNode implements \IteratorAggregate {
self::checkInput( $elements );
array_push( $this->args, ...$elements );
return $this;
}
public function pop() {
@ -489,4 +493,30 @@ class TexArray extends TexNode implements \IteratorAggregate {
public function getIterator(): Generator {
yield from $this->args;
}
/**
* @return TexNode[]
*/
public function getArgs(): array {
return parent::getArgs();
}
public function offsetExists( $offset ): bool {
return isset( $this->args[$offset] );
}
public function offsetGet( $offset ): ?TexNode {
return $this->args[$offset] ?? null;
}
public function offsetSet( $offset, $value ): void {
if ( !( $value instanceof TexNode ) ) {
throw new InvalidArgumentException( 'TexArray elements must be of type TexNode.' );
}
$this->args[$offset] = $value;
}
public function offsetUnset( $offset ): void {
unset( $this->args[$offset] );
}
}

View file

@ -75,7 +75,7 @@ class TexNode {
return true;
}
public function getLength(): ?int {
public function getLength(): int {
if ( isset( $this->args[0] ) ) {
return count( $this->args );
} else {

View file

@ -55,12 +55,12 @@ class UQ extends TexNode {
// If the superscript has empty elements, render them with empty mi elements to prevent browser issues
$mi = new MMLmi();
if ( $base instanceof TexArray && count( $base->getArgs() ) == 0 ) {
if ( $base instanceof TexArray && $base->getLength() == 0 ) {
$baseRendered = $mi->getEmpty();
} else {
$baseRendered = $base->renderMML( $arguments, $state );
}
if ( $up instanceof TexArray && count( $up->getArgs() ) == 0 ) {
if ( $up instanceof TexArray && $up->getLength() == 0 ) {
$upRendered = $mi->getEmpty();
} else {
// up is inferring a new mrow if it has some content

View file

@ -442,14 +442,14 @@ class Parser {
private function peg_f23($f) {
$parser = new Parser();
$ast = $parser->parse($this->tu->nullary_macro_aliase($f), $this->options);
assert($ast instanceof TexArray && count($ast->getArgs()) === 1);
assert($ast instanceof TexArray && $ast->getLength() === 1);
return $ast->first();
}
private function peg_f24($f) { return $this->tu->deprecated_nullary_macro_aliase($f); }
private function peg_f25($f) {
$parser = new Parser();
$ast = $parser->parse($this->tu->deprecated_nullary_macro_aliase($f), $this->options);
assert($ast instanceof TexArray && count($ast->getArgs()) === 1);
assert($ast instanceof TexArray && $ast->getLength() === 1);
if ($this->options['oldtexvc']){
return $ast->first();
} else {
@ -469,38 +469,36 @@ class Parser {
private function peg_f35($name, $l1, $l2) { return new Fun2nb($name, $l1, $l2); }
private function peg_f36($e) { return $e->setCurly(); }
private function peg_f37($e1, $name, $e2) { return new Infix($name, $e1, $e2); }
private function peg_f38($m) { return new Matrix("matrix", ParserUtil::lst2arr($m)); }
private function peg_f39($m) { return new Matrix("pmatrix", ParserUtil::lst2arr($m)); }
private function peg_f40($m) { return new Matrix("bmatrix", ParserUtil::lst2arr($m)); }
private function peg_f41($m) { return new Matrix("Bmatrix", ParserUtil::lst2arr($m)); }
private function peg_f42($m) { return new Matrix("vmatrix", ParserUtil::lst2arr($m)); }
private function peg_f43($m) { return new Matrix("Vmatrix", ParserUtil::lst2arr($m)); }
private function peg_f44($m) { return new Matrix("array", ParserUtil::lst2arr($m)); }
private function peg_f45($m) { return new Matrix("aligned", ParserUtil::lst2arr($m)); }
private function peg_f46($m) { return new Matrix("alignedat", ParserUtil::lst2arr($m)); }
private function peg_f47($m) { return new Matrix("smallmatrix", ParserUtil::lst2arr($m)); }
private function peg_f48($m) { return new Matrix("cases", ParserUtil::lst2arr($m)); }
private function peg_f38($m) { return $m->setTop( 'matrix' ); }
private function peg_f39($m) { return $m->setTop( 'pmatrix' ); }
private function peg_f40($m) { return $m->setTop( 'bmatrix' ); }
private function peg_f41($m) { return $m->setTop( 'Bmatrix' ); }
private function peg_f42($m) { return $m->setTop( 'vmatrix' ); }
private function peg_f43($m) { return $m->setTop( 'Vmatrix' ); }
private function peg_f44($m) { return $m->setTop( 'array' ); }
private function peg_f45($m) { return $m->setTop( 'aligned' ); }
private function peg_f46($m) { return $m->setTop( 'alignedat' ); }
private function peg_f47($m) { return $m->setTop( 'smallmatrix' ); }
private function peg_f48($m) { return $m->setTop( 'cases' ); }
private function peg_f49() { throw new SyntaxError("Illegal TeX function", [], $this->text(), $this->offset(),
$this->line(), $this->column()); }
private function peg_f50($f) { return !$this->tu->getAllFunctionsAt($f); }
private function peg_f51($f) { throw new SyntaxError("Illegal TeX function", [], $f, $this->offset(), $this->line(), $this->column()); }
private function peg_f52($cs, $m) {
if ($m->getLength() ) {
$m->first()->first()->unshift($cs);
return $m;
}
return new TexArray(new TexArray($cs));
}
private function peg_f53($as, $m) { $m->first()->first()->unshift($as); return $m; }
private function peg_f52($cs, $m) { return $m->setColumnSpecs( $cs ); }
private function peg_f53($as, $m) { return $m->setColumnSpecs( $as ); }
private function peg_f54($l, $m) { return $m; }
private function peg_f55($l, $tail) { return new TexArray( ParserUtil::lst2arr($l), $tail ); }
private function peg_f55($l, $tail) { if ($tail === null) { return new Matrix( 'matrix', new TexArray( $l ) ); }
return new Matrix( 'matrix', $tail->unshift($l) ); }
private function peg_f56($f, $l) {
if ($l->first() === null ) {
$l->push(new TexArray());
}
$l->first()->unshift(new Literal($f . " ")); return $l;}
private function peg_f57($e, $l) { return $l; }
private function peg_f58($e, $tail) { return new TexArray($e, $tail); }
private function peg_f58($e, $tail) {
if ($tail === null) { return new TexArray( $e ) ; }
return $tail->unshift($e);
}
private function peg_f59() { return $this->text(); }
private function peg_f60($cs) { return TexArray::newCurly(new Literal($cs)); }
private function peg_f61($num) { return TexArray::newCurly(new Literal($num)); }

View file

@ -4,29 +4,8 @@ declare( strict_types = 1 );
namespace MediaWiki\Extension\Math\WikiTexVC;
use MediaWiki\Extension\Math\WikiTexVC\Nodes\TexArray;
class ParserUtil {
/**
* @param TexArray|null $l
* @param bool $curly
* @return TexArray
*/
public static function lst2arr( $l, $curly = false ) {
$arr = $curly ? TexArray::newCurly() : new TexArray();
while ( $l !== null ) {
$first = $l->first();
if ( $first !== null ) {
$arr->push( $l->first() );
}
$l = $l->second();
}
return $arr;
}
/**
* @param array|null $options
* @return array

View file

@ -122,14 +122,14 @@ lit
{
$parser = new Parser();
$ast = $parser->parse($this->tu->nullary_macro_aliase($f), $this->options);
assert($ast instanceof TexArray && count($ast->getArgs()) === 1);
assert($ast instanceof TexArray && $ast->getLength() === 1);
return $ast->first();
}
/ f:generic_func &{ return $this->tu->deprecated_nullary_macro_aliase($f); } _ // from Texutil.find(...)
{
$parser = new Parser();
$ast = $parser->parse($this->tu->deprecated_nullary_macro_aliase($f), $this->options);
assert($ast instanceof TexArray && count($ast->getArgs()) === 1);
assert($ast instanceof TexArray && $ast->getLength() === 1);
if ($this->options['oldtexvc']){
return $ast->first();
} else {
@ -155,31 +155,31 @@ lit
/ CURLY_OPEN e1:ne_expr name:FUN_INFIX e2:ne_expr CURLY_CLOSE
{ return new Infix($name, $e1, $e2); }
/ BEGIN_MATRIX m:(array/matrix) END_MATRIX
{ return new Matrix("matrix", ParserUtil::lst2arr($m)); }
{ return $m->setTop( 'matrix' ); }
/ BEGIN_PMATRIX m:(array/matrix) END_PMATRIX
{ return new Matrix("pmatrix", ParserUtil::lst2arr($m)); }
{ return $m->setTop( 'pmatrix' ); }
/ BEGIN_BMATRIX m:(array/matrix) END_BMATRIX
{ return new Matrix("bmatrix", ParserUtil::lst2arr($m)); }
{ return $m->setTop( 'bmatrix' ); }
/ BEGIN_BBMATRIX m:(array/matrix) END_BBMATRIX
{ return new Matrix("Bmatrix", ParserUtil::lst2arr($m)); }
{ return $m->setTop( 'Bmatrix' ); }
/ BEGIN_VMATRIX m:(array/matrix) END_VMATRIX
{ return new Matrix("vmatrix", ParserUtil::lst2arr($m)); }
{ return $m->setTop( 'vmatrix' ); }
/ BEGIN_VVMATRIX m:(array/matrix) END_VVMATRIX
{ return new Matrix("Vmatrix", ParserUtil::lst2arr($m)); }
{ return $m->setTop( 'Vmatrix' ); }
/ BEGIN_ARRAY opt_pos m:array END_ARRAY
{ return new Matrix("array", ParserUtil::lst2arr($m)); }
{ return $m->setTop( 'array' ); }
/ BEGIN_ALIGN opt_pos m:matrix END_ALIGN
{ return new Matrix("aligned", ParserUtil::lst2arr($m)); }
{ return $m->setTop( 'aligned' ); }
/ BEGIN_ALIGNED opt_pos m:matrix END_ALIGNED // parse what we emit
{ return new Matrix("aligned", ParserUtil::lst2arr($m)); }
{ return $m->setTop( 'aligned' ); }
/ BEGIN_ALIGNAT m:alignat END_ALIGNAT
{ return new Matrix("alignedat", ParserUtil::lst2arr($m)); }
{ return $m->setTop( 'alignedat' ); }
/ BEGIN_ALIGNEDAT m:alignat END_ALIGNEDAT // parse what we emit
{ return new Matrix("alignedat", ParserUtil::lst2arr($m)); }
{ return $m->setTop( 'alignedat' ); }
/ BEGIN_SMALLMATRIX m:(array/matrix) END_SMALLMATRIX
{ return new Matrix("smallmatrix", ParserUtil::lst2arr($m)); }
{ return $m->setTop( 'smallmatrix' ); }
/ BEGIN_CASES m:matrix END_CASES
{ return new Matrix("cases", ParserUtil::lst2arr($m)); }
{ return $m->setTop( 'cases' ); }
/ "\\begin{" alpha+ "}" /* better error messages for unknown environments */
{ throw new SyntaxError("Illegal TeX function", [], $this->text(), $this->offset(),
$this->line(), $this->column()); }
@ -190,23 +190,18 @@ lit
// "array" requires mandatory column specification
array
= cs:column_spec m:matrix
{
if ($m->getLength() ) {
$m->first()->first()->unshift($cs);
return $m;
}
return new TexArray(new TexArray($cs));
}
{ return $m->setColumnSpecs( $cs ); }
// "alignat" requires mandatory # of columns
alignat
= as:alignat_spec m:matrix
{ $m->first()->first()->unshift($as); return $m; }
{ return $m->setColumnSpecs( $as ); }
// "matrix" does not require column specification
matrix
= l:line_start tail:( NEXT_ROW m:matrix { return $m; } )?
{ return new TexArray( ParserUtil::lst2arr($l), $tail ); }
{ if ($tail === null) { return new Matrix( 'matrix', new TexArray( $l ) ); }
return new Matrix( 'matrix', $tail->unshift($l) ); }
line_start
= f:HLINE l:line_start
{
@ -217,7 +212,10 @@ line_start
/ line
line
= e:expr tail:( NEXT_CELL l:line { return $l; } )?
{ return new TexArray($e, $tail); }
{
if ($tail === null) { return new TexArray( $e ) ; }
return $tail->unshift($e);
}
column_spec
= CURLY_OPEN cs:(one_col+ { return $this->text(); }) CURLY_CLOSE

View file

@ -567,7 +567,7 @@ class AllTest extends MediaWikiUnitTestCase {
'oldtexvc' => $tc['oldtexvc'] ?? false
] );
$this->assertEquals( '+', $result['status'], $message );
$this->assertEquals( $result['output'], $tc['output'], $message );
$this->assertEquals( $tc['output'], $result['output'], $message );
}
if ( !array_key_exists( 'skipReparse', $tc ) || !$tc['skipReparse'] ) {
// verify that the output doesn't change if we feed it

View file

@ -108,4 +108,11 @@ class BaseParsingTest extends TestCase {
[ 'k' => '"<script>alert("problem")</script>"' ], null, 'oXXX', '00AF' );
$this->assertStringContainsString( 'k="&quot;&lt;script&gt;alert(&quot;problem&quot;)', $result );
}
public function testAlignAt() {
$matrix = new Matrix( 'alignat',
new TexArray( new TexArray( new Literal( '\\sin' ) ) ) );
$result = BaseParsing::alignAt( $matrix, [], null, 'alignat', '002A' );
$this->assertStringContainsString( 'mtable', $result );
}
}

View file

@ -37,11 +37,10 @@ class MatrixTest extends MediaWikiUnitTestCase {
throw new TypeError( 'Nested arguments have to be type of TexArray' );
}
public function testInstanceOfTexNode() {
// this was an instance of TexNode validation didnt verify in php, for review: is this workaround sufficient ?
$this->assertEquals( 'MediaWiki\\Extension\\Math\\WikiTexVC\\Nodes\\TexNode',
public function testInstanceOfTexArray() {
$this->assertEquals( 'MediaWiki\\Extension\\Math\\WikiTexVC\\Nodes\\TexArray',
get_parent_class( $this->sampleMatrix ),
'Should create an instance of TexNode' );
'Should create an instance of TexArray' );
}
public function testGetters() {
@ -62,4 +61,43 @@ class MatrixTest extends MediaWikiUnitTestCase {
$this->assertEquals( [ 'a' ], $this->sampleMatrix->extractIdentifiers(),
'Should extract identifiers' );
}
public function testCreateFromMatrix() {
$matrix = new Matrix( 'align',
new TexArray( new TexArray( new Literal( 'a' ) ) ) );
$newMatrix = new Matrix( 'align', $matrix );
$this->assertEquals( $matrix, $newMatrix,
'Should create a matrix from a matrix' );
}
public function testContainsTop() {
$matrix = new Matrix( 'top',
new TexArray( new TexArray( new Literal( 'a' ) ) ) );
$this->assertTrue( $matrix->containsFunc( '\\begin{top}' ),
'Should create top attribute' );
}
public function testContainsArgs() {
$matrix = new Matrix( 'top',
new TexArray( new TexArray( new Literal( '\\sin' ) ) ) );
$this->assertTrue( $matrix->containsFunc( '\\sin' ),
'Should contain inner elements' );
}
public function testRenderMML() {
$matrix = new Matrix( 'matrix',
new TexArray( new TexArray( new Literal( '\\sin' ) ) ) );
$this->assertStringContainsString( 'mtable', $matrix->renderMML(),
'Should render a matrix' );
}
public function testTop() {
$this->sampleMatrix->setTop( 'abc' );
$this->assertEquals( 'abc', $this->sampleMatrix->getTop() );
}
public function testColSpec() {
$this->sampleMatrix->setColumnSpecs( TexArray::newCurly( new Literal( '2' ) ) );
$this->assertEquals( '{2}', $this->sampleMatrix->getColumnSpecs()->render() );
}
}

View file

@ -2,6 +2,7 @@
namespace MediaWiki\Extension\Math\Tests\WikiTexVC\Nodes;
use InvalidArgumentException;
use MediaWiki\Extension\Math\WikiTexVC\Nodes\Literal;
use MediaWiki\Extension\Math\WikiTexVC\Nodes\TexArray;
use MediaWiki\Extension\Math\WikiTexVC\Nodes\TexNode;
@ -89,4 +90,34 @@ class TexArrayTest extends MediaWikiUnitTestCase {
'Should iterate over the elements' );
}
}
public function testOffsetExists() {
$ta = new TexArray( new Literal( 'a' ), new Literal( 'b' ) );
$this->assertTrue( isset( $ta[0] ) );
$this->assertFalse( isset( $ta[2] ) );
}
public function testOffsetGet() {
$ta = new TexArray( new Literal( 'a' ), new Literal( 'b' ) );
$this->assertEquals( 'a', $ta[0]->render() );
$this->assertNull( $ta[100] );
}
public function testOffsetUnset() {
$ta = new TexArray( new Literal( 'a' ), new Literal( 'b' ) );
unset( $ta[0] );
$this->assertNull( $ta[0] );
}
public function testOffsetSet() {
$ta = new TexArray();
$ta[0] = new Literal( 'a' );
$this->assertEquals( 'a', $ta[0]->render() );
}
public function testOffsetSetInvalid() {
$this->expectException( InvalidArgumentException::class );
$ta = new TexArray();
$ta[0] = 'a';
}
}