Implement mhchemParser in PHP
* mhchemParser v4.2.2 in Typescript was used as blueprint for this: * https://github.com/mhchem/mhchemParser * The mhchemParserPHP component tests can be run locally on a machine with PHP without MediaWiki-Environment with the MMLmhchemTestLocal.php * For the tests it is probably only necessary to review the json files (Mhchemv4mml.json, Mhchemv4tex.json). * src/TexVC/MHChem/MhchemParser.php contains the basic functionality, Patterns, StateMachines, Texify functions are located in dedicated classes * added extracted mhchem commands which have intermediately have been introduced to the texVC(PHP) grammar Change-Id: I8cc3d04937b93339e352adc95c85a8a178b8825f Bug: T329620
这个提交包含在:
父节点
8c5a694656
当前提交
6514c9d24e
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
|||
src/TexVC/test.4.1.1.php
|
||||
src/TexVC/test.4.1.2.php
|
||||
.DS_Store
|
||||
/nbproject/private/
|
||||
node_modules/
|
||||
|
|
|
@ -165,6 +165,16 @@ class JsonToMathML extends Maintenance {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
// Example file ExamplesNewCommandsMhchem.json
|
||||
foreach ( $fileData as $entry ) {
|
||||
$inputF[] = [
|
||||
"description" => $entry["description"],
|
||||
"tex" => $entry['tex'],
|
||||
"type" => $entry['type'],
|
||||
];
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $inputF;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,208 @@
|
|||
**************************************************************************
|
||||
*
|
||||
* mhchemParser.ts
|
||||
* 4.2.2
|
||||
*
|
||||
* Parser for the \ce command and \pu command for MathJax and Co.
|
||||
*
|
||||
* mhchem's \ce is a tool for writing beautiful chemical equations easily.
|
||||
* mhchem's \pu is a tool for writing physical units easily.
|
||||
*
|
||||
* -----------------------------------------------------------------------
|
||||
*
|
||||
* Copyright 2015-2023 Martin Hensel
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* or in file LICENSE.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* -----------------------------------------------------------------------
|
||||
*
|
||||
* https://github.com/mhchem/mhchemParser
|
||||
*
|
||||
**************************************************************************
|
||||
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
187
src/TexVC/Mhchem/MhchemParser.php
普通文件
187
src/TexVC/Mhchem/MhchemParser.php
普通文件
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2023 Johannes Stegmüller
|
||||
*
|
||||
* This file is a port of mhchemParser originally authored by Martin Hensel in javascript/typescript.
|
||||
* The original license for this software can be found in the accompanying LICENSE.mhchemParser-ts.txt file.
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
namespace MediaWiki\Extension\Math\TexVC\Mhchem;
|
||||
|
||||
use MediaWiki\Logger\LoggerFactory;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Port of mhchemParser v4.2.2 by Martin Hensel (https://github.com/mhchem/mhchemParser)
|
||||
* from typescript/javascript to PHP.
|
||||
*
|
||||
* This class contains the go (¸l.89 in mhchemParser.js)
|
||||
* and the toTex function (l.39 of mhchemParser.js)
|
||||
*
|
||||
* For usage of mhchemParser in PHP instantiate this class and call toTex-Function.
|
||||
*
|
||||
* @author Johannes Stegmüller
|
||||
* @license GPL-2.0-or-later
|
||||
*/
|
||||
class MhchemParser {
|
||||
/** @var MhchemPatterns */
|
||||
private MhchemPatterns $mhchemPatterns;
|
||||
|
||||
/** @var MhchemStateMachines */
|
||||
private MhchemStateMachines $mhchemStateMachines;
|
||||
|
||||
/** @var LoggerInterface */
|
||||
private $logger;
|
||||
/** @var int */
|
||||
private int $debugIndex;
|
||||
|
||||
/**
|
||||
* Instantiate Mhchemparser, required for usage of "toTex" functionality
|
||||
* @param bool $doLogging debug log internal state changes and input output for each state
|
||||
*/
|
||||
public function __construct( bool $doLogging = false ) {
|
||||
$this->mhchemPatterns = new MhchemPatterns();
|
||||
$this->mhchemStateMachines = new MhchemStateMachines( $this );
|
||||
$this->debugIndex = 0;
|
||||
if ( $doLogging ) {
|
||||
$this->logger = LoggerFactory::getInstance( 'Math' );
|
||||
}
|
||||
}
|
||||
|
||||
public function getPatterns(): MhchemPatterns {
|
||||
return $this->mhchemPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $input input formula in tex eventually containing chemical environments or physical units
|
||||
* @param string $type currently ce or pu (physical units)
|
||||
* @param bool $optimizeMhchemForTexVC optimize the output of mhchem for usage in TexVC, usually extra curlies
|
||||
* surrounding parameters which specify dimensions
|
||||
* @return string
|
||||
*/
|
||||
public function toTex( $input, $type, bool $optimizeMhchemForTexVC = false ): string {
|
||||
$parsed = $this->go( $input, $type );
|
||||
$mhchemTexifiy = new MhchemTexify( $optimizeMhchemForTexVC );
|
||||
return $mhchemTexifiy->go( $parsed, $type !== "tex" );
|
||||
}
|
||||
|
||||
public function go( $input, $stateMachine ): array {
|
||||
if ( !MhchemUtil::issetJS( $input ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ( !MhchemUtil::issetJS( $stateMachine ) ) {
|
||||
$stateMachine = 'ce';
|
||||
}
|
||||
|
||||
$state = '0';
|
||||
$buffer = [];
|
||||
|
||||
$buffer['parenthesisLevel'] = 0;
|
||||
|
||||
if ( $input != null ) {
|
||||
$input = preg_replace( "/\n/", "", $input );
|
||||
$input = preg_replace( "/[\x{2212}\x{2013}\x{2014}\x{2010}]/u", "-", $input );
|
||||
$input = preg_replace( "/[\x{2026}]/u", "...", $input );
|
||||
|
||||
}
|
||||
|
||||
// Looks through _mhchemParser.transitions, to execute a matching action
|
||||
// (recursive)actions
|
||||
$lastInput = "";
|
||||
$watchdog = 10;
|
||||
$output = [];
|
||||
while ( true ) {
|
||||
if ( $lastInput !== $input ) {
|
||||
$watchdog = 10;
|
||||
$lastInput = $input;
|
||||
} else {
|
||||
$watchdog--;
|
||||
}
|
||||
|
||||
// Find actions in transition table
|
||||
$machine = $this->mhchemStateMachines->stateMachines[$stateMachine];
|
||||
$t = $machine["transitions"][$state] ?? $machine["transitions"]['*'];
|
||||
|
||||
for ( $i = 0; $i < count( $t ); $i++ ) {
|
||||
$matches = $this->mhchemPatterns->match( $t[$i]["pattern"], $input ?? "" );
|
||||
|
||||
if ( $matches ) {
|
||||
if ( $this->logger ) {
|
||||
$this->logger->debug( "\n Match at: " . $i . "\tPattern: " . $t[$i]["pattern"] .
|
||||
"\t State-machine: " . $stateMachine );
|
||||
}
|
||||
|
||||
// Execute actions
|
||||
$task = $t[$i]["task"];
|
||||
for ( $iA = 0; $iA < count( $task["action_"] ); $iA++ ) {
|
||||
$this->debugIndex++;
|
||||
|
||||
$o = null;
|
||||
|
||||
// Find and execute action
|
||||
if ( array_key_exists( $task["action_"][$iA]["type_"], $machine["actions"] ) ) {
|
||||
$option = $task["action_"][$iA]["option"] ?? null; // tbd, setting null ok ?
|
||||
if ( $this->logger ) {
|
||||
$this->logger->debug( "\n action: \t" . $task["action_"][$iA]["type_"] );
|
||||
}
|
||||
$o = $machine["actions"][$task["action_"][$iA]["type_"]]
|
||||
( $buffer, $matches["match_"], $option );
|
||||
} elseif ( array_key_exists( $task["action_"][$iA]["type_"],
|
||||
$this->mhchemStateMachines->getGenericActions() ) ) {
|
||||
$option = $task["action_"][$iA]["option"] ?? null;
|
||||
if ( $this->logger ) {
|
||||
$this->logger->debug( "\n action: \t" . $task["action_"][$iA]["type_"] );
|
||||
}
|
||||
$o = $this->mhchemStateMachines->getGenericActions()
|
||||
[$task["action_"][$iA]["type_"]]( $buffer, $matches["match_"], $option );
|
||||
} else {
|
||||
// Unexpected character
|
||||
throw new RuntimeException( "MhchemBugA: mhchem bug A. Please report. ("
|
||||
. $task->action_[$iA]->type_ . ")" );
|
||||
}
|
||||
|
||||
// Add output
|
||||
MhchemUtil::concatArray( $output, $o );
|
||||
|
||||
if ( $this->logger ) {
|
||||
$this->logger->debug( "\n State: " . $state );
|
||||
$this->logger->debug( "\n Buffer: " . json_encode( $buffer ) );
|
||||
$this->logger->debug( "\n Input: " . $input );
|
||||
$this->logger->debug( "\n Output: " . json_encode( $output ) );
|
||||
$this->logger->debug( "\n" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Set next state,
|
||||
// Shorten input,
|
||||
// Continue with next character concatArray
|
||||
// (= apply only one transition per position)
|
||||
$state = $task["nextState"] ?? $state;
|
||||
|
||||
if ( $input != null && strlen( $input ) > 0 ) {
|
||||
if ( !array_key_exists( "revisit", $task ) ) {
|
||||
$input = $matches["remainder"];
|
||||
}
|
||||
if ( !array_key_exists( "toContinue", $task ) ) {
|
||||
// this breaks the two for loops
|
||||
break 1;
|
||||
}
|
||||
} else {
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent infinite loop
|
||||
if ( $watchdog <= 0 ) {
|
||||
// Unexpected character
|
||||
throw new RunTimeException( "MhchemBugU: mhchem-PHP bug U. Please report." );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
402
src/TexVC/Mhchem/MhchemPatterns.php
普通文件
402
src/TexVC/Mhchem/MhchemPatterns.php
普通文件
|
@ -0,0 +1,402 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2023 Johannes Stegmüller
|
||||
*
|
||||
* This file is a port of mhchemParser originally authored by Martin Hensel in javascript/typescript.
|
||||
* The original license for this software can be found in the accompanying LICENSE.mhchemParser-ts.txt file.
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
namespace MediaWiki\Extension\Math\TexVC\Mhchem;
|
||||
|
||||
use MediaWiki\Extension\Math\TexVC\Mhchem\MhchemRegExp as Reg;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Contains all matching regex patterns and match functions for mhchemParser in PHP.
|
||||
*
|
||||
* corresponds mostly to the 'patterns' array in line ~207 in mhchemParser.js by Martin Hensel
|
||||
*
|
||||
* @author Johannes Stegmüller
|
||||
* @license GPL-2.0-or-later
|
||||
*/
|
||||
class MhchemPatterns {
|
||||
|
||||
/** @var array */
|
||||
private array $patterns;
|
||||
|
||||
/**
|
||||
* Matching patterns
|
||||
* either regexes or function that return null or {match_:"a", remainder:"bc"}
|
||||
* @return array
|
||||
*/
|
||||
public function getPatterns(): array {
|
||||
return $this->patterns;
|
||||
}
|
||||
|
||||
public function findObserveGroups( $input, $begExcl, $begIncl, $endIncl,
|
||||
$endExcl = null, $beg2Excl = null, $beg2Incl = null,
|
||||
$end2Incl = null, $end2Excl = null, $combine = null ): ?array {
|
||||
$match = $this->matchObsGrInner( $input, $begExcl );
|
||||
if ( $match === null ) {
|
||||
return null;
|
||||
}
|
||||
$input = substr( $input, strlen( $match ) );
|
||||
$match = $this->matchObsGrInner( $input, $begIncl );
|
||||
if ( $match === null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( $endIncl === "0" ) {
|
||||
throw new RuntimeException( "error in condition, check next loc " );
|
||||
}
|
||||
$e = $this->findObserveGroupsInner( $input, strlen( $match ),
|
||||
MhchemUtil::issetJS( $endIncl ) ? $endIncl : $endExcl );
|
||||
if ( $e === null ) {
|
||||
return null;
|
||||
}
|
||||
$match1 = substr( $input, 0, ( $endIncl ? $e["endMatchEnd"] : $e["endMatchBegin"] ) );
|
||||
|
||||
if ( !( MhchemUtil::issetJS( $beg2Excl ) || MhchemUtil::issetJS( $beg2Incl ) ) ) {
|
||||
return [
|
||||
"match_" => $match1,
|
||||
"remainder" => substr( $input, $e["endMatchEnd"] )
|
||||
];
|
||||
} else {
|
||||
$group2 = $this->findObserveGroups( substr( $input, $e["endMatchEnd"] ),
|
||||
$beg2Excl, $beg2Incl, $end2Incl, $end2Excl );
|
||||
if ( $group2 === null ) {
|
||||
return null;
|
||||
}
|
||||
$matchRet = [ $match1, $group2["match_"] ];
|
||||
return [
|
||||
"match_" => ( $combine ? implode( "", $matchRet ) : $matchRet ),
|
||||
"remainder" => $group2["remainder"]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private function matchObsGrInner( string $input, $pattern ) {
|
||||
/**
|
||||
* In javascript this is checking if the incoming pattern is a string,
|
||||
* if not the assumption is that it is of regex type. Since PHP has
|
||||
* strings here.
|
||||
*/
|
||||
if ( !$pattern instanceof Reg ) {
|
||||
// Added this if to catch empty needle for strpos input in PHP
|
||||
if ( !MhchemUtil::issetJS( $pattern ) ) {
|
||||
return $pattern;
|
||||
}
|
||||
if ( strpos( $input, $pattern ) !== 0 ) {
|
||||
return null;
|
||||
}
|
||||
return $pattern;
|
||||
} else {
|
||||
$matches = [];
|
||||
$match = preg_match( $pattern->getRegExp(), $input, $matches );
|
||||
if ( !$match ) {
|
||||
return null;
|
||||
}
|
||||
return $matches[0];
|
||||
}
|
||||
}
|
||||
|
||||
private function findObserveGroupsInner( string $input, $i, $endChars ): ?array {
|
||||
$braces = 0;
|
||||
while ( $i < strlen( $input ) ) {
|
||||
$a = $input[$i];
|
||||
$match = $this->matchObsGrInner( substr( $input, $i ), $endChars );
|
||||
if ( $match !== null && $braces === 0 ) {
|
||||
return [ "endMatchBegin" => $i, "endMatchEnd" => $i + strlen( $match ) ];
|
||||
} elseif ( $a === "{" ) {
|
||||
$braces++;
|
||||
} elseif ( $a === "}" ) {
|
||||
if ( $braces === 0 ) {
|
||||
// Unexpected character
|
||||
throw new RuntimeException(
|
||||
"ExtraCloseMissingOpen: Extra close brace or missing open brace" );
|
||||
} else {
|
||||
$braces--;
|
||||
}
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
$this->patterns = [
|
||||
'empty' => new Reg( "/^$/" ),
|
||||
'else' => new Reg( "/^./" ),
|
||||
'else2' => new Reg( "/^./" ),
|
||||
'space' => new Reg( "/^\s/" ),
|
||||
'space A' => new Reg( "/^\s(?=[A-Z\\\\$])/" ),
|
||||
'space$' => new Reg( "/^\s$/" ),
|
||||
'a-z' => new Reg( "/^[a-z]/" ),
|
||||
'x' => new Reg( "/^x/" ),
|
||||
'x$' => new Reg( "/^x$/" ),
|
||||
'i$' => new Reg( "/^i$/" ),
|
||||
'letters' => new Reg(
|
||||
"/^(?:[a-zA-Z\x{03B1}-\x{03C9}\x{0391}-\x{03A9}?@]|(?:\\\\(?:alpha|beta|gamma|delta|epsilon"
|
||||
. "|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|Gamma"
|
||||
. "|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)(?:\s+|\{\}|(?![a-zA-Z]))))+/u" ),
|
||||
'\\greek' => new Reg(
|
||||
"/^\\\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi"
|
||||
. "|rho|sigma|tau|upsilon|phi|chi|psi|omega|Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)"
|
||||
. "(?:\s+|\{\}|(?![a-zA-Z]))/" ),
|
||||
'one lowercase latin letter $' => new Reg( "/^(?:([a-z])(?:$|[^a-zA-Z]))$/" ),
|
||||
'$one lowercase latin letter$ $' => new Reg( "/^\\\$(?:([a-z])(?:$|[^a-zA-Z]))\\\$$/" ),
|
||||
'one lowercase greek letter $' => new Reg(
|
||||
"/^(?:\\\$?[\x{003B1}-\x{0003C9}]\\\$?|\\\$?\\\\(?:alpha|beta|gamma|" .
|
||||
"delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|" .
|
||||
"phi|chi|psi|omega)\s*\\\$?)(?:\s+|\{\}|(?![a-zA-Z]))$/u" ),
|
||||
'digits' => new Reg( "/^[0-9]+/" ),
|
||||
'-9.,9' => new Reg( "/^[+\-]?(?:[0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))/" ),
|
||||
'-9.,9 no missing 0' => new Reg( "/^[+\-]?[0-9]+(?:[.,][0-9]+)?/" ),
|
||||
'(-)(9.,9)(e)(99)' => static function ( $input ) {
|
||||
$matches = [];
|
||||
$match = preg_match( "/^(\+\-|\+\/\-|\+|\-|\\\\pm\s?)?([0-9]+(?:[,.][0-9]+)?|" .
|
||||
"[0-9]*(?:\.[0-9]+))?(\((?:[0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))\))?(?:(?:([eE])" .
|
||||
"|\s*(\*|x|\\\\times|\x{00D7})\s*10\^)([+\-]?[0-9]+|\{[+\-]?[0-9]+\}))?/u", $input, $matches );
|
||||
if ( $match && $matches[0] ) {
|
||||
// could also match ""
|
||||
return [ "match_" => array_slice( $matches, 1 ),
|
||||
"remainder" => substr( $input, strlen( $matches[0] ) ) ];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
'(-)(9)^(-9)' => new Reg( "/^(\+\-|\+\/\-|\+|\-|\\\\pm\s?)?([0-9]+(?:[,.][0-9]+)?|"
|
||||
. "[0-9]*(?:\.[0-9]+)?)\^([+\-]?[0-9]+|\{[+\-]?[0-9]+\})/" ),
|
||||
'state of aggregation $' => function ( $input ) {
|
||||
// ... or crystal system
|
||||
$a = $this->findObserveGroups( $input, "",
|
||||
new Reg( "/^\([a-z]{1,3}(?=[\),])/" ), ")", "" );
|
||||
if ( $a && preg_match( "/^($|[\s,;\)\]\}])/", $a["remainder"] ) ) {
|
||||
return $a;
|
||||
}
|
||||
$matches = [];
|
||||
$match = preg_match( "/^(?:\((?:\\\\ca\s?)?\\\$[amothc]\\\$\))/", $input, $matches );
|
||||
if ( $match ) {
|
||||
return [ "match_" => $matches[0], "remainder" => substr( $input, strlen( $matches[0] ) ) ];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
'_{(state of aggregation)}$' => new Reg( "/^_\{(\([a-z]{1,3}\))\}/" ),
|
||||
'{[(' => new Reg( "/^(?:\\\{|\[|\()/" ),
|
||||
')]}' => new Reg( "/^(?:\)|\]|\\\})/" ),
|
||||
', ' => new Reg( "/^[,;]\s*/" ),
|
||||
',' => new Reg( "/^[,;]/" ),
|
||||
'.' => new Reg( "/^[.]/" ),
|
||||
'. __* ' => new Reg( "/^([.\x{22C5}\x{00B7}\x{2022}]|[*])\s*/u" ),
|
||||
'...' => new Reg( "/^\.\.\.(?=$|[^.])/" ),
|
||||
'^{(...)}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "^{", "", "", "}" );
|
||||
},
|
||||
'^($...$)' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "^", "$", "$", "" );
|
||||
},
|
||||
'^a' => new Reg( "/^\^([0-9]+|[^\\\_])/u" ),
|
||||
'^\\x{}{}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "^",
|
||||
new Reg( "/^\\\\[a-zA-Z]+\{/" ), "}", "", "",
|
||||
"{", "}", "", true );
|
||||
},
|
||||
'^\\x{}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "^",
|
||||
new Reg( "/^\\\\[a-zA-Z]+\{/" ), "}", "" );
|
||||
},
|
||||
'^\\x' => new Reg( "/^\^(\\\\[a-zA-Z]+)\s*/" ),
|
||||
'^(-1)' => new Reg( "/^\^(-?\d+)/" ),
|
||||
'\'' => new Reg( "/^'/" ),
|
||||
'_{(...)}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "_{", "", "", "}" );
|
||||
},
|
||||
'_($...$)' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "_", "$", "$", "" );
|
||||
},
|
||||
'_9' => new Reg( "/^_([+\-]?[0-9]+|[^\\\\])/" ),
|
||||
'_\\x{}{}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "_", new Reg( "/^\\\\[a-zA-Z]+\{/" ), "}",
|
||||
"", "", "{", "}", "", true );
|
||||
},
|
||||
'_\\x{}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "_",
|
||||
new Reg( "/^\\\\[a-zA-Z]+\{/" ), "}", "" );
|
||||
},
|
||||
'_\\x' => new Reg( "/^_(\\\\[a-zA-Z]+)\s*/" ),
|
||||
'^_' => new Reg( "/^(?:\^(?=_)|\_(?=\^)|[\^_]$)/" ),
|
||||
'{}^' => new Reg( "/^\{\}(?=\^)/" ),
|
||||
'{}' => new Reg( "/^\{\}/" ),
|
||||
'{...}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "", "{", "}", "" );
|
||||
},
|
||||
'{(...)}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "{", "", "", "}" );
|
||||
},
|
||||
'$...$' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "", "\$", "\$", "" );
|
||||
},
|
||||
'${(...)}$__$(...)$' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "\${", "", "", "}\$" )
|
||||
?? $this->findObserveGroups( $input, "\$", "", "", "\$" );
|
||||
},
|
||||
'=<>' => new Reg( "/^[=<>]/" ),
|
||||
'#' => new Reg( "/^[#\x{2261}]/u" ),
|
||||
'+' => new Reg( "/^\+/" ),
|
||||
// -space -, -; -] -/ -$ -state-of-aggregation orig: "/^-(?=[\s_},;\]/]|$|\([a-z]+\))/"
|
||||
'-$' => new Reg( "/^-(?=[\s_},;\]\/]|$|\([a-z]+\))/u" ),
|
||||
'-9' => new Reg( "/^-(?=[0-9])/" ),
|
||||
'- orbital overlap' => new Reg( "/^-(?=(?:[spd]|sp)(?:$|[\s,;\)\]\}]))/" ),
|
||||
'-' => new Reg( "/^-/" ),
|
||||
'pm-operator' => new Reg( "/^(?:\\\\pm|\\\$\\\\pm\\\$|\+-|\+\/-)/" ),
|
||||
'operator' => new Reg( "/^(?:\+|(?:[\-=<>]|<<|>>|\\\\approx|\\\$\\\\approx\\\$)(?=\s|$|-?[0-9]))/" ),
|
||||
'arrowUpDown' => new Reg( "/^(?:v|\(v\)|\^|\(\^\))(?=$|[\s,;\)\]\}])/" ),
|
||||
'\\bond{(...)}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "\\bond{", "", "", "}" );
|
||||
},
|
||||
'->' => new Reg( '/^(?:<->|<-->|->|<-|<=>>|<<=>|<=>|[\x{2192}\x{27F6}\x{21CC}])/u' ),
|
||||
'CMT' => new Reg( "/^[CMT](?=\[)/" ),
|
||||
'[(...)]' => function ( $input ) { return $this->findObserveGroups( $input, "[", "",
|
||||
"", "]" );
|
||||
},
|
||||
'1st-level escape' => new Reg( "/^(&|\\\\\\\\|\\\\hline)\s*/" ),
|
||||
// \\x - but output no space before
|
||||
'\\,' => new Reg( "/^(?:\\\\[,\ ;:])/" ),
|
||||
'\\x{}{}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "", new Reg( "/^\\\\[a-zA-Z]+\{/" ), "}",
|
||||
"", "", "{", "}", "", true );
|
||||
},
|
||||
'\\x{}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "", new Reg( "/^\\\\[a-zA-Z]+\{/" ), "}",
|
||||
"" );
|
||||
},
|
||||
'\\ca' => new Reg( "/^\\\\ca(?:\s+|(?![a-zA-Z]))/" ),
|
||||
'\\x' => new Reg( "/^(?:\\\\[a-zA-Z]+\s*|\\\\[_&{}%])/" ),
|
||||
// only those with numbers in front, because the others will be formatted correctly anyway
|
||||
'orbital' => new Reg( "/^(?:[0-9]{1,2}[spdfgh]|[0-9]{0,2}sp)(?=$|[^a-zA-Z])/" ),
|
||||
'others' => new Reg( "/^[\/~|]/" ),
|
||||
'\\frac{(...)}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "\\frac{", "",
|
||||
"", "}", "{", "", "", "}" );
|
||||
},
|
||||
'\\overset{(...)}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "\\overset{", "",
|
||||
"", "}", "{", "", "", "}" );
|
||||
},
|
||||
'\\underset{(...)}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "\\underset{", "",
|
||||
"", "}", "{", "", "", "}" );
|
||||
},
|
||||
'\\underbrace{(...)}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "\\underbrace{", "",
|
||||
"", "}_", "{", "", "", "}" );
|
||||
},
|
||||
'\\color{(...)}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "\\color{", "", "", "}" );
|
||||
},
|
||||
'\\color{(...)}{(...)}' => function ( $input ) {
|
||||
// ?? instead of ||
|
||||
return $this->findObserveGroups( $input, "\\color{", "",
|
||||
"", "}", "{", "", "", "}" ) ??
|
||||
$this->findObserveGroups( $input, "\\color", "\\", "",
|
||||
new Reg( "/^(?=\{)/" ), "{", "", "", "}" );
|
||||
},
|
||||
'\\ce{(...)}' => function ( $input ) {
|
||||
return $this->findObserveGroups( $input, "\\ce{", "", "", "}" );
|
||||
},
|
||||
'\\pu{(...)}' => function ( $input ) { return $this->findObserveGroups( $input,
|
||||
"\\pu{", "", "", "}" );
|
||||
},
|
||||
'oxidation$' => new Reg( "/^(?:[+-][IVX]+|(?:\\\\pm|\\\$\\\\pm\\\$|\+-|\+\/-)\s*0)$/" ),
|
||||
'd-oxidation$' => new Reg( "/^(?:[+-]?[IVX]+|(?:\\\\pm|\\\$\\\\pm\\\$|\+-|\+\/-)\s*0)$/" ),
|
||||
'1/2$' => new Reg( "/^[+\-]?(?:[0-9]+|\\\$[a-z]\\\$|[a-z])\/[0-9]+(?:\\\$[a-z]\\\$|[a-z])?$/" ),
|
||||
'amount' => function ( $input ) {
|
||||
$matches = [];
|
||||
// e.g. 2, 0.5, 1/2, -2, n/2, +; $a$ could be added later in parsing
|
||||
$match = preg_match( "/^(?:(?:(?:\([+\-]?[0-9]+\/[0-9]+\)|[+\-]?(?:[0-9]+|\\\$[a-z]\\\$" .
|
||||
"|[a-z])\/[0-9]+|[+\-]?[0-9]+[.,][0-9]+|[+\-]?\.[0-9]+|[+\-]?[0-9]+)(?:[a-z](?=\s*[A-Z]))?)" .
|
||||
"|[+\-]?[a-z](?=\s*[A-Z])|\+(?!\s))/", $input, $matches );
|
||||
|
||||
if ( $match ) {
|
||||
return [ "match_" => $matches[0], "remainder" => substr( $input, strlen( $matches[0] ) ) ];
|
||||
}
|
||||
$a = $this->findObserveGroups( $input, "", "$", "$", "" );
|
||||
// e.g. $2n-1$, $-$
|
||||
if ( MhchemUtil::issetJS( $a ) ) {
|
||||
$matchesI = [];
|
||||
|
||||
$match = preg_match( "/^\\\$(?:\(?[+\-]?(?:[0-9]*[a-z]?[+\-])" .
|
||||
"?[0-9]*[a-z](?:[+\-][0-9]*[a-z]?)?\)?|\+|-)\\\$$/", $a["match_"] ?? "",
|
||||
$matchesI );
|
||||
if ( $match ) {
|
||||
return [ "match_" => $matchesI[0], "remainder" => substr( $input, strlen( $matchesI[0] ) ) ];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
'amount2' => function ( $input ) {
|
||||
/* @phan-suppress-next-line PhanInfiniteRecursion, PhanUndeclaredInvokeInCallable */
|
||||
return $this->patterns['amount']( $input );
|
||||
},
|
||||
'(KV letters),' => new Reg( "/^(?:[A-Z][a-z]{0,2}|i)(?=,)/" ),
|
||||
'formula$' => static function ( $input ) {
|
||||
if ( preg_match( "/^\([a-z]+\)$/", $input ) ) {
|
||||
// state of aggregation = no formula
|
||||
return null;
|
||||
}
|
||||
$matches = [];
|
||||
$match = preg_match( "/^(?:[a-z]|(?:[0-9\ \+\-\,\.\(\)]+[a-z])+[0-9\ \+\-\,\.\(\)]*|"
|
||||
. "(?:[a-z][0-9\ \+\-\,\.\(\)]+)+[a-z]?)$/", $input, $matches );
|
||||
if ( $match ) {
|
||||
return [ "match_" => $matches[0], "remainder" => substr( $input, strlen( $matches[0] ) ) ];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
'uprightEntities' => new Reg( "/^(?:pH|pOH|pC|pK|iPr|iBu)(?=$|[^a-zA-Z])/" ),
|
||||
'/' => new Reg( "/^\s*(\/)\s*/" ),
|
||||
'//' => new Reg( "/^\s*(\/\/)\s*/" ),
|
||||
'*' => new Reg( "/^\s*[*.]\s*/" )
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Matching function
|
||||
* e.g. match("a", input) will look for the regexp called "a" and see if it matches
|
||||
* returns null or {match_:"a", remainder:"bc"}
|
||||
* @param string $m key for fetching a pattern
|
||||
* @param string $input string to check
|
||||
* @return array|mixed|null information about the match
|
||||
*/
|
||||
public function match( string $m, string $input ) {
|
||||
$pattern = $this->patterns[$m] ?? null;
|
||||
if ( !$pattern ) {
|
||||
// Trying to use non-existing pattern
|
||||
throw new RuntimeException( "MhchemBugP: mhchem bug P. Please report. (" . $m . ")" );
|
||||
} elseif ( $pattern instanceof Reg ) {
|
||||
$matches = [];
|
||||
$match = preg_match( $pattern->getRegExp(), $input, $matches );
|
||||
if ( $match ) {
|
||||
if ( count( $matches ) > 2 ) {
|
||||
return [
|
||||
"match_" => array_slice( $matches, 1 ),
|
||||
"remainder" => substr( $input, strlen( $matches[0] ) )
|
||||
];
|
||||
|
||||
} else {
|
||||
return [
|
||||
"match_" => MhchemUtil::issetJS( $matches[1] ?? null ) ? $matches[1] : $matches[0],
|
||||
"remainder" => substr( $input, strlen( $matches[0] ) )
|
||||
];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} elseif ( is_callable( $pattern ) ) {
|
||||
// $pattern cannot be an instance of MhchemRegExp here, which causes this warning.
|
||||
/* @phan-suppress-next-line PhanUndeclaredInvokeInCallable */
|
||||
return $this->patterns[$m]( $input );
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2023 Johannes Stegmüller
|
||||
*
|
||||
* This file is a port of mhchemParser originally authored by Martin Hensel in javascript/typescript.
|
||||
* The original license for this software can be found in the accompanying LICENSE.mhchemParser-ts.txt file.
|
||||
*/
|
||||
|
||||
namespace MediaWiki\Extension\Math\TexVC\Mhchem;
|
||||
|
||||
/**
|
||||
* Wrapper class to declare a hardcoded string to a regular expression.
|
||||
* @author Johannes Stegmüller
|
||||
* @license GPL-2.0-or-later
|
||||
*/
|
||||
class MhchemRegExp {
|
||||
|
||||
/** @var string regular expression pattern as a string */
|
||||
private string $regexp;
|
||||
|
||||
/**
|
||||
* Utility class to distinguish Regular expression strings defined in
|
||||
* the codebase of mhchemParser from regular strings.
|
||||
* @param string $pattern regular expression pattern, usually of the format "/regexp/"
|
||||
*/
|
||||
public function __construct( string $pattern ) {
|
||||
$this->regexp = $pattern;
|
||||
}
|
||||
|
||||
public function getRegExp(): string {
|
||||
return $this->regexp;
|
||||
}
|
||||
}
|
1250
src/TexVC/Mhchem/MhchemStateMachines.php
普通文件
1250
src/TexVC/Mhchem/MhchemStateMachines.php
普通文件
文件差异内容过多而无法显示
加载差异
408
src/TexVC/Mhchem/MhchemTexify.php
普通文件
408
src/TexVC/Mhchem/MhchemTexify.php
普通文件
|
@ -0,0 +1,408 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2023 Johannes Stegmüller
|
||||
*
|
||||
* This file is a port of mhchemParser originally authored by Martin Hensel in javascript/typescript.
|
||||
* The original license for this software can be found in the accompanying LICENSE.mhchemParser-ts.txt file.
|
||||
*/
|
||||
|
||||
namespace MediaWiki\Extension\Math\TexVC\Mhchem;
|
||||
|
||||
use MediaWiki\Extension\Math\TexVC\MHChem\MhchemUtil as MU;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Takes MhchemParser output and convert it to TeX
|
||||
*
|
||||
* Functionality is the same as mhchemTexify class at ~line 1505 in mhchemParser.js
|
||||
* in mhchemParser by Martin Hensel.
|
||||
*
|
||||
* @author Johannes Stegmüller
|
||||
* @license GPL-2.0-or-later
|
||||
*/
|
||||
class MhchemTexify {
|
||||
|
||||
/** @var bool optimize the output TeX for TexVC */
|
||||
private bool $optimizeForTexVC;
|
||||
|
||||
/**
|
||||
* Takes MhchemParser output and convert it to TeX
|
||||
* @param bool $optimizeForTexVC optimizes the output for TexVC grammar by
|
||||
* wrapping dimensions for some TeX commands in curly brackets.
|
||||
*/
|
||||
public function __construct( bool $optimizeForTexVC = false ) {
|
||||
$this->optimizeForTexVC = $optimizeForTexVC;
|
||||
}
|
||||
|
||||
public function go( $input, $addOuterBraces ): string {
|
||||
if ( !MhchemUtil::issetJS( $input ) ) {
|
||||
return "";
|
||||
}
|
||||
$res = "";
|
||||
$cee = false;
|
||||
for ( $i = 0; $i < count( $input ); $i++ ) {
|
||||
$inputI = $input[$i];
|
||||
|
||||
if ( is_string( $inputI ) ) {
|
||||
$res .= $inputI;
|
||||
} else {
|
||||
$res .= self::go2( $inputI );
|
||||
if ( $inputI["type_"] === '1st-level escape' ) {
|
||||
$cee = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( $addOuterBraces && !$cee && $res ) {
|
||||
$res = "{" . $res . "}";
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function goInner( $input ): string {
|
||||
return self::go( $input, false );
|
||||
}
|
||||
|
||||
private function strReplaceFirst( $search, $replace, $subject ): string {
|
||||
return implode( $replace, explode( $search, $subject, 2 ) );
|
||||
}
|
||||
|
||||
private function go2( $buf ): string {
|
||||
switch ( $buf["type_"] ) {
|
||||
case 'chemfive':
|
||||
$res = "";
|
||||
$b5 = [
|
||||
"a" => self::goInner( $buf["a"] ),
|
||||
"b" => self::goInner( $buf["b"] ),
|
||||
"p" => self::goInner( $buf["p"] ),
|
||||
"o" => self::goInner( $buf["o"] ),
|
||||
"q" => self::goInner( $buf["q"] ),
|
||||
"d" => self::goInner( $buf["d"] )
|
||||
];
|
||||
if ( MU::issetJS( $b5["a"] ) ) {
|
||||
if ( preg_match( "/^[+\-]/", $b5["a"] ) ) {
|
||||
$b5["a"] = "{" . $b5["a"] . "}";
|
||||
}
|
||||
$res .= $b5["a"] . "\\,";
|
||||
}
|
||||
if ( MU::issetJS( $b5["b"] ) || MU::issetJS( $b5["p"] ) ) {
|
||||
$res .= "{\\vphantom{A}}";
|
||||
$res .= "^{\\hphantom{" . ( $b5["b"] ) . "}}_{\\hphantom{" . ( $b5["p"] ) . "}}";
|
||||
$res .= !$this->optimizeForTexVC ? "\\mkern-1.5mu" : "\\mkern{-1.5mu}";
|
||||
$res .= "{\\vphantom{A}}";
|
||||
$res .= "^{\\smash[t]{\\vphantom{2}}\\llap{" . ( $b5["b"] ) . "}}";
|
||||
$res .= "_{\\vphantom{2}\\llap{\\smash[t]{" . ( $b5["p"] ) . "}}}";
|
||||
}
|
||||
|
||||
if ( MU::issetJS( $b5["o"] ) ) {
|
||||
if ( preg_match( "/^[+\-]/", $b5["o"] ) ) {
|
||||
$b5["o"] = "{" . $b5["o"] . "}";
|
||||
}
|
||||
$res .= $b5["o"];
|
||||
}
|
||||
if ( isset( $buf["dType"] ) && $buf["dType"] === 'kv' ) {
|
||||
if ( MU::issetJS( $b5["d"] ) || MU::issetJS( $b5["q"] ) ) {
|
||||
$res .= "{\\vphantom{A}}";
|
||||
}
|
||||
if ( MU::issetJS( $b5["d"] ) ) {
|
||||
$res .= "^{" . $b5["d"] . "}";
|
||||
}
|
||||
if ( MU::issetJS( $b5["q"] ) ) {
|
||||
$res .= "_{\\smash[t]{" . $b5["q"] . "}}";
|
||||
}
|
||||
} elseif ( MU::issetJS( $buf["dType"] ?? null ) && $buf["dType"] === 'oxidation' ) {
|
||||
if ( MU::issetJS( $b5["d"] ) ) {
|
||||
$res .= "{\\vphantom{A}}";
|
||||
$res .= "^{" . $b5["d"] . "}";
|
||||
}
|
||||
if ( MU::issetJS( $b5["q"] ) ) {
|
||||
$res .= "{\\vphantom{A}}";
|
||||
$res .= "_{\\smash[t]{" . $b5["q"] . "}}";
|
||||
}
|
||||
} else {
|
||||
if ( MU::issetJS( $b5["q"] ) ) {
|
||||
$res .= "{\\vphantom{A}}";
|
||||
$res .= "_{\\smash[t]{" . $b5["q"] . "}}";
|
||||
}
|
||||
if ( MU::issetJS( $b5["d"] ) ) {
|
||||
$res .= "{\\vphantom{A}}";
|
||||
$res .= "^{" . $b5["d"] . "}";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'roman numeral':
|
||||
case 'rm':
|
||||
$res = "\\mathrm{" . $buf["p1"] . "}";
|
||||
break;
|
||||
case 'text':
|
||||
if ( preg_match( "/[\^_]/", $buf["p1"] ) ) {
|
||||
$buf["p1"] = self::strReplaceFirst( "-", "\\text{-}",
|
||||
self::strReplaceFirst( " ", "~", $buf["p1"] ) );
|
||||
$res = "\\mathrm{" . $buf["p1"] . "}";
|
||||
} else {
|
||||
$res = "\\text{" . $buf["p1"] . "}";
|
||||
}
|
||||
break;
|
||||
case 'state of aggregation':
|
||||
$res = ( !$this->optimizeForTexVC ? "\\mskip2mu " : "\\mskip{2mu} " ) . self::goInner( $buf["p1"] );
|
||||
break;
|
||||
case 'state of aggregation subscript':
|
||||
$res = ( !$this->optimizeForTexVC ? "\\mskip1mu " : "\\mskip{1mu} " ) . self::goInner( $buf["p1"] );
|
||||
break;
|
||||
case 'bond':
|
||||
$res = self::getBond( $buf["kind_"] );
|
||||
if ( !$res ) {
|
||||
throw new RuntimeException( "MhchemErrorBond: mhchem Error. Unknown bond type ("
|
||||
. $buf["kind_"] . ")" );
|
||||
}
|
||||
break;
|
||||
case 'frac':
|
||||
$c = "\\frac{" . $buf["p1"] . "}{" . $buf["p2"] . "}";
|
||||
$res = "\\mathchoice{\\textstyle" . $c . "}{" . $c . "}{" . $c . "}{" . $c . "}";
|
||||
break;
|
||||
case 'pu-frac':
|
||||
$d = "\\frac{" . self::goInner( $buf["p1"] ) . "}{" . self::goInner( $buf["p2"] ) . "}";
|
||||
$res = "\\mathchoice{\\textstyle" . $d . "}{" . $d . "}{" . $d . "}{" . $d . "}";
|
||||
break;
|
||||
case '1st-level escape':
|
||||
case 'tex-math':
|
||||
$res = $buf["p1"] . " ";
|
||||
break;
|
||||
case 'frac-ce':
|
||||
$res = "\\frac{" . self::goInner( $buf["p1"] ) . "}{" . self::goInner( $buf["p2"] ) . "}";
|
||||
break;
|
||||
case 'overset':
|
||||
$res = "\\overset{" . self::goInner( $buf["p1"] ) . "}{" . self::goInner( $buf["p2"] ) . "}";
|
||||
break;
|
||||
case 'underset':
|
||||
$res = "\\underset{" . self::goInner( $buf["p1"] ) . "}{" . self::goInner( $buf["p2"] ) . "}";
|
||||
break;
|
||||
case 'underbrace':
|
||||
$res = "\\underbrace{" . self::goInner( $buf["p1"] ) . "}_{" . self::goInner( $buf["p2"] ) . "}";
|
||||
break;
|
||||
case 'color':
|
||||
$res = "{\\color{" . $buf["color1"] . "}{" . self::goInner( $buf["color2"] ) . "}}";
|
||||
break;
|
||||
case 'color0':
|
||||
$res = "\\color{" . $buf["color"] . "}";
|
||||
break;
|
||||
case 'arrow':
|
||||
$b6 = [
|
||||
"rd" => self::goInner( $buf["rd"] ),
|
||||
"rq" => self::goInner( $buf["rq"] )
|
||||
];
|
||||
$arrow = self::getArrow( $buf["r"] );
|
||||
if ( MU::issetJS( $b6["rd"] ) || MU::issetJS( $b6["rq"] ) ) {
|
||||
if ( $buf["r"] === "<=>" || $buf["r"] === "<=>>" || $buf["r"] === "<<=>" || $buf["r"] === "<-->" ) {
|
||||
$arrow = "\\long" . $arrow;
|
||||
if ( MU::issetJS( $b6["rd"] ) ) {
|
||||
$arrow = "\\overset{" . $b6["rd"] . "}{" . $arrow . "}";
|
||||
}
|
||||
if ( MU::issetJS( $b6["rq"] ) ) {
|
||||
if ( $buf["r"] === "<-->" ) {
|
||||
$arrow = !$this->optimizeForTexVC ?
|
||||
"\\underset{\\lower2mu{" . $b6["rq"] . "}}{" . $arrow . "}"
|
||||
: "\\underset{\\lower{2mu}{" . $b6["rq"] . "}}{" . $arrow . "}";
|
||||
} else {
|
||||
$arrow = !$this->optimizeForTexVC ?
|
||||
"\\underset{\\lower6mu{" . $b6["rq"] . "}}{" . $arrow . "}"
|
||||
: "\\underset{\\lower{6mu}{" . $b6["rq"] . "}}{" . $arrow . "}";
|
||||
}
|
||||
}
|
||||
$arrow = " {}\\mathrel{" . $arrow . "}{} ";
|
||||
} else {
|
||||
if ( MU::issetJS( $b6["rq"] ) ) {
|
||||
$arrow .= "[{" . $b6["rq"] . "}]";
|
||||
}
|
||||
$arrow .= "{" . $b6["rd"] . "}";
|
||||
$arrow = " {}\\mathrel{\\x" . $arrow . "}{} ";
|
||||
}
|
||||
} else {
|
||||
$arrow = " {}\\mathrel{\\long" . $arrow . "}{} ";
|
||||
}
|
||||
$res = $arrow;
|
||||
break;
|
||||
case 'operator':
|
||||
$res = self::getOperator( $buf["kind_"] );
|
||||
break;
|
||||
default:
|
||||
$res = null;
|
||||
}
|
||||
if ( isset( $res ) ) {
|
||||
return $res;
|
||||
}
|
||||
|
||||
switch ( $buf["type_"] ) {
|
||||
case 'space':
|
||||
$res = " ";
|
||||
break;
|
||||
case 'tinySkip':
|
||||
$res = !$this->optimizeForTexVC ? '\\mkern2mu' : '\\mkern{2mu}';
|
||||
break;
|
||||
case 'pu-space-1':
|
||||
case 'entitySkip':
|
||||
$res = "~";
|
||||
break;
|
||||
case 'pu-space-2':
|
||||
$res = !$this->optimizeForTexVC ? "\\mkern3mu " : "\\mkern{3mu} ";
|
||||
break;
|
||||
case '1000 separator':
|
||||
$res = !$this->optimizeForTexVC ? "\\mkern2mu " : "\\mkern{2mu} ";
|
||||
break;
|
||||
case 'commaDecimal':
|
||||
$res = "{,}";
|
||||
break;
|
||||
case 'comma enumeration L':
|
||||
$res = "{" . $buf["p1"] . "}" . ( !$this->optimizeForTexVC ? "\\mkern6mu " : "\\mkern{6mu} " );
|
||||
break;
|
||||
case 'comma enumeration M':
|
||||
$res = "{" . $buf["p1"] . "}" . ( !$this->optimizeForTexVC ? "\\mkern3mu " : "\\mkern{3mu} " );
|
||||
break;
|
||||
case 'comma enumeration S':
|
||||
$res = "{" . $buf["p1"] . "}" . ( !$this->optimizeForTexVC ? "\\mkern1mu " : "\\mkern{1mu} " );
|
||||
break;
|
||||
case 'hyphen':
|
||||
$res = "\\text{-}";
|
||||
break;
|
||||
case 'addition compound':
|
||||
$res = "\\,{\\cdot}\\,";
|
||||
break;
|
||||
case 'electron dot':
|
||||
$res = !$this->optimizeForTexVC ?
|
||||
"\\mkern1mu \\bullet\\mkern1mu " : "\\mkern{1mu} \\bullet\\mkern{1mu} ";
|
||||
break;
|
||||
case 'KV x':
|
||||
$res = "{\\times}";
|
||||
break;
|
||||
case 'prime':
|
||||
$res = "\\prime ";
|
||||
break;
|
||||
case 'cdot':
|
||||
$res = "\\cdot ";
|
||||
break;
|
||||
case 'tight cdot':
|
||||
$res = !$this->optimizeForTexVC ? "\\mkern1mu{\\cdot}\\mkern1mu " : "\\mkern{1mu}{\\cdot}\\mkern{1mu} ";
|
||||
break;
|
||||
case 'times':
|
||||
$res = "\\times ";
|
||||
break;
|
||||
case 'circa':
|
||||
$res = "{\\sim}";
|
||||
break;
|
||||
case '^':
|
||||
$res = "uparrow";
|
||||
break;
|
||||
case 'v':
|
||||
$res = "downarrow";
|
||||
break;
|
||||
case 'ellipsis':
|
||||
$res = "\\ldots ";
|
||||
break;
|
||||
case '/':
|
||||
$res = "/";
|
||||
break;
|
||||
case ' / ':
|
||||
$res = "\\,/\\,";
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException( "MhchemBugT: mhchem bug T. Please report." );
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function getArrow( $a ): string {
|
||||
switch ( $a ) {
|
||||
case "\u2192":
|
||||
case "\u27F6":
|
||||
case "->":
|
||||
return "rightarrow";
|
||||
case "<-":
|
||||
return "leftarrow";
|
||||
case "<->":
|
||||
return "leftrightarrow";
|
||||
case "<-->":
|
||||
return "leftrightarrows";
|
||||
case "\u21CC":
|
||||
case "<=>":
|
||||
return "rightleftharpoons";
|
||||
case "<=>>":
|
||||
return "Rightleftharpoons";
|
||||
case "<<=>":
|
||||
return "Leftrightharpoons";
|
||||
default:
|
||||
throw new RuntimeException( "MhchemBugT: mhchem bug T. Please report." );
|
||||
}
|
||||
}
|
||||
|
||||
private function getBond( $a ): string {
|
||||
switch ( $a ) {
|
||||
case "1":
|
||||
case "-":
|
||||
return "{-}";
|
||||
case "2":
|
||||
case "=":
|
||||
return "{=}";
|
||||
case "3":
|
||||
case "#":
|
||||
return "{\\equiv}";
|
||||
case "~":
|
||||
return "{\\tripledash}";
|
||||
case "~-":
|
||||
return !$this->optimizeForTexVC ? "{\\rlap{\\lower.1em{-}}\\raise.1em{\\tripledash}}"
|
||||
: "{\\rlap{\\lower{.1em}{-}}\\raise{.1em}{\\tripledash}}";
|
||||
case "~--":
|
||||
case "~=":
|
||||
return !$this->optimizeForTexVC ? "{\\rlap{\\lower.2em{-}}\\rlap{\\raise.2em{\\tripledash}}-}"
|
||||
: "{\\rlap{\\lower{.2em}{-}}\\rlap{\\raise{.2em}{\\tripledash}}-}";
|
||||
case "-~-":
|
||||
return !$this->optimizeForTexVC ? "{\\rlap{\\lower.2em{-}}\\rlap{\\raise.2em{-}}\\tripledash}"
|
||||
: "{\\rlap{\\lower{.2em}{-}}\\rlap{\\raise{.2em}{-}}\\tripledash}";
|
||||
case "...":
|
||||
return "{{\\cdot}{\\cdot}{\\cdot}}";
|
||||
case "....":
|
||||
return "{{\\cdot}{\\cdot}{\\cdot}{\\cdot}}";
|
||||
case "->":
|
||||
return "{\\rightarrow}";
|
||||
case "<-":
|
||||
return "{\\leftarrow}";
|
||||
case "<":
|
||||
return "{<}";
|
||||
case ">":
|
||||
return "{>}";
|
||||
default:
|
||||
throw new RuntimeException( "MhchemBugT: mhchem bug T. Please report." );
|
||||
}
|
||||
}
|
||||
|
||||
private function getOperator( $a ): string {
|
||||
switch ( $a ) {
|
||||
case "+":
|
||||
return " {}+{} ";
|
||||
case "-":
|
||||
return " {}-{} ";
|
||||
case "=":
|
||||
return " {}={} ";
|
||||
case "<":
|
||||
return " {}<{} ";
|
||||
case ">":
|
||||
return " {}>{} ";
|
||||
case "<<":
|
||||
return " {}\\ll{} ";
|
||||
case ">>":
|
||||
return " {}\\gg{} ";
|
||||
case "\\pm":
|
||||
return " {}\\pm{} ";
|
||||
case "$\\approx$":
|
||||
case "\\approx":
|
||||
return " {}\\approx{} ";
|
||||
case "(v)":
|
||||
case "v":
|
||||
return " \\downarrow{} ";
|
||||
case "(^)":
|
||||
case "^":
|
||||
return " \\uparrow{} ";
|
||||
default:
|
||||
throw new RuntimeException( "MhchemBugT: mhchem bug T. Please report." );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
79
src/TexVC/Mhchem/MhchemUtil.php
普通文件
79
src/TexVC/Mhchem/MhchemUtil.php
普通文件
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2023 Johannes Stegmüller
|
||||
*
|
||||
* This file is a port of mhchemParser originally authored by Martin Hensel in javascript/typescript.
|
||||
* The original license for this software can be found in the accompanying LICENSE.mhchemParser-ts.txt file.
|
||||
*/
|
||||
|
||||
namespace MediaWiki\Extension\Math\TexVC\Mhchem;
|
||||
|
||||
/**
|
||||
* Some utility classes mostly for creating similar functionalities
|
||||
* like in javascript in PHP.
|
||||
*
|
||||
* concatArray method here has the same functionality as concatArray (~l.194)
|
||||
* in mhchemParser.js by Martin Hensel.
|
||||
*
|
||||
* @author Johannes Stegmüller
|
||||
* @license GPL-2.0-or-later
|
||||
*/
|
||||
class MhchemUtil {
|
||||
|
||||
/**
|
||||
* The input is used as boolean operator in a javascript-type if condition,
|
||||
* example: "if(input)"
|
||||
* output has the same boolean results as an if-condition in javascript.
|
||||
* arrays as input have to be used like this "issetJS($arr["b"] ?? null);"
|
||||
* properties as input have to be used like this "issetJS($inst->prop ?? null);"
|
||||
* @param mixed|null $input input to be checked in a javascript-type if condition
|
||||
* @return bool indicator if input is populated
|
||||
*/
|
||||
public static function issetJS( $input ): bool {
|
||||
if ( $input === 0 || $input == "" ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the incoming string is containing a regex pattern.
|
||||
* @param string $input string to verify
|
||||
* @param string $subject subject to check, usually empty string
|
||||
* @return bool true if regex pattern, false if not
|
||||
*/
|
||||
public static function isRegex( string $input, string $subject = "" ): bool {
|
||||
/**
|
||||
* Ignoring preg_match phpcs error here, since this is the fastest variant: 582 ms for MMLmhchemTestLocal,
|
||||
* 835 ms for the try catch version of this, 735 ms for the version deactivating error handler
|
||||
* during function call.
|
||||
* See: https://stackoverflow.com/questions/16039362/how-can-i-suppress-phpcs-warnings-using-comments
|
||||
*/
|
||||
|
||||
// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
|
||||
return !( @preg_match( $input, $subject ) === false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an array is an associative array
|
||||
* @param array $array to check
|
||||
* @return bool true if associative, otherwise false
|
||||
*/
|
||||
public static function isAssoc( array $array ): bool {
|
||||
return ( $array !== array_values( $array ) );
|
||||
}
|
||||
|
||||
public static function concatArray( &$a, $b ) {
|
||||
if ( self::issetJS( $b ) ) {
|
||||
if ( is_array( $b ) && ( self::isAssoc( $b ) ) ) {
|
||||
$a[] = $b;
|
||||
} elseif ( is_array( $b ) && !self::isAssoc( $b ) ) {
|
||||
foreach ( $b as $value ) {
|
||||
$a[] = $value;
|
||||
}
|
||||
} else {
|
||||
$a[] = $b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
namespace MediaWiki\Extension\Math\TexVC\Mhchem;
|
||||
|
||||
use Exception;
|
||||
use MediaWiki\Extension\Math\TexVC\MMLmappings\Util\MMLTestUtil;
|
||||
use MediaWiki\Extension\Math\TexVC\MMLmappings\Util\MMLTestUtilHTML;
|
||||
use MediaWiki\Extension\Math\TexVC\TexVC;
|
||||
use MediaWikiUnitTestCase;
|
||||
|
||||
/**
|
||||
* This test checks the functionality MHCHem module within MediaWiki environment
|
||||
* It is running all defined testcases from "Mhchemv4mml.json" these are from test.html from javascript mhchemparser.
|
||||
* Mhchemv4mml.json can be recreated by running the maintenance script JsonToMathML with Mhchemv4tex.json
|
||||
* within a MediaWiki environment:
|
||||
* 'php extensions/Math/maintenance/JsonToMathML.php
|
||||
* /var/www/html/extensions/Math/tests/phpunit/unit/TexVC/Mhchem/Mhchemv4tex.json
|
||||
* /var/www/html/extensions/Math/tests/phpunit/unit/TexVC/Mhchem/Mhchemv4mml.json -i 3'
|
||||
*
|
||||
* Settings for running the testbench are defined as constants within this class.
|
||||
*
|
||||
* @covers \MediaWiki\Extension\Math\TexVC\TexVC
|
||||
*/
|
||||
final class MMLmhchemTest extends MediaWikiUnitTestCase {
|
||||
private static bool $LOGMHCHEM = false;
|
||||
private static bool $SKIPXMLVALIDATION = false;
|
||||
private static string $FILENAMEREF = __DIR__ . "/Mhchemv4mml.json";
|
||||
|
||||
private static bool $APPLYFILTER = false;
|
||||
private static int $FILTERSTART = 93;
|
||||
private static int $FILTERLENGTH = 1;
|
||||
private static bool $GENERATEHTML = false;
|
||||
private static string $GENERATEDHTMLFILE = __DIR__ . "/MMLmhchemTest-Output.html";
|
||||
private static array $SKIPPEDINDICES = [];
|
||||
|
||||
public static function setUpBeforeClass(): void {
|
||||
MMLTestUtilHTML::generateHTMLstart( self::$GENERATEDHTMLFILE, [ "name","TeX-Input",
|
||||
"Tex-MhchemParser","Tex-PHP-Mhchem" ], self::$GENERATEHTML );
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass(): void {
|
||||
MMLTestUtilHTML::generateHTMLEnd( self::$GENERATEDHTMLFILE, self::$GENERATEHTML );
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideTestCases
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testTexVC( $title, $tc ) {
|
||||
$texVC = new TexVC();
|
||||
|
||||
if ( in_array( $tc->ctr, self::$SKIPPEDINDICES ) ) {
|
||||
MMLTestUtilHTML::generateHTMLtableRow( self::$GENERATEDHTMLFILE, [ $tc->ctr, $tc->tex,
|
||||
"skipped", "skipped", "skipped" ], false, self::$GENERATEHTML );
|
||||
$this->assertTrue( true );
|
||||
return;
|
||||
}
|
||||
|
||||
# Fetch result from TexVC(PHP)
|
||||
$texVC->check( $tc->tex, [
|
||||
'debug' => false,
|
||||
'usemathrm' => false,
|
||||
'oldtexvc' => false,
|
||||
'usemhchem' => true
|
||||
] );
|
||||
|
||||
$mhchemParser = new MhchemParser( self::$LOGMHCHEM );
|
||||
$mhchemOutput = $mhchemParser->toTex( $tc->tex, $tc->typeC );
|
||||
|
||||
MMLTestUtilHTML::generateHTMLtableRow(
|
||||
self::$GENERATEDHTMLFILE, [ $title, $tc->tex, $tc->texNew, $mhchemOutput ],
|
||||
false, self::$GENERATEHTML );
|
||||
|
||||
if ( !self::$SKIPXMLVALIDATION ) {
|
||||
$this->assertEquals( $tc->texNew, $mhchemOutput );
|
||||
} else {
|
||||
$this->assertTrue( true );
|
||||
}
|
||||
}
|
||||
|
||||
public static function provideTestCases() {
|
||||
$fileTestcases = MMLTestUtil::getJSON( self::$FILENAMEREF );
|
||||
|
||||
$f = [];
|
||||
// Adding running indices for location of tests.
|
||||
$ctr = 0;
|
||||
foreach ( $fileTestcases as $tcF ) {
|
||||
$tc = [
|
||||
"ctr" => $ctr,
|
||||
"tex" => $tcF->tex,
|
||||
"texNew" => $tcF->texNew,
|
||||
"type" => $tcF->type,
|
||||
"typeC" => $tcF->typeC,
|
||||
"mml_mathoid" => $tcF->mmlMathoid,
|
||||
"mml_latexml" => $tcF->mmlLaTeXML,
|
||||
];
|
||||
$f[] = [ "tc#" . str_pad( $ctr, 3, '0', STR_PAD_LEFT ) . " " . $tcF->description,
|
||||
(object)$tc ];
|
||||
$ctr++;
|
||||
}
|
||||
// Filtering results by index if necessary
|
||||
if ( self::$APPLYFILTER ) {
|
||||
$f = array_slice( $f, self::$FILTERSTART, self::$FILTERLENGTH );
|
||||
}
|
||||
return $f;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,385 @@
|
|||
<?php
|
||||
|
||||
namespace MediaWiki\Extension\Math\TexVC\Mhchem;
|
||||
|
||||
use MediaWikiUnitTestCase;
|
||||
|
||||
/**
|
||||
* Some simple tests to test specific functions within
|
||||
* MhchemParser in PHP.
|
||||
*
|
||||
* @covers \MediaWiki\Extension\Math\TexVC\TexVC
|
||||
*/
|
||||
final class MhchemBasicTest extends MediaWikiUnitTestCase {
|
||||
|
||||
public function testZk() {
|
||||
// ReplaceFirst in MhchemTexify is introduced with this test, is this meant to be replace in javascript ?
|
||||
$input = "\ce{{Zk_{c} e^2 m_{e}}}";
|
||||
$mhchemParser = new MhchemParser();
|
||||
$out = $mhchemParser->toTex( $input, "ce" );
|
||||
$this->assertEquals( "{{\mathrm{Zk_{c}~e^2 m_{e}}}}", $out );
|
||||
}
|
||||
|
||||
public function testTextPattern() {
|
||||
// With this test a fix for the a^2 pattern to ^\\\_ instead ^\\x was introduced
|
||||
$input = "K^\\text{b, 2}";
|
||||
$mhchemParser = new MhchemParser();
|
||||
$out = $mhchemParser->toTex( $input, "ce" );
|
||||
$this->assertEquals( "{\mathrm{K}{\\vphantom{A}}^{\\text{b, 2}}}", $out );
|
||||
}
|
||||
|
||||
public function testFormulaDollarPattern() {
|
||||
$input = "\ce{H_{n + 2}}";
|
||||
$mhchemParser = new MhchemParser();
|
||||
$out = $mhchemParser->toTex( $input, "ce" );
|
||||
$this->assertEquals( "{\mathrm{H}{\\vphantom{A}}_{\smash[t]{n + 2}}}", $out );
|
||||
}
|
||||
|
||||
public function testPhantom() {
|
||||
$input = "\ce{-C-O-}";
|
||||
$mhchemParser = new MhchemParser();
|
||||
$out = $mhchemParser->toTex( $input, "ce" );
|
||||
$this->assertEquals( "{{-}\mathrm{C}{-}\mathrm{O}{\\vphantom{A}}^{-}}", $out );
|
||||
}
|
||||
|
||||
public function testH20withPU() {
|
||||
$input = "H2O (\pu{1g})";
|
||||
$mhchemParser = new MhchemParser();
|
||||
$out = $mhchemParser->toTex( $input, "ce" );
|
||||
$this->assertEquals( "{\mathrm{H}{\\vphantom{A}}_{\smash[t]{2}}\mathrm{O}~({1~\mathrm{g}})}", $out );
|
||||
}
|
||||
|
||||
public function testPUt1010() {
|
||||
$input = "10^-10 m";
|
||||
$mhchemParser = new MhchemParser();
|
||||
$out = $mhchemParser->toTex( $input, "pu" );
|
||||
$this->assertEquals( "{10^{-10}~\mathrm{m}}", $out );
|
||||
}
|
||||
|
||||
public function testPUtimes() {
|
||||
$input = "7.8 \\times 10^-10 m";
|
||||
$mhchemParser = new MhchemParser();
|
||||
$out = $mhchemParser->toTex( $input, "pu" );
|
||||
$this->assertEquals( "{7.8\\times 10^{-10}~\mathrm{m}}", $out );
|
||||
}
|
||||
|
||||
public function testPUE10() {
|
||||
$input = "E10";
|
||||
$mhchemParser = new MhchemParser();
|
||||
$out = $mhchemParser->toTex( $input, "pu" );
|
||||
$this->assertEquals( "{10^{10}}", $out );
|
||||
}
|
||||
|
||||
public function testXParenthesis() {
|
||||
$input = "\${x}$";
|
||||
$mhchemParser = new MhchemParser();
|
||||
$out = $mhchemParser->toTex( $input, "ce" );
|
||||
$this->assertEquals( "{x }", $out );
|
||||
}
|
||||
|
||||
public function testH2OAmount() {
|
||||
$input = "\$n$/2 H2O";
|
||||
$mhchemParser = new MhchemParser();
|
||||
$out = $mhchemParser->toTex( $input, "ce" );
|
||||
$this->assertEquals(
|
||||
"{\mathchoice{\\textstyle\\frac{n}{2}}{\\frac{n}{2}}{\\frac{n}{2}}{\\frac{n}{2}}\,\mathrm{H}" .
|
||||
"{\\vphantom{A}}_{\smash[t]{2}}\mathrm{O}}", $out );
|
||||
}
|
||||
|
||||
public function testH1() {
|
||||
$input = "H^°";
|
||||
$mhchemParser = new MhchemParser();
|
||||
$out = $mhchemParser->toTex( $input, "ce" );
|
||||
$this->assertEquals( "{\mathrm{H}{\\vphantom{A}}^{°}}", $out );
|
||||
}
|
||||
|
||||
public function testDollar1() {
|
||||
$input = "A $\pm$ B";
|
||||
$mhchemParser = new MhchemParser();
|
||||
$out = $mhchemParser->toTex( $input, "ce" );
|
||||
$this->assertEquals( "{\mathrm{A} {}\pm{} \mathrm{B}}", $out );
|
||||
}
|
||||
|
||||
public function testDollar2() {
|
||||
$input = "1/2\$n\$ H2O";
|
||||
$mhchemParser = new MhchemParser();
|
||||
$out = $mhchemParser->toTex( $input, "ce" );
|
||||
$this->assertEquals(
|
||||
"{\mathchoice{\\textstyle\\frac{1}{2}}{\\frac{1}{2}}{\\frac{1}{2}}" .
|
||||
"{\\frac{1}{2}}n \,\mathrm{H}{\\vphantom{A}}_{\smash[t]{2}}\\mathrm{O}}", $out );
|
||||
}
|
||||
|
||||
public function testApattern() {
|
||||
$input = "^0_-1n-";
|
||||
$pattern = "/^\^([0-9]+|[^\\_])/";
|
||||
|
||||
$matches = [];
|
||||
$match = preg_match( $pattern, $input, $matches );
|
||||
|
||||
$mhchemPatterns = new MhchemPatterns();
|
||||
$matchesR = $mhchemPatterns->match( "^a", $input );
|
||||
|
||||
$this->assertSame( 1, $match );
|
||||
$this->assertSame( "0", $matchesR["match_"] );
|
||||
$this->assertEquals( "_-1n-", $matchesR["remainder"] );
|
||||
}
|
||||
|
||||
public function testLettersPattern() {
|
||||
$input = "mu-Cl";
|
||||
$pattern = '/^(?:[a-zA-Z\x{03B1}-\x{03C9}\x{0391}-\x{03A9}?@]|(?:\\\\(?:alpha|beta|gamma|delta|epsilon|zeta|' .
|
||||
'eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|Gamma' .
|
||||
'|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)(?:\s+|\{\}|(?![a-zA-Z]))))+/u';
|
||||
$matches = [];
|
||||
$match = preg_match( $pattern, $input, $matches );
|
||||
$this->assertTrue( $match == 1 );
|
||||
}
|
||||
|
||||
public function testStateOfAggregationPattern() {
|
||||
$input = "(\\ca\$c\$)";
|
||||
$match = preg_match( '/^(?:\((?:\\\\ca\s?)?\$[amothc]\$\))/', $input );
|
||||
$this->assertTrue( $match == 1 );
|
||||
}
|
||||
|
||||
public function testCelsiusPattern() {
|
||||
$input = "°C";
|
||||
$output = preg_replace( "/\x{00B0}C|\^oC|\^{o}C/u", "{}^{\\circ}C", $input );
|
||||
$target = "{}^{\circ}C";
|
||||
$this->assertEquals( $target, $output );
|
||||
}
|
||||
|
||||
public function testPatternsMatchObsGroups() {
|
||||
$mhchemPatterns = new MhchemPatterns();
|
||||
$a = $mhchemPatterns->findObserveGroups( "(aq)", "",
|
||||
new MhchemRegExp( '/^\\([a-z]{1,3}(?=[\\),])/' ), ")", "" );
|
||||
$target = [
|
||||
"match_" => "(aq)",
|
||||
"remainder" => ""
|
||||
];
|
||||
$this->assertEquals( $target, $a );
|
||||
}
|
||||
|
||||
public function testIssetJS() {
|
||||
$this->assertFalse( MhchemUtil::issetJS( "" ) );
|
||||
$this->assertFalse( MhchemUtil::issetJS( null ) );
|
||||
$this->assertFalse( MhchemUtil::issetJS( false ) );
|
||||
$this->assertFalse( MhchemUtil::issetJS( 0 ) );
|
||||
|
||||
// checkEmpty(new Object("")); tbd
|
||||
$this->assertTrue( MhchemUtil::issetJS( new \stdClass( "" ) ) );
|
||||
$this->assertTrue( MhchemUtil::issetJS( "abc" ) );
|
||||
$this->assertTrue( MhchemUtil::issetJS( "false" ) );
|
||||
$this->assertTrue( MhchemUtil::issetJS( [] ) );
|
||||
$this->assertTrue( MhchemUtil::issetJS( "0" ) );
|
||||
$this->assertTrue( MhchemUtil::issetJS( 123 ) );
|
||||
|
||||
// Also the function should not crash when checking nested non-existent properties but return false
|
||||
$test1 = [ "b" => 123 ];
|
||||
$this->assertFalse( MhchemUtil::issetJS( $test1["a"] ?? null ) );
|
||||
$this->assertTrue( MhchemUtil::issetJS( $test1["b"] ) );
|
||||
}
|
||||
|
||||
public function testTransitionsInitTex() {
|
||||
$empty = [
|
||||
"pattern" => "empty",
|
||||
"task" => [
|
||||
"action_" => [
|
||||
[ "type_" => "copy" ]
|
||||
],
|
||||
"stateArray" => [
|
||||
"0"
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
$ce = [
|
||||
"pattern" => "\\ce{(...)}",
|
||||
"task" => [
|
||||
"action_" => [
|
||||
[ "type_" => "write", "option" => "{" ],
|
||||
[ "type_" => "ce" ],
|
||||
[ "type_" => "write", "option" => "}" ],
|
||||
],
|
||||
"stateArray" => [
|
||||
"0"
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
$mhchemParser = new MhchemParser();
|
||||
$mhchemStateMachines = new MhchemStateMachines( $mhchemParser );
|
||||
$transitions = $mhchemStateMachines->stateMachines["tex"]["transitions"];
|
||||
|
||||
// When done eval(count($transition), NUMBER)
|
||||
$emptyGen = $transitions[0][0];
|
||||
$ceGen = $transitions[0][1];
|
||||
$this->assertEquals( $empty, $emptyGen );
|
||||
$this->assertEquals( $ce, $ceGen );
|
||||
}
|
||||
|
||||
public function testTransitionsInitCe() {
|
||||
$mhchemParser = new MhchemParser();
|
||||
$mhchemStateMachines = new MhchemStateMachines( $mhchemParser );
|
||||
$transitions = $mhchemStateMachines->stateMachines["ce"]["transitions"];
|
||||
// When done eval(count($transition), NUMBER)
|
||||
$this->assertCount( 23, $transitions );
|
||||
$this->assertCount( 61, $transitions["0"] );
|
||||
$this->assertCount( 50, $transitions["qD"] );
|
||||
}
|
||||
|
||||
public function testMHPatternsMatch() {
|
||||
$mhchemPatterns = new MhchemPatterns();
|
||||
$pattern = "\ce{(...)}";
|
||||
$matches = $mhchemPatterns->match( $pattern, "\ce{CO2 + C -> 2 CO}" );
|
||||
$this->assertEquals( "CO2 + C -> 2 CO", $matches["match_"] );
|
||||
$this->assertSame( "", $matches["remainder"] );
|
||||
}
|
||||
|
||||
public function testMHPatternsMatch2() {
|
||||
$mhchemPatterns = new MhchemPatterns();
|
||||
$matches = $mhchemPatterns->match( 'letters', "C" );
|
||||
$this->assertNotNull( $matches );
|
||||
}
|
||||
|
||||
public function testMMHPatternsMatch3() {
|
||||
$mhchemPatterns = new MhchemPatterns();
|
||||
$matches2 = $mhchemPatterns->match( '->', "C" );
|
||||
$this->assertNull( $matches2 );
|
||||
}
|
||||
|
||||
public function testMHChemParserGo1() {
|
||||
$target = [
|
||||
"type_" => "chemfive",
|
||||
"a" => [],
|
||||
"b" => [],
|
||||
"p" => [],
|
||||
"o" => [
|
||||
[
|
||||
"type_" => "rm",
|
||||
"p1" => "C"
|
||||
]
|
||||
],
|
||||
"q" => [],
|
||||
"d" => []
|
||||
];
|
||||
|
||||
$mhchemParser = new MhchemParser();
|
||||
$output = $mhchemParser->go( "C", "ce" );
|
||||
|
||||
$this->assertEquals( $target, $output[0] );
|
||||
}
|
||||
|
||||
public function testMHChemParserGo2() {
|
||||
$target = [
|
||||
"type_" => "chemfive",
|
||||
"a" => [],
|
||||
"b" => [],
|
||||
"p" => [],
|
||||
"o" => [
|
||||
[
|
||||
"type_" => "rm",
|
||||
"p1" => "CO"
|
||||
]
|
||||
],
|
||||
"q" => [
|
||||
"2"
|
||||
],
|
||||
"d" => []
|
||||
];
|
||||
|
||||
$mhchemParser = new MhchemParser();
|
||||
$output = $mhchemParser->go( "CO2", "ce" );
|
||||
|
||||
$this->assertEquals( $target, $output[0] );
|
||||
}
|
||||
|
||||
public function testMHChemParserGo3() {
|
||||
$target = [
|
||||
[
|
||||
"type_" => "arrow",
|
||||
"r" => "->",
|
||||
"rd" => [],
|
||||
"rq" => []
|
||||
]
|
||||
];
|
||||
|
||||
$mhchemParser = new MhchemParser();
|
||||
$output = $mhchemParser->go( "->", "ce" );
|
||||
|
||||
$this->assertEquals( $target, $output );
|
||||
}
|
||||
|
||||
public function testMHChemParserGo4() {
|
||||
$target = [
|
||||
[
|
||||
"type_" => "chemfive",
|
||||
"a" => [],
|
||||
"b" => [],
|
||||
"p" => [],
|
||||
"o" => [
|
||||
"+"
|
||||
],
|
||||
"q" => [],
|
||||
"d" => []
|
||||
]
|
||||
];
|
||||
|
||||
$mhchemParser = new MhchemParser();
|
||||
$output = $mhchemParser->go( "+", "ce" );
|
||||
|
||||
$this->assertEquals( $target, $output );
|
||||
}
|
||||
|
||||
public function testMHChemParserGo5() {
|
||||
// order of patterns correct, hit different pattern
|
||||
$mhchemParser = new MhchemParser();
|
||||
$output = $mhchemParser->go( "CO2 + ", "ce" );
|
||||
$this->assertCount( 2, $output );
|
||||
$this->assertEquals( "chemfive", $output[0]["type_"] );
|
||||
$this->assertEquals( "operator", $output[1]["type_"] );
|
||||
}
|
||||
|
||||
public function testMHChemParserGo6() {
|
||||
$mhchemParser = new MhchemParser();
|
||||
$output = $mhchemParser->go( "CO2 + C -> 2 CO", "ce" );
|
||||
$this->assertCount( 5, $output );
|
||||
$this->assertEquals( "chemfive", $output[0]["type_"] );
|
||||
$this->assertEquals( "operator", $output[1]["type_"] );
|
||||
$this->assertEquals( "chemfive", $output[2]["type_"] );
|
||||
$this->assertEquals( "arrow", $output[3]["type_"] );
|
||||
$this->assertEquals( 2, $output[4]["a"][0] );
|
||||
$this->assertEquals( "CO", $output[4]["o"][0]["p1"] );
|
||||
}
|
||||
|
||||
public function testMHChemTexify() {
|
||||
$mhchemParser = new MhchemParser();
|
||||
$output = $mhchemParser->toTex( "CO2", "ce" );
|
||||
$this->assertEquals( "{\\mathrm{CO}{\\vphantom{A}}_{\\smash[t]{2}}}", $output );
|
||||
}
|
||||
|
||||
public function testConcatArray1() {
|
||||
$a = [];
|
||||
$b = [ "{" ];
|
||||
$target = [ "{" ];
|
||||
MhchemUtil::concatArray( $a, $b );
|
||||
$this->assertEquals( $target, $a );
|
||||
}
|
||||
|
||||
public function testConcatArray2() {
|
||||
$a = [];
|
||||
// This is an object in javascript typescript, in PHP this is currently an array.
|
||||
$b = [ "type_" => "rm", "p1" => "C" ];
|
||||
$target = [ $b ];
|
||||
MhchemUtil::concatArray( $a, $b );
|
||||
$this->assertEquals( $target, $a );
|
||||
}
|
||||
|
||||
public function testConcatArray3() {
|
||||
$a = [ "{" ];
|
||||
$b = [ "type_" => "chemfive" ];
|
||||
|
||||
$target = [ $a[0], $b ];
|
||||
MhchemUtil::concatArray( $a, $b );
|
||||
$this->assertEquals( $target, $a );
|
||||
}
|
||||
|
||||
}
|
文件差异因一行或多行过长而隐藏
|
@ -0,0 +1,635 @@
|
|||
{
|
||||
"Init": [
|
||||
{
|
||||
"tex": "m_{\\ce{H2O}} = \\pu{1.2kg}",
|
||||
"texNew": "m_{{\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}} = {1.2~\\mathrm{kg}}",
|
||||
"type": "tex"
|
||||
}
|
||||
],
|
||||
"Chemical Equations": [
|
||||
{
|
||||
"tex": "CO2 + C -> 2 CO",
|
||||
"texNew": "{\\mathrm{CO}{\\vphantom{A}}_{\\smash[t]{2}} {}+{} \\mathrm{C} {}\\mathrel{\\longrightarrow}{} 2\\,\\mathrm{CO}}",
|
||||
"type": "ce"
|
||||
},
|
||||
{
|
||||
"tex": "Hg^2+ ->[I-] HgI2 ->[I-] [Hg^{II}I4]^2-",
|
||||
"texNew": "{\\mathrm{Hg}{\\vphantom{A}}^{2+} {}\\mathrel{\\xrightarrow{\\mathrm{I}{\\vphantom{A}}^{-}}}{} \\mathrm{HgI}{\\vphantom{A}}_{\\smash[t]{2}} {}\\mathrel{\\xrightarrow{\\mathrm{I}{\\vphantom{A}}^{-}}}{} [\\mathrm{Hg}{\\vphantom{A}}^{\\mathrm{II}}\\mathrm{I}{\\vphantom{A}}_{\\smash[t]{4}}]{\\vphantom{A}}^{2-}}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Chemical Formulae": [
|
||||
{
|
||||
"tex": "H2O",
|
||||
"texNew": "{\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}",
|
||||
"type": "ce"
|
||||
},
|
||||
{
|
||||
"tex": "Sb2O3",
|
||||
"texNew": "{\\mathrm{Sb}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}{\\vphantom{A}}_{\\smash[t]{3}}}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Charges": [
|
||||
{
|
||||
"tex": "H+",
|
||||
"texNew": "{\\mathrm{H}{\\vphantom{A}}^{+}}",
|
||||
"type": "ce"
|
||||
},
|
||||
{
|
||||
"tex": "CrO4^2-",
|
||||
"texNew": "{\\mathrm{CrO}{\\vphantom{A}}_{\\smash[t]{4}}{\\vphantom{A}}^{2-}}",
|
||||
"type": "ce"
|
||||
},
|
||||
{
|
||||
"tex": "[AgCl2]-",
|
||||
"texNew": "{[\\mathrm{AgCl}{\\vphantom{A}}_{\\smash[t]{2}}]{\\vphantom{A}}^{-}}",
|
||||
"type": "ce"
|
||||
},
|
||||
{
|
||||
"tex": "Y^99+",
|
||||
"texNew": "{\\mathrm{Y}{\\vphantom{A}}^{99+}}",
|
||||
"type": "ce"
|
||||
},
|
||||
{
|
||||
"tex": "Y^{99+}",
|
||||
"texNew": "{\\mathrm{Y}{\\vphantom{A}}^{99+}}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Stoichiometric Numbers": [
|
||||
{
|
||||
"tex": "2 H2O",
|
||||
"texNew": "{2\\,\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}",
|
||||
"type": "ce"
|
||||
},
|
||||
{
|
||||
"tex": "2H2O",
|
||||
"texNew": "{2\\,\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}",
|
||||
"type": "ce"
|
||||
},
|
||||
{
|
||||
"tex": "0.5 H2O",
|
||||
"texNew": "{0.5\\,\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}",
|
||||
"type": "ce"
|
||||
},
|
||||
{
|
||||
"tex": "1/2 H2O",
|
||||
"texNew": "{\\mathchoice{\\textstyle\\frac{1}{2}}{\\frac{1}{2}}{\\frac{1}{2}}{\\frac{1}{2}}\\,\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}",
|
||||
"type": "ce"
|
||||
},
|
||||
{
|
||||
"tex": "(1/2) H2O",
|
||||
"texNew": "{(1/2)\\,\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}",
|
||||
"type": "ce"
|
||||
},
|
||||
{
|
||||
"tex": "$n$ H2O",
|
||||
"texNew": "{n \\,\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}",
|
||||
"type": "ce"
|
||||
},
|
||||
{
|
||||
"tex": "n H2O",
|
||||
"texNew": "{n\\,\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}",
|
||||
"type": "ce"
|
||||
},
|
||||
{
|
||||
"tex": "nH2O",
|
||||
"texNew": "{n\\,\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}",
|
||||
"type": "ce"
|
||||
},
|
||||
{
|
||||
"tex": "n/2 H2O",
|
||||
"texNew": "{\\mathchoice{\\textstyle\\frac{n}{2}}{\\frac{n}{2}}{\\frac{n}{2}}{\\frac{n}{2}}\\,\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Nuclides, Isotopes": [
|
||||
{
|
||||
"tex": "^{227}_{90}Th+",
|
||||
"texNew": "{{\\vphantom{A}}^{\\hphantom{227}}_{\\hphantom{90}}\\mkern-1.5mu{\\vphantom{A}}^{\\smash[t]{\\vphantom{2}}\\llap{227}}_{\\vphantom{2}\\llap{\\smash[t]{90}}}\\mathrm{Th}{\\vphantom{A}}^{+}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "^{0}_{-1}n^{-}",
|
||||
"texNew": "{{\\vphantom{A}}^{\\hphantom{0}}_{\\hphantom{-1}}\\mkern-1.5mu{\\vphantom{A}}^{\\smash[t]{\\vphantom{2}}\\llap{0}}_{\\vphantom{2}\\llap{\\smash[t]{-1}}}\\mathrm{n}{\\vphantom{A}}^{-}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "^0_-1n-",
|
||||
"texNew": "{{\\vphantom{A}}^{\\hphantom{0}}_{\\hphantom{-1}}\\mkern-1.5mu{\\vphantom{A}}^{\\smash[t]{\\vphantom{2}}\\llap{0}}_{\\vphantom{2}\\llap{\\smash[t]{-1}}}\\mathrm{n}{\\vphantom{A}}^{-}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "H{}^3HO",
|
||||
"texNew": "{\\mathrm{H}\\mkern2mu{\\vphantom{A}}^{\\hphantom{3}}_{\\hphantom{}}\\mkern-1.5mu{\\vphantom{A}}^{\\smash[t]{\\vphantom{2}}\\llap{3}}_{\\vphantom{2}\\llap{\\smash[t]{}}}\\mathrm{HO}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "H^3HO",
|
||||
"texNew": "{\\mathrm{H}\\mkern2mu{\\vphantom{A}}^{\\hphantom{3}}_{\\hphantom{}}\\mkern-1.5mu{\\vphantom{A}}^{\\smash[t]{\\vphantom{2}}\\llap{3}}_{\\vphantom{2}\\llap{\\smash[t]{}}}\\mathrm{HO}}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Reaction Arrows": [
|
||||
{
|
||||
"tex": "A -> B",
|
||||
"texNew": "{\\mathrm{A} {}\\mathrel{\\longrightarrow}{} \\mathrm{B}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A <- B",
|
||||
"texNew": "{\\mathrm{A} {}\\mathrel{\\longleftarrow}{} \\mathrm{B}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A <-> B",
|
||||
"texNew": "{\\mathrm{A} {}\\mathrel{\\longleftrightarrow}{} \\mathrm{B}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A <--> B",
|
||||
"texNew": "{\\mathrm{A} {}\\mathrel{\\longleftrightarrows}{} \\mathrm{B}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A <=> B",
|
||||
"texNew": "{\\mathrm{A} {}\\mathrel{\\longrightleftharpoons}{} \\mathrm{B}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A <=>> B",
|
||||
"texNew": "{\\mathrm{A} {}\\mathrel{\\longRightleftharpoons}{} \\mathrm{B}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A <<=> B",
|
||||
"texNew": "{\\mathrm{A} {}\\mathrel{\\longLeftrightharpoons}{} \\mathrm{B}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A ->[H2O] B",
|
||||
"texNew": "{\\mathrm{A} {}\\mathrel{\\xrightarrow{\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}}{} \\mathrm{B}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A ->[{text above}][{text below}] B",
|
||||
"texNew": "{\\mathrm{A} {}\\mathrel{\\xrightarrow[{{\\text{text below}}}]{{\\text{text above}}}}{} \\mathrm{B}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A ->[$x$][$x_i$] B",
|
||||
"texNew": "{\\mathrm{A} {}\\mathrel{\\xrightarrow[{x_i }]{x }}{} \\mathrm{B}}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Parentheses, Brackets, Braces": [
|
||||
{
|
||||
"tex": "(NH4)2S",
|
||||
"texNew": "{(\\mathrm{NH}{\\vphantom{A}}_{\\smash[t]{4}}){\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{S}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "[\\{(X2)3\\}2]^3+",
|
||||
"texNew": "{[\\{(\\mathrm{X}{\\vphantom{A}}_{\\smash[t]{2}}){\\vphantom{A}}_{\\smash[t]{3}}\\}{\\vphantom{A}}_{\\smash[t]{2}}]{\\vphantom{A}}^{3+}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "CH4 + 2 $\\left( \\ce{O2 + 79/21 N2} \\right)$",
|
||||
"texNew": "{\\mathrm{CH}{\\vphantom{A}}_{\\smash[t]{4}} {}+{} 2\\,\\left( \\mathrm{O}{\\vphantom{A}}_{\\smash[t]{2}} {}+{} \\mathchoice{\\textstyle\\frac{79}{21}}{\\frac{79}{21}}{\\frac{79}{21}}{\\frac{79}{21}}\\,\\mathrm{N}{\\vphantom{A}}_{\\smash[t]{2}} \\right) }",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"States of Aggregation": [
|
||||
{
|
||||
"tex": "H2(aq)",
|
||||
"texNew": "{\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mskip2mu (\\mathrm{aq})}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "CO3^2-_{(aq)}",
|
||||
"texNew": "{\\mathrm{CO}{\\vphantom{A}}_{\\smash[t]{3}}{\\vphantom{A}}^{2-}{\\vphantom{A}}_{\\smash[t]{\\mskip1mu (\\mathrm{aq})}}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "CO3^2-{}_{(aq)}",
|
||||
"texNew": "{\\mathrm{CO}{\\vphantom{A}}_{\\smash[t]{3}}{\\vphantom{A}}^{2-}{\\vphantom{A}}_{\\smash[t]{\\mskip1mu (\\mathrm{aq})}}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "NaOH(aq,$\\infty$)",
|
||||
"texNew": "{\\mathrm{NaOH}\\mskip2mu (\\mathrm{aq},\\infty )}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Crystal Systems": [
|
||||
{
|
||||
"tex": "ZnS ($c$)",
|
||||
"texNew": "{\\mathrm{ZnS}\\mskip2mu (c )}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "ZnS (\\ca$c$)",
|
||||
"texNew": "{\\mathrm{ZnS}\\mskip2mu ({\\sim}c )}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Variables like __*x*, *n*, 2*n*+1__": [
|
||||
{
|
||||
"tex": "NO_x",
|
||||
"texNew": "{\\mathrm{NO}{\\vphantom{A}}_{\\smash[t]{x }}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "Fe^n+",
|
||||
"texNew": "{\\mathrm{Fe}{\\vphantom{A}}^{n +}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "x Na(NH4)HPO4 ->[Delta] (NaPO3)_x + x NH3 ^ + x H2O",
|
||||
"texNew": "{x\\,\\mathrm{Na}(\\mathrm{NH}{\\vphantom{A}}_{\\smash[t]{4}})\\mathrm{HPO}{\\vphantom{A}}_{\\smash[t]{4}} {}\\mathrel{\\xrightarrow{\\mathrm{Delta}}}{} (\\mathrm{NaPO}{\\vphantom{A}}_{\\smash[t]{3}}){\\vphantom{A}}_{\\smash[t]{x }} {}+{} x\\,\\mathrm{NH}{\\vphantom{A}}_{\\smash[t]{3}} \\uparrow{} {}+{} x\\,\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Greek Characters": [
|
||||
{
|
||||
"tex": "mu-Cl",
|
||||
"texNew": "{\\mathrm{mu}{-}\\mathrm{Cl}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "[Pt(\\eta^2-C2H4)Cl3]-",
|
||||
"texNew": "{[\\mathrm{Pt}(\\mathrm{\\eta}{\\vphantom{A}}^{2}\\text{-}\\mathrm{C}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{4}})\\mathrm{Cl}{\\vphantom{A}}_{\\smash[t]{3}}]{\\vphantom{A}}^{-}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "\\beta +",
|
||||
"texNew": "{\\mathrm{\\beta }{\\vphantom{A}}^{+}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "^40_18Ar + \\gamma{} + \\nu_e",
|
||||
"texNew": "{{\\vphantom{A}}^{\\hphantom{40}}_{\\hphantom{18}}\\mkern-1.5mu{\\vphantom{A}}^{\\smash[t]{\\vphantom{2}}\\llap{40}}_{\\vphantom{2}\\llap{\\smash[t]{18}}}\\mathrm{Ar} {}+{} \\mathrm{\\gamma{}} {}+{} \\mathrm{\\nu}{\\vphantom{A}}_{\\smash[t]{e }}}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"(Italic) Math": [
|
||||
{
|
||||
"tex": "NaOH(aq,$\\infty$)",
|
||||
"texNew": "{\\mathrm{NaOH}\\mskip2mu (\\mathrm{aq},\\infty )}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "Fe(CN)_{$\\frac{6}{2}$}",
|
||||
"texNew": "{\\mathrm{Fe}(\\mathrm{CN}){\\vphantom{A}}_{\\smash[t]{\\frac{6}{2} }}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "X_{$i$}^{$x$}",
|
||||
"texNew": "{\\mathrm{X}{\\vphantom{A}}_{\\smash[t]{i }}{\\vphantom{A}}^{x }}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "X_$i$^$x$",
|
||||
"texNew": "{\\mathrm{X}{\\vphantom{A}}_{\\smash[t]{i }}{\\vphantom{A}}^{x }}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Italic Text": [
|
||||
{
|
||||
"tex": "$cis${-}[PtCl2(NH3)2]",
|
||||
"texNew": "{cis {\\text{-}}[\\mathrm{PtCl}{\\vphantom{A}}_{\\smash[t]{2}}(\\mathrm{NH}{\\vphantom{A}}_{\\smash[t]{3}}){\\vphantom{A}}_{\\smash[t]{2}}]}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "CuS($hP12$)",
|
||||
"texNew": "{\\mathrm{CuS}(hP12 )}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Upright Text, Escape Parsing": [
|
||||
{
|
||||
"tex": "{Gluconic Acid} + H2O2",
|
||||
"texNew": "{{\\text{Gluconic Acid}} {}+{} \\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}{\\vphantom{A}}_{\\smash[t]{2}}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "X_{{red}}",
|
||||
"texNew": "{\\mathrm{X}{\\vphantom{A}}_{\\smash[t]{\\text{red}}}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "{(+)}_589{-}[Co(en)3]Cl3",
|
||||
"texNew": "{{\\text{(+)}}{\\vphantom{A}}_{\\smash[t]{589}}{\\text{-}}[\\mathrm{Co}(\\mathrm{en}){\\vphantom{A}}_{\\smash[t]{3}}]\\mathrm{Cl}{\\vphantom{A}}_{\\smash[t]{3}}}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Bonds": [
|
||||
{
|
||||
"tex": "C6H5-CHO",
|
||||
"texNew": "{\\mathrm{C}{\\vphantom{A}}_{\\smash[t]{6}}\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{5}}{-}\\mathrm{CHO}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A-B=C#D",
|
||||
"texNew": "{\\mathrm{A}{-}\\mathrm{B}{=}\\mathrm{C}{\\equiv}\\mathrm{D}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A\\bond{-}B\\bond{=}C\\bond{#}D",
|
||||
"texNew": "{\\mathrm{A}{-}\\mathrm{B}{=}\\mathrm{C}{\\equiv}\\mathrm{D}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A\\bond{1}B\\bond{2}C\\bond{3}D",
|
||||
"texNew": "{\\mathrm{A}{-}\\mathrm{B}{=}\\mathrm{C}{\\equiv}\\mathrm{D}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A\\bond{~}B\\bond{~-}C",
|
||||
"texNew": "{\\mathrm{A}{\\tripledash}\\mathrm{B}{\\rlap{\\lower.1em{-}}\\raise.1em{\\tripledash}}\\mathrm{C}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A\\bond{~--}B\\bond{~=}C\\bond{-~-}D",
|
||||
"texNew": "{\\mathrm{A}{\\rlap{\\lower.2em{-}}\\rlap{\\raise.2em{\\tripledash}}-}\\mathrm{B}{\\rlap{\\lower.2em{-}}\\rlap{\\raise.2em{\\tripledash}}-}\\mathrm{C}{\\rlap{\\lower.2em{-}}\\rlap{\\raise.2em{-}}\\tripledash}\\mathrm{D}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A\\bond{...}B\\bond{....}C",
|
||||
"texNew": "{\\mathrm{A}{{\\cdot}{\\cdot}{\\cdot}}\\mathrm{B}{{\\cdot}{\\cdot}{\\cdot}{\\cdot}}\\mathrm{C}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A\\bond{->}B\\bond{<-}C",
|
||||
"texNew": "{\\mathrm{A}{\\rightarrow}\\mathrm{B}{\\leftarrow}\\mathrm{C}}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Addition Compounds": [
|
||||
{
|
||||
"tex": "KCr(SO4)2*12H2O",
|
||||
"texNew": "{\\mathrm{KCr}(\\mathrm{SO}{\\vphantom{A}}_{\\smash[t]{4}}){\\vphantom{A}}_{\\smash[t]{2}}\\,{\\cdot}\\,12\\,\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "KCr(SO4)2.12H2O",
|
||||
"texNew": "{\\mathrm{KCr}(\\mathrm{SO}{\\vphantom{A}}_{\\smash[t]{4}}){\\vphantom{A}}_{\\smash[t]{2}}\\,{\\cdot}\\,12\\,\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "KCr(SO4)2 * 12 H2O",
|
||||
"texNew": "{\\mathrm{KCr}(\\mathrm{SO}{\\vphantom{A}}_{\\smash[t]{4}}){\\vphantom{A}}_{\\smash[t]{2}}\\,{\\cdot}\\,12\\,\\mathrm{H}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Oxidation States": [
|
||||
{
|
||||
"tex": "Fe^{II}Fe^{III}2O4",
|
||||
"texNew": "{\\mathrm{Fe}{\\vphantom{A}}^{\\mathrm{II}}\\mathrm{Fe}{\\vphantom{A}}^{\\mathrm{III}}{\\vphantom{A}}_{\\smash[t]{2}}\\mathrm{O}{\\vphantom{A}}_{\\smash[t]{4}}}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Unpaired Electrons, Radical Dots": [
|
||||
{
|
||||
"tex": "OCO^{.-}",
|
||||
"texNew": "{\\mathrm{OCO}{\\vphantom{A}}^{\\mkern1mu \\bullet\\mkern1mu -}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "NO^{(2.)-}",
|
||||
"texNew": "{\\mathrm{NO}{\\vphantom{A}}^{(2\\mkern1mu \\bullet\\mkern1mu )-}}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Kröger-Vink Notation": [
|
||||
{
|
||||
"tex": "Li^x_{Li,1-2x}Mg^._{Li,x}$V$'_{Li,x}Cl^x_{Cl}",
|
||||
"texNew": "{\\mathrm{Li}{\\vphantom{A}}^{{\\times}}_{\\smash[t]{\\mathrm{Li}{,}\\mkern1mu 1-2x }}\\mathrm{Mg}{\\vphantom{A}}^{\\mkern1mu \\bullet\\mkern1mu }_{\\smash[t]{\\mathrm{Li}{,}\\mkern1mu x }}V {\\vphantom{A}}^{\\prime }_{\\smash[t]{\\mathrm{Li}{,}\\mkern1mu x }}\\mathrm{Cl}{\\vphantom{A}}^{{\\times}}_{\\smash[t]{\\mathrm{Cl}}}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "O''_{i,x}",
|
||||
"texNew": "{\\mathrm{O}{\\vphantom{A}}^{\\prime \\prime }_{\\smash[t]{\\mathrm{i}{,}\\mkern1mu x }}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "M^{..}_i",
|
||||
"texNew": "{\\mathrm{M}{\\vphantom{A}}^{\\mkern1mu \\bullet\\mkern1mu \\mkern1mu \\bullet\\mkern1mu }_{\\smash[t]{\\mathrm{i}}}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "$V$^{4'}_{Ti}",
|
||||
"texNew": "{V {\\vphantom{A}}^{4\\prime }_{\\smash[t]{\\mathrm{Ti}}}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "V_{V,1}C_{C,0.8}$V$_{C,0.2}",
|
||||
"texNew": "{\\mathrm{V}{\\vphantom{A}}_{\\smash[t]{\\mathrm{V}{,}\\mkern1mu 1}}\\mathrm{C}{\\vphantom{A}}_{\\smash[t]{\\mathrm{C}{,}\\mkern1mu 0.8}}V {\\vphantom{A}}_{\\smash[t]{\\mathrm{C}{,}\\mkern1mu 0.2}}}",
|
||||
"type":"ce"
|
||||
}
|
||||
],
|
||||
"Equation Operators": [
|
||||
{
|
||||
"tex": "A + B",
|
||||
"texNew": "{\\mathrm{A} {}+{} \\mathrm{B}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A - B",
|
||||
"texNew": "{\\mathrm{A} {}-{} \\mathrm{B}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A = B",
|
||||
"texNew": "{\\mathrm{A} {}={} \\mathrm{B}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A \\pm B",
|
||||
"texNew": "{\\mathrm{A} {}\\pm{} \\mathrm{B}}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Precipitate and Gas": [
|
||||
{
|
||||
"tex": "SO4^2- + Ba^2+ -> BaSO4 v",
|
||||
"texNew": "{\\mathrm{SO}{\\vphantom{A}}_{\\smash[t]{4}}{\\vphantom{A}}^{2-} {}+{} \\mathrm{Ba}{\\vphantom{A}}^{2+} {}\\mathrel{\\longrightarrow}{} \\mathrm{BaSO}{\\vphantom{A}}_{\\smash[t]{4}} \\downarrow{} }",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A v B (v) -> B ^ B (^)",
|
||||
"texNew": "{\\mathrm{A} \\downarrow{} ~\\mathrm{B} \\downarrow{} {}\\mathrel{\\longrightarrow}{} \\mathrm{B} \\uparrow{} ~\\mathrm{B} \\uparrow{} }",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Other Symbols and Shortcuts": [
|
||||
{
|
||||
"tex": "NO^*",
|
||||
"texNew": "{\\mathrm{NO}{\\vphantom{A}}^{*}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "1s^2-N",
|
||||
"texNew": "{1\\mathrm{s}{\\vphantom{A}}^{2}\\text{-}\\mathrm{N}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "n-Pr",
|
||||
"texNew": "{n \\text{-}\\mathrm{Pr}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "iPr",
|
||||
"texNew": "{\\mathrm{iPr}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "\\ca Fe",
|
||||
"texNew": "{{\\sim}\\mathrm{Fe}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "A, B, C; F",
|
||||
"texNew": "{\\mathrm{A}{,}\\mkern6mu \\mathrm{B}{,}\\mkern6mu \\mathrm{C}{;}\\mkern6mu \\mathrm{F}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "{and others}",
|
||||
"texNew": "{{\\text{and others}}}",
|
||||
"type": "ce"
|
||||
}
|
||||
],
|
||||
"Complex Examples": [
|
||||
{
|
||||
"tex": "Zn^2+ <=>[+ 2OH-][+ 2H+] $\\underset{\\text{amphoteres Hydroxid}}{\\ce{Zn(OH)2 v}}$ <=>[+ 2OH-][+ 2H+] $\\underset{\\text{Hydroxozikat}}{\\ce{[Zn(OH)4]^2-}}$",
|
||||
"texNew": "{\\mathrm{Zn}{\\vphantom{A}}^{2+} {}\\mathrel{\\underset{\\lower6mu{ {}+{} 2\\,\\mathrm{H}{\\vphantom{A}}^{+}}}{\\overset{ {}+{} 2\\,\\mathrm{OH}{\\vphantom{A}}^{-}}{\\longrightleftharpoons}}}{} \\underset{\\text{amphoteres Hydroxid}}{\\ce{Zn(OH)2 v}} {}\\mathrel{\\underset{\\lower6mu{ {}+{} 2\\,\\mathrm{H}{\\vphantom{A}}^{+}}}{\\overset{ {}+{} 2\\,\\mathrm{OH}{\\vphantom{A}}^{-}}{\\longrightleftharpoons}}}{} \\underset{\\text{Hydroxozikat}}{\\ce{[Zn(OH)4]^2-}} }",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "$K = \\frac{[\\ce{Hg^2+}][\\ce{Hg}]}{[\\ce{Hg2^2+}]}$",
|
||||
"texNew": "{K = \\frac{[\\ce{Hg^2+}][\\ce{Hg}]}{[\\ce{Hg2^2+}]} }",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "$K = \\ce{\\frac{[Hg^2+][Hg]}{[Hg2^2+]}}$",
|
||||
"texNew": "{K = \\frac{[\\mathrm{Hg}{\\vphantom{A}}^{2+}][\\mathrm{Hg}]}{[\\mathrm{Hg}{\\vphantom{A}}_{\\smash[t]{2}}{\\vphantom{A}}^{2+}]}}",
|
||||
"type":"ce"
|
||||
},
|
||||
{
|
||||
"tex": "Hg^2+ ->[I-] $\\underset{\\mathrm{red}}{\\ce{HgI2}}$ ->[I-] $\\underset{\\mathrm{red}}{\\ce{[Hg^{II}I4]^2-}}$",
|
||||
"texNew": "{\\mathrm{Hg}{\\vphantom{A}}^{2+} {}\\mathrel{\\xrightarrow{\\mathrm{I}{\\vphantom{A}}^{-}}}{} \\underset{\\mathrm{red}}{\\ce{HgI2}} {}\\mathrel{\\xrightarrow{\\mathrm{I}{\\vphantom{A}}^{-}}}{} \\underset{\\mathrm{red}}{\\ce{[Hg^{II}I4]^2-}} }",
|
||||
"type":"ce"
|
||||
}
|
||||
],
|
||||
"pu": [
|
||||
{
|
||||
"tex": "123 kJ",
|
||||
"texNew": "{123~\\mathrm{kJ}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "123 mm2",
|
||||
"texNew": "{123~\\mathrm{mm^{2}}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "123 J s",
|
||||
"texNew": "{123~\\mathrm{J}\\mkern3mu \\mathrm{s}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "123 J*s",
|
||||
"texNew": "{123~\\mathrm{J}\\mkern1mu{\\cdot}\\mkern1mu \\mathrm{s}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "123 kJ/mol",
|
||||
"texNew": "{123~\\mathrm{kJ}/\\mathrm{mol}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "123 kJ//mol",
|
||||
"texNew": "{123~\\mathchoice{\\textstyle\\frac{\\mathrm{kJ}}{\\mathrm{mol}}}{\\frac{\\mathrm{kJ}}{\\mathrm{mol}}}{\\frac{\\mathrm{kJ}}{\\mathrm{mol}}}{\\frac{\\mathrm{kJ}}{\\mathrm{mol}}}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "123 kJ mol^-1",
|
||||
"texNew": "{123~\\mathrm{kJ}\\mkern3mu \\mathrm{mol^{-1}}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "123 kJ*mol-1",
|
||||
"texNew": "{123~\\mathrm{kJ}\\mkern1mu{\\cdot}\\mkern1mu \\mathrm{mol^{-1}}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "123 kJ.mol-1",
|
||||
"texNew": "{123~\\mathrm{kJ}\\mkern1mu{\\cdot}\\mkern1mu \\mathrm{mol^{-1}}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "1.2e3 kJ",
|
||||
"texNew": "{1.2\\cdot 10^{3}~\\mathrm{kJ}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "1,2e3 kJ",
|
||||
"texNew": "{1{,}2\\cdot 10^{3}~\\mathrm{kJ}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "1.2E3 kJ",
|
||||
"texNew": "{1.2\\times 10^{3}~\\mathrm{kJ}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "1,2E3 kJ",
|
||||
"texNew": "{1{,}2\\times 10^{3}~\\mathrm{kJ}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "1234",
|
||||
"texNew": "{1234}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "12345",
|
||||
"texNew": "{12\\mkern2mu 345}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "1\u00B0C",
|
||||
"texNew": "{1~\\mathrm{{}^{\\circ}C}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "23.4782(32) m",
|
||||
"texNew": "{23.4782(32)~\\mathrm{m}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "8.00001 \\pm 0.00005 nm",
|
||||
"texNew": "{8.000\\mkern2mu 01 {}\\pm{} 0.000\\mkern2mu 05~\\mathrm{nm}}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": ".25",
|
||||
"texNew": "{.25}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "1 mol ",
|
||||
"texNew": "{1~\\mathrm{mol}~}",
|
||||
"type":"pu"
|
||||
},
|
||||
{
|
||||
"tex": "123 l//100km",
|
||||
"texNew": "{123~\\mathchoice{\\textstyle\\frac{\\mathrm{l}}{100~\\mathrm{km}}}{\\frac{\\mathrm{l}}{100~\\mathrm{km}}}{\\frac{\\mathrm{l}}{100~\\mathrm{km}}}{\\frac{\\mathrm{l}}{100~\\mathrm{km}}}}",
|
||||
"type":"pu"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
}
|
||||
|
正在加载...
在新工单中引用
屏蔽一个用户