tex.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*************************************************************
  2. *
  3. * Copyright (c) 2017-2022 The MathJax Consortium
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /**
  18. * @fileoverview Implements the TeX InputJax object
  19. *
  20. * @author dpvc@mathjax.org (Davide Cervone)
  21. */
  22. import {AbstractInputJax} from '../core/InputJax.js';
  23. import {userOptions, separateOptions, OptionList} from '../util/Options.js';
  24. import {MathDocument} from '../core/MathDocument.js';
  25. import {MathItem} from '../core/MathItem.js';
  26. import {MmlNode} from '../core/MmlTree/MmlNode.js';
  27. import {MmlFactory} from '../core/MmlTree/MmlFactory.js';
  28. import {FindTeX} from './tex/FindTeX.js';
  29. import FilterUtil from './tex/FilterUtil.js';
  30. import NodeUtil from './tex/NodeUtil.js';
  31. import TexParser from './tex/TexParser.js';
  32. import TexError from './tex/TexError.js';
  33. import ParseOptions from './tex/ParseOptions.js';
  34. import {TagsFactory} from './tex/Tags.js';
  35. import {ParserConfiguration} from './tex/Configuration.js';
  36. // Import base as it is the default package loaded.
  37. import './tex/base/BaseConfiguration.js';
  38. /*****************************************************************/
  39. /*
  40. * Implements the TeX class (extends AbstractInputJax)
  41. */
  42. /**
  43. * @template N The HTMLElement node class
  44. * @template T The Text node class
  45. * @template D The Document class
  46. */
  47. export class TeX<N, T, D> extends AbstractInputJax<N, T, D> {
  48. /**
  49. * Name of input jax.
  50. * @type {string}
  51. */
  52. public static NAME: string = 'TeX';
  53. /**
  54. * Default options for the jax.
  55. * @type {OptionList}
  56. */
  57. public static OPTIONS: OptionList = {
  58. ...AbstractInputJax.OPTIONS,
  59. FindTeX: null,
  60. packages: ['base'],
  61. // Digit pattern to match numbers.
  62. digits: /^(?:[0-9]+(?:\{,\}[0-9]{3})*(?:\.[0-9]*)?|\.[0-9]+)/,
  63. // Maximum size of TeX string to process.
  64. maxBuffer: 5 * 1024,
  65. formatError: (jax: TeX<any, any, any>, err: TexError) => jax.formatError(err)
  66. };
  67. /**
  68. * The FindTeX instance used for locating TeX in strings
  69. */
  70. protected findTeX: FindTeX<N, T, D>;
  71. /**
  72. * The configuration of the TeX jax.
  73. * @type {ParserConfiguration}
  74. */
  75. protected configuration: ParserConfiguration;
  76. /**
  77. * The LaTeX code that is parsed.
  78. * @type {string}
  79. */
  80. protected latex: string;
  81. /**
  82. * The Math node that results from parsing.
  83. * @type {MmlNode}
  84. */
  85. protected mathNode: MmlNode;
  86. private _parseOptions: ParseOptions;
  87. /**
  88. * Initialises the configurations.
  89. * @param {string[]} packages Names of packages.
  90. * @return {Configuration} The configuration object.
  91. */
  92. protected static configure(packages: (string | [string, number])[]): ParserConfiguration {
  93. let configuration = new ParserConfiguration(packages, ['tex']);
  94. configuration.init();
  95. return configuration;
  96. }
  97. /**
  98. * Initialises the Tags factory. Add tagging structures from packages and set
  99. * tagging to given default.
  100. * @param {ParseOptions} options The parse options.
  101. * @param {Configuration} configuration The configuration.
  102. */
  103. protected static tags(options: ParseOptions, configuration: ParserConfiguration) {
  104. TagsFactory.addTags(configuration.tags);
  105. TagsFactory.setDefault(options.options.tags);
  106. options.tags = TagsFactory.getDefault();
  107. options.tags.configuration = options;
  108. }
  109. /**
  110. * @override
  111. */
  112. constructor(options: OptionList = {}) {
  113. const [rest, tex, find] = separateOptions(options, TeX.OPTIONS, FindTeX.OPTIONS);
  114. super(tex);
  115. this.findTeX = this.options['FindTeX'] || new FindTeX(find);
  116. const packages = this.options.packages;
  117. const configuration = this.configuration = TeX.configure(packages);
  118. const parseOptions = this._parseOptions =
  119. new ParseOptions(configuration, [this.options, TagsFactory.OPTIONS]);
  120. userOptions(parseOptions.options, rest);
  121. configuration.config(this);
  122. TeX.tags(parseOptions, configuration);
  123. this.postFilters.add(FilterUtil.cleanSubSup, -6);
  124. this.postFilters.add(FilterUtil.setInherited, -5);
  125. this.postFilters.add(FilterUtil.moveLimits, -4);
  126. this.postFilters.add(FilterUtil.cleanStretchy, -3);
  127. this.postFilters.add(FilterUtil.cleanAttributes, -2);
  128. this.postFilters.add(FilterUtil.combineRelations, -1);
  129. }
  130. /**
  131. * @override
  132. */
  133. public setMmlFactory(mmlFactory: MmlFactory) {
  134. super.setMmlFactory(mmlFactory);
  135. this._parseOptions.nodeFactory.setMmlFactory(mmlFactory);
  136. }
  137. /**
  138. * @return {ParseOptions} The parse options that configure this JaX instance.
  139. */
  140. public get parseOptions(): ParseOptions {
  141. return this._parseOptions;
  142. }
  143. /**
  144. * @override
  145. */
  146. public reset(tag: number = 0) {
  147. this.parseOptions.tags.reset(tag);
  148. }
  149. /**
  150. * @override
  151. */
  152. public compile(math: MathItem<N, T, D>, document: MathDocument<N, T, D>): MmlNode {
  153. this.parseOptions.clear();
  154. this.executeFilters(this.preFilters, math, document, this.parseOptions);
  155. let display = math.display;
  156. this.latex = math.math;
  157. let node: MmlNode;
  158. this.parseOptions.tags.startEquation(math);
  159. let globalEnv;
  160. try {
  161. let parser = new TexParser(this.latex,
  162. {display: display, isInner: false},
  163. this.parseOptions);
  164. node = parser.mml();
  165. globalEnv = parser.stack.global;
  166. } catch (err) {
  167. if (!(err instanceof TexError)) {
  168. throw err;
  169. }
  170. this.parseOptions.error = true;
  171. node = this.options.formatError(this, err);
  172. }
  173. node = this.parseOptions.nodeFactory.create('node', 'math', [node]);
  174. if (globalEnv?.indentalign) {
  175. NodeUtil.setAttribute(node, 'indentalign', globalEnv.indentalign);
  176. }
  177. if (display) {
  178. NodeUtil.setAttribute(node, 'display', 'block');
  179. }
  180. this.parseOptions.tags.finishEquation(math);
  181. this.parseOptions.root = node;
  182. this.executeFilters(this.postFilters, math, document, this.parseOptions);
  183. this.mathNode = this.parseOptions.root;
  184. return this.mathNode;
  185. }
  186. /**
  187. * @override
  188. */
  189. public findMath(strings: string[]) {
  190. return this.findTeX.findMath(strings);
  191. }
  192. /**
  193. * Default formatter for error messages:
  194. * wrap an error into a node for output.
  195. * @param {TeXError} err The TexError.
  196. * @return {Node} The merror node.
  197. */
  198. public formatError(err: TexError): MmlNode {
  199. let message = err.message.replace(/\n.*/, '');
  200. return this.parseOptions.nodeFactory.create(
  201. 'error', message, err.id, this.latex);
  202. }
  203. }