mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Math
synced 2024-11-28 09:20:42 +00:00
Add TexVC with Check functionality
Bug: T312528 Change-Id: Ib66a4603e3c1869d5c197b72b46b098d0df15e34
This commit is contained in:
parent
740c07e869
commit
e091c77593
171
src/TexVC/TexVC.php
Normal file
171
src/TexVC/TexVC.php
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare( strict_types = 1 );
|
||||||
|
|
||||||
|
namespace MediaWiki\Extension\Math\TexVC;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use MediaWiki\Extension\Math\TexVC\Nodes\TexArray;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A TeX/LaTeX validator.
|
||||||
|
* TexVC(PHP) takes user input and validates it while replacing
|
||||||
|
* MediaWiki-specific functions. It is a PHP port of the JavaScript port of texvc,
|
||||||
|
* which was originally written in Ocaml for the Math extension.
|
||||||
|
*
|
||||||
|
* @author Johannes Stegmüller
|
||||||
|
*/
|
||||||
|
class TexVC {
|
||||||
|
/** @var Parser */
|
||||||
|
private $parser;
|
||||||
|
/** @var TexUtil */
|
||||||
|
private $tu;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->parser = new Parser();
|
||||||
|
$this->tu = new TexUtil();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function strStartsWith( $haystack, $needle ): bool {
|
||||||
|
return strpos( $haystack, $needle ) === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Usually this step is done implicitly within the check-method.
|
||||||
|
* @param string $input tex-string as input for the grammar
|
||||||
|
* @param null|array $options array options for the grammar.
|
||||||
|
* @return mixed output of the grammar.
|
||||||
|
* @throws SyntaxError when SyntaxError in the input
|
||||||
|
*/
|
||||||
|
public function parse( $input, $options = null ) {
|
||||||
|
return $this->parser->parse( $input, $options );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** status is one character:
|
||||||
|
* + : success! result is in 'output'
|
||||||
|
* E : Lexer exception raised
|
||||||
|
* F : TeX function not recognized
|
||||||
|
* S : Parsing error
|
||||||
|
* - : Generic/Default failure code. Might be an invalid argument,
|
||||||
|
* output file already exist, a problem with an external
|
||||||
|
* command ...
|
||||||
|
* @param string|TexArray|stdClass $input tex to be checked as string,
|
||||||
|
* can also be the output of former parser call
|
||||||
|
* @param null|array $options array options for settings of the check
|
||||||
|
* @param array &$warnings reference on warnings occurring during the check
|
||||||
|
* @return array|string[] output with information status (see above)
|
||||||
|
* @throws Exception in case of a major problem with the check and activated debug option.
|
||||||
|
*/
|
||||||
|
public function check( $input, $options = null, &$warnings = [] ) {
|
||||||
|
try{
|
||||||
|
$options = ParserUtil::createOptions( $options );
|
||||||
|
if ( is_string( $input ) ) {
|
||||||
|
$input = $this->parser->parse( $input, $options );
|
||||||
|
}
|
||||||
|
$output = $input->render();
|
||||||
|
$result = [
|
||||||
|
'status' => '+',
|
||||||
|
'output' => $output,
|
||||||
|
'warnings' => $warnings,
|
||||||
|
'input' => $input,
|
||||||
|
'success' => true,
|
||||||
|
];
|
||||||
|
|
||||||
|
$pkgs = [ 'ams', 'cancel', 'color', 'euro', 'teubner', 'mhchem', 'mathoid' ];
|
||||||
|
|
||||||
|
foreach ( $pkgs as $pkg ) {
|
||||||
|
$pkg .= '_required';
|
||||||
|
$tuRef = $this->tu->getBaseElements()[$pkg];
|
||||||
|
$result[$pkg] = $input->containsFunc( $tuRef );
|
||||||
|
}
|
||||||
|
if ( !$options['usemhchem'] ) {
|
||||||
|
if ( $result['mhchem_required'] ) {
|
||||||
|
return [
|
||||||
|
'status' => 'C',
|
||||||
|
'details' => 'mhchem package required.'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
} catch ( Exception $ex ) {
|
||||||
|
if ( $ex instanceof SyntaxError && !$options['oldtexvc']
|
||||||
|
&& $this->strStartsWith( $ex->getMessage(), 'Deprecation' ) ) {
|
||||||
|
|
||||||
|
$warnings[] = [
|
||||||
|
'type' => 'texvc-deprecation',
|
||||||
|
'details' => $this->handleTexError( $ex, $options )
|
||||||
|
];
|
||||||
|
$options['oldtexvc'] = true;
|
||||||
|
return $this->check( $input, $options, $warnings );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $ex instanceof SyntaxError && $options['usemhchem'] && !$options['oldmhchem'] ) {
|
||||||
|
$warnings[] = [
|
||||||
|
'type' => 'mhchem-deprecation',
|
||||||
|
'details' => $this->handleTexError( $ex, $options )
|
||||||
|
];
|
||||||
|
$options['oldmhchem'] = true;
|
||||||
|
return $this->check( $input, $options, $warnings );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->handleTexError( $ex, $options );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleTexError( Exception $e, $options = null ) {
|
||||||
|
if ( $options && $options['debug'] ) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
$report = [ 'success' => false, 'warnings' => [] ];
|
||||||
|
if ( $e instanceof SyntaxError ) {
|
||||||
|
if ( $e->getMessage() === 'Illegal TeX function' ) {
|
||||||
|
$report['status'] = 'F';
|
||||||
|
$report['details'] = $e->found;
|
||||||
|
$report += $this->getLocationInfo( $e );
|
||||||
|
} else {
|
||||||
|
$report['status'] = 'S';
|
||||||
|
$report['details'] = $e->getMessage();
|
||||||
|
$report += $this->getLocationInfo( $e );
|
||||||
|
}
|
||||||
|
$report['error'] = [
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
'expected' => $e->expected,
|
||||||
|
'found' => $e->found,
|
||||||
|
'location' => [
|
||||||
|
/** This currently only has the start location, since end is not noted in SyntaxError in PHP
|
||||||
|
* this issue is tracked in: https://phabricator.wikimedia.org/T321060
|
||||||
|
*/
|
||||||
|
'offset' => $e->grammarOffset,
|
||||||
|
'line' => $e->grammarLine,
|
||||||
|
'column' => $e->grammarColumn
|
||||||
|
],
|
||||||
|
'name' => $e->name
|
||||||
|
];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$report['status'] = '-';
|
||||||
|
$report['details'] = $e->getMessage();
|
||||||
|
$report['error'] = $e;
|
||||||
|
}
|
||||||
|
return $report;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the location information of an error object, or returns default error
|
||||||
|
* location if no location information was specified.
|
||||||
|
* @param SyntaxError $e error object
|
||||||
|
* @return array information on the error.
|
||||||
|
*/
|
||||||
|
private function getLocationInfo( SyntaxError $e ) {
|
||||||
|
try {
|
||||||
|
return [
|
||||||
|
'offset' => $e->grammarOffset,
|
||||||
|
'line' => $e->grammarLine,
|
||||||
|
'column' => $e->grammarColumn
|
||||||
|
];
|
||||||
|
} catch ( Exception $err ) {
|
||||||
|
return [ 'offset' => 0, 'line' => 0, 'column' => 0 ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue