AmsMethods.ts 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. /*************************************************************
  2. *
  3. * Copyright (c) 2018-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 The AMS Parse methods.
  19. *
  20. * @author v.sorge@mathjax.org (Volker Sorge)
  21. */
  22. import {StackItem} from '../StackItem.js';
  23. import {ParseMethod} from '../Types.js';
  24. import ParseUtil from '../ParseUtil.js';
  25. import ParseMethods from '../ParseMethods.js';
  26. import NodeUtil from '../NodeUtil.js';
  27. import {TexConstant} from '../TexConstants.js';
  28. import TexParser from '../TexParser.js';
  29. import TexError from '../TexError.js';
  30. import {Macro} from '../Symbol.js';
  31. import {CommandMap} from '../SymbolMap.js';
  32. import {ArrayItem} from '../base/BaseItems.js';
  33. import {FlalignItem} from './AmsItems.js';
  34. import BaseMethods from '../base/BaseMethods.js';
  35. import {TEXCLASS} from '../../../core/MmlTree/MmlNode.js';
  36. import {MmlMunderover} from '../../../core/MmlTree/MmlNodes/munderover.js';
  37. import {MmlNode, AbstractMmlTokenNode} from '../../../core/MmlTree/MmlNode.js';
  38. // Namespace
  39. export const AmsMethods: Record<string, ParseMethod> = {};
  40. /**
  41. * Handle AMS array environments.
  42. * @param {TexParser} parser The calling parser.
  43. * @param {StackItem} begin The opening stackitem.
  44. * @param {boolean} numbered Environment numbered.
  45. * @param {boolean} taggable Environment taggable (e.g., align* is taggable,
  46. * split is not).
  47. * @param {string} align Column alignment.
  48. * @param {string} spacing Column spacing.
  49. * @param {string} style Display style indicator.
  50. */
  51. AmsMethods.AmsEqnArray = function(parser: TexParser, begin: StackItem,
  52. numbered: boolean, taggable: boolean,
  53. align: string, spacing: string,
  54. style: string) {
  55. // @test Aligned, Gathered
  56. const args = parser.GetBrackets('\\begin{' + begin.getName() + '}');
  57. const array = BaseMethods.EqnArray(parser, begin, numbered, taggable, align, spacing, style);
  58. return ParseUtil.setArrayAlign(array as ArrayItem, args);
  59. };
  60. /**
  61. * Handle AMS alignat environments.
  62. * @param {TexParser} parser The calling parser.
  63. * @param {StackItem} begin The opening stackitem.
  64. * @param {boolean} numbered Environment numbered.
  65. * @param {boolean} taggable Environment taggable (e.g., align* is taggable,
  66. * split is not).
  67. */
  68. AmsMethods.AlignAt = function(parser: TexParser, begin: StackItem,
  69. numbered: boolean, taggable: boolean) {
  70. const name = begin.getName();
  71. let n, valign, align = '', spacing = [];
  72. if (!taggable) {
  73. // @test Alignedat
  74. valign = parser.GetBrackets('\\begin{' + name + '}');
  75. }
  76. n = parser.GetArgument('\\begin{' + name + '}');
  77. if (n.match(/[^0-9]/)) {
  78. // @test PositiveIntegerArg
  79. throw new TexError('PositiveIntegerArg',
  80. 'Argument to %1 must me a positive integer',
  81. '\\begin{' + name + '}');
  82. }
  83. let count = parseInt(n, 10);
  84. while (count > 0) {
  85. align += 'rl';
  86. spacing.push('0em 0em');
  87. count--;
  88. }
  89. let spaceStr = spacing.join(' ');
  90. if (taggable) {
  91. // @test Alignat, Alignat Star
  92. return AmsMethods.EqnArray(parser, begin, numbered, taggable, align, spaceStr);
  93. }
  94. // @test Alignedat
  95. let array = AmsMethods.EqnArray(parser, begin, numbered, taggable, align, spaceStr);
  96. return ParseUtil.setArrayAlign(array as ArrayItem, valign);
  97. };
  98. /**
  99. * Implements multline environment (mostly handled through STACKITEM below)
  100. * @param {TexParser} parser The calling parser.
  101. * @param {StackItem} begin The opening stackitem.
  102. * @param {boolean} numbered Environment numbered.
  103. */
  104. AmsMethods.Multline = function (parser: TexParser, begin: StackItem, numbered: boolean) {
  105. // @test Shove*, Multline
  106. parser.Push(begin);
  107. ParseUtil.checkEqnEnv(parser);
  108. const item = parser.itemFactory.create('multline', numbered, parser.stack) as ArrayItem;
  109. item.arraydef = {
  110. displaystyle: true,
  111. rowspacing: '.5em',
  112. columnspacing: '100%',
  113. width: parser.options.ams['multlineWidth'],
  114. side: parser.options['tagSide'],
  115. minlabelspacing: parser.options['tagIndent'],
  116. framespacing: parser.options.ams['multlineIndent'] + ' 0',
  117. frame: '', // Use frame spacing with no actual frame
  118. 'data-width-includes-label': true // take label space out of 100% width
  119. };
  120. return item;
  121. };
  122. /**
  123. * Generate an align at environment.
  124. * @param {TexParser} parser The current TeX parser.
  125. * @param {StackItem} begin The begin stackitem.
  126. * @param {boolean} numbered Is this a numbered array.
  127. * @param {boolean} padded Is it padded.
  128. */
  129. AmsMethods.XalignAt = function(parser: TexParser, begin: StackItem,
  130. numbered: boolean, padded: boolean) {
  131. let n = parser.GetArgument('\\begin{' + begin.getName() + '}');
  132. if (n.match(/[^0-9]/)) {
  133. throw new TexError('PositiveIntegerArg',
  134. 'Argument to %1 must me a positive integer',
  135. '\\begin{' + begin.getName() + '}');
  136. }
  137. const align = (padded ? 'crl' : 'rlc');
  138. const width = (padded ? 'fit auto auto' : 'auto auto fit');
  139. const item = AmsMethods.FlalignArray(parser, begin, numbered, padded, false, align, width, true) as FlalignItem;
  140. item.setProperty('xalignat', 2 * parseInt(n));
  141. return item;
  142. };
  143. /**
  144. * Generate an flalign environment.
  145. * @param {TexParser} parser The current TeX parser.
  146. * @param {StackItem} begin The begin stackitem.
  147. * @param {boolean} numbered Is this a numbered array.
  148. * @param {boolean} padded Is it padded.
  149. * @param {boolean} center Is it centered.
  150. * @param {string} align The horizontal alignment for columns
  151. * @param {string} width The column widths of the table
  152. * @param {boolean} zeroWidthLabel True if the label should be in llap/rlap
  153. */
  154. AmsMethods.FlalignArray = function(parser: TexParser, begin: StackItem, numbered: boolean,
  155. padded: boolean, center: boolean, align: string,
  156. width: string, zeroWidthLabel: boolean = false) {
  157. parser.Push(begin);
  158. ParseUtil.checkEqnEnv(parser);
  159. align = align
  160. .split('')
  161. .join(' ')
  162. .replace(/r/g, 'right')
  163. .replace(/l/g, 'left')
  164. .replace(/c/g, 'center');
  165. const item = parser.itemFactory.create(
  166. 'flalign', begin.getName(), numbered, padded, center, parser.stack) as FlalignItem;
  167. item.arraydef = {
  168. width: '100%',
  169. displaystyle: true,
  170. columnalign: align,
  171. columnspacing: '0em',
  172. columnwidth: width,
  173. rowspacing: '3pt',
  174. side: parser.options['tagSide'],
  175. minlabelspacing: (zeroWidthLabel ? '0' : parser.options['tagIndent']),
  176. 'data-width-includes-label': true,
  177. };
  178. item.setProperty('zeroWidthLabel', zeroWidthLabel);
  179. return item;
  180. };
  181. export const NEW_OPS = 'ams-declare-ops';
  182. /**
  183. * Handle DeclareMathOperator.
  184. * @param {TexParser} parser The calling parser.
  185. * @param {string} name The macro name.
  186. */
  187. AmsMethods.HandleDeclareOp = function (parser: TexParser, name: string) {
  188. let star = (parser.GetStar() ? '*' : '');
  189. let cs = ParseUtil.trimSpaces(parser.GetArgument(name));
  190. if (cs.charAt(0) === '\\') {
  191. cs = cs.substr(1);
  192. }
  193. let op = parser.GetArgument(name);
  194. (parser.configuration.handlers.retrieve(NEW_OPS) as CommandMap).
  195. add(cs, new Macro(cs, AmsMethods.Macro, [`\\operatorname${star}{${op}}`]));
  196. };
  197. /**
  198. * Handle operatorname.
  199. * @param {TexParser} parser The calling parser.
  200. * @param {string} name The macro name.
  201. */
  202. AmsMethods.HandleOperatorName = function(parser: TexParser, name: string) {
  203. // @test Operatorname
  204. const star = parser.GetStar();
  205. //
  206. // Parse the argument using operator letters and grouping multiple letters.
  207. //
  208. let op = ParseUtil.trimSpaces(parser.GetArgument(name));
  209. let mml = new TexParser(op, {
  210. ...parser.stack.env,
  211. font: TexConstant.Variant.NORMAL,
  212. multiLetterIdentifiers: /^[-*a-z]+/i as any,
  213. operatorLetters: true
  214. }, parser.configuration).mml();
  215. //
  216. // If we get something other than a single mi, wrap in a TeXAtom.
  217. //
  218. if (!mml.isKind('mi')) {
  219. mml = parser.create('node', 'TeXAtom', [mml]);
  220. }
  221. //
  222. // Mark the limit properties and the TeX class.
  223. //
  224. NodeUtil.setProperties(mml, {movesupsub: star, movablelimits: true, texClass: TEXCLASS.OP});
  225. //
  226. // Skip a following \limits macro if not a starred operator
  227. //
  228. if (!star) {
  229. const c = parser.GetNext(), i = parser.i;
  230. if (c === '\\' && ++parser.i && parser.GetCS() !== 'limits') {
  231. parser.i = i;
  232. }
  233. }
  234. //
  235. parser.Push(mml);
  236. };
  237. /**
  238. * Handle sideset.
  239. * @param {TexParser} parser The calling parser.
  240. * @param {string} name The macro name.
  241. */
  242. AmsMethods.SideSet = function (parser: TexParser, name: string) {
  243. //
  244. // Get the pre- and post-scripts, and any extra material from the arguments
  245. //
  246. const [preScripts, preRest] = splitSideSet(parser.ParseArg(name));
  247. const [postScripts, postRest] = splitSideSet(parser.ParseArg(name));
  248. const base = parser.ParseArg(name);
  249. let mml = base;
  250. //
  251. // If there are pre-scripts...
  252. //
  253. if (preScripts) {
  254. //
  255. // If there is other material...
  256. //
  257. if (preRest) {
  258. //
  259. // Replace the empty base of the prescripts with a phantom element of the
  260. // original base, with width 0 (but still of the correct height and depth).
  261. // so the scripts will be at the right heights.
  262. //
  263. preScripts.replaceChild(
  264. parser.create('node', 'mphantom', [
  265. parser.create('node', 'mpadded', [ParseUtil.copyNode(base, parser)], {width: 0})
  266. ]),
  267. NodeUtil.getChildAt(preScripts, 0)
  268. );
  269. } else {
  270. //
  271. // If there is no extra meterial, make a mmultiscripts element
  272. //
  273. mml = parser.create('node', 'mmultiscripts', [base]);
  274. //
  275. // Add any postscripts
  276. //
  277. if (postScripts) {
  278. NodeUtil.appendChildren(mml, [
  279. NodeUtil.getChildAt(postScripts, 1) || parser.create('node', 'none'),
  280. NodeUtil.getChildAt(postScripts, 2) || parser.create('node', 'none')
  281. ]);
  282. }
  283. //
  284. // Add the prescripts (left aligned)
  285. //
  286. NodeUtil.setProperty(mml, 'scriptalign', 'left');
  287. NodeUtil.appendChildren(mml, [
  288. parser.create('node', 'mprescripts'),
  289. NodeUtil.getChildAt(preScripts, 1) || parser.create('node', 'none'),
  290. NodeUtil.getChildAt(preScripts, 2) || parser.create('node', 'none')
  291. ]);
  292. }
  293. }
  294. //
  295. // If there are postscripts and we didn't make a mmultiscript element above...
  296. //
  297. if (postScripts && mml === base) {
  298. //
  299. // Replace the emtpy base with actual base, and use that as the mml
  300. //
  301. postScripts.replaceChild(base, NodeUtil.getChildAt(postScripts, 0));
  302. mml = postScripts;
  303. }
  304. //
  305. // Put the needed pieces into a TeXAtom of class OP.
  306. // Note that the postScripts are in the mml element,
  307. // either as part of the mmultiscripts node, or the
  308. // msubsup with the base inserted into it.
  309. //
  310. const mrow = parser.create('node', 'TeXAtom', [], {texClass: TEXCLASS.OP, movesupsub: true, movablelimits: true});
  311. if (preRest) {
  312. preScripts && mrow.appendChild(preScripts);
  313. mrow.appendChild(preRest);
  314. }
  315. mrow.appendChild(mml);
  316. postRest && mrow.appendChild(postRest);
  317. parser.Push(mrow);
  318. };
  319. /**
  320. * Utility for breaking the \sideset scripts from any other material.
  321. * @param {MmlNode} mml The node to check.
  322. * @return {[MmlNode, MmlNode]} The msubsup with the scripts together with any extra nodes.
  323. */
  324. function splitSideSet(mml: MmlNode): [MmlNode, MmlNode] {
  325. if (!mml || (mml.isInferred && mml.childNodes.length === 0)) return [null, null];
  326. if (mml.isKind('msubsup') && checkSideSetBase(mml)) return [mml, null];
  327. const child = NodeUtil.getChildAt(mml, 0);
  328. if (!(mml.isInferred && child && checkSideSetBase(child))) return [null, mml];
  329. mml.childNodes.splice(0, 1); // remove first child
  330. return [child, mml];
  331. }
  332. /**
  333. * Utility for checking if a \sideset argument has scripts with an empty base.
  334. * @param {MmlNode} mml The node to check.
  335. * @return {boolean} True if the base is not and empty mi element.
  336. */
  337. function checkSideSetBase(mml: MmlNode): boolean {
  338. const base = mml.childNodes[0];
  339. return base && base.isKind('mi') && (base as AbstractMmlTokenNode).getText() === '';
  340. }
  341. /**
  342. * Handle extra letters in \operatorname (- and *), default to normal otherwise.
  343. * @param {TexParser} parser The calling parser.
  344. * @param {string} c The letter being checked
  345. */
  346. AmsMethods.operatorLetter = function (parser: TexParser, c: string) {
  347. return parser.stack.env.operatorLetters ? ParseMethods.variable(parser, c) : false;
  348. };
  349. /**
  350. * Handle multi integral signs.
  351. * @param {TexParser} parser The calling parser.
  352. * @param {string} name The macro name.
  353. * @param {string} integral The actual integral sign.
  354. */
  355. AmsMethods.MultiIntegral = function(parser: TexParser, name: string,
  356. integral: string) {
  357. let next = parser.GetNext();
  358. if (next === '\\') {
  359. // @test MultiInt with Command
  360. let i = parser.i;
  361. next = parser.GetArgument(name);
  362. parser.i = i;
  363. if (next === '\\limits') {
  364. if (name === '\\idotsint') {
  365. // @test MultiInt with Limits
  366. integral = '\\!\\!\\mathop{\\,\\,' + integral + '}';
  367. }
  368. else {
  369. // Question: This is not used anymore?
  370. integral = '\\!\\!\\!\\mathop{\\,\\,\\,' + integral + '}';
  371. }
  372. }
  373. }
  374. // @test MultiInt, MultiInt in Context
  375. parser.string = integral + ' ' + parser.string.slice(parser.i);
  376. parser.i = 0;
  377. };
  378. /**
  379. * Handle stretchable arrows.
  380. * @param {TexParser} parser The calling parser.
  381. * @param {string} name The macro name.
  382. * @param {number} chr The arrow character in hex code.
  383. * @param {number} l Left width.
  384. * @param {number} r Right width.
  385. */
  386. AmsMethods.xArrow = function(parser: TexParser, name: string,
  387. chr: number, l: number, r: number) {
  388. let def = {width: '+' + ParseUtil.Em((l + r) / 18), lspace: ParseUtil.Em(l / 18)};
  389. let bot = parser.GetBrackets(name);
  390. let first = parser.ParseArg(name);
  391. let dstrut = parser.create('node', 'mspace', [], {depth: '.25em'});
  392. let arrow = parser.create('token',
  393. 'mo', {stretchy: true, texClass: TEXCLASS.REL}, String.fromCodePoint(chr));
  394. arrow = parser.create('node', 'mstyle', [arrow], {scriptlevel: 0});
  395. let mml = parser.create('node', 'munderover', [arrow]) as MmlMunderover;
  396. let mpadded = parser.create('node', 'mpadded', [first, dstrut], def);
  397. NodeUtil.setAttribute(mpadded, 'voffset', '-.2em');
  398. NodeUtil.setAttribute(mpadded, 'height', '-.2em');
  399. NodeUtil.setChild(mml, mml.over, mpadded);
  400. if (bot) {
  401. // @test Above Below Left Arrow, Above Below Right Arrow
  402. let bottom = new TexParser(bot, parser.stack.env, parser.configuration).mml();
  403. let bstrut = parser.create('node', 'mspace', [], {height: '.75em'});
  404. mpadded = parser.create('node', 'mpadded', [bottom, bstrut], def);
  405. NodeUtil.setAttribute(mpadded, 'voffset', '.15em');
  406. NodeUtil.setAttribute(mpadded, 'depth', '-.15em');
  407. NodeUtil.setChild(mml, mml.under, mpadded);
  408. }
  409. // @test Above Left Arrow, Above Right Arrow, Above Left Arrow in Context,
  410. // Above Right Arrow in Context
  411. NodeUtil.setProperty(mml, 'subsupOK', true);
  412. parser.Push(mml);
  413. };
  414. /**
  415. * Record presence of \shoveleft and \shoveright
  416. * @param {TexParser} parser The calling parser.
  417. * @param {string} name The macro name.
  418. * @param {string} shove The shove value.
  419. */
  420. AmsMethods.HandleShove = function(parser: TexParser, _name: string,
  421. shove: string) {
  422. let top = parser.stack.Top();
  423. // @test Shove (Left|Right) (Top|Middle|Bottom)
  424. if (top.kind !== 'multline') {
  425. // @test Shove Error Environment
  426. throw new TexError('CommandOnlyAllowedInEnv',
  427. '%1 only allowed in %2 environment',
  428. parser.currentCS, 'multline');
  429. }
  430. if (top.Size()) {
  431. // @test Shove Error (Top|Middle|Bottom)
  432. throw new TexError('CommandAtTheBeginingOfLine',
  433. '%1 must come at the beginning of the line', parser.currentCS);
  434. }
  435. top.setProperty('shove', shove);
  436. };
  437. /**
  438. * Handle \cfrac
  439. * @param {TexParser} parser The calling parser.
  440. * @param {string} name The macro name.
  441. */
  442. AmsMethods.CFrac = function(parser: TexParser, name: string) {
  443. let lr = ParseUtil.trimSpaces(parser.GetBrackets(name, ''));
  444. let num = parser.GetArgument(name);
  445. let den = parser.GetArgument(name);
  446. let lrMap: {[key: string]: string} = {
  447. l: TexConstant.Align.LEFT, r: TexConstant.Align.RIGHT, '': ''};
  448. let numNode = new TexParser('\\strut\\textstyle{' + num + '}',
  449. parser.stack.env, parser.configuration).mml();
  450. let denNode = new TexParser('\\strut\\textstyle{' + den + '}',
  451. parser.stack.env, parser.configuration).mml();
  452. let frac = parser.create('node', 'mfrac', [numNode, denNode]);
  453. lr = lrMap[lr];
  454. if (lr == null) {
  455. // @test Center Fraction Error
  456. throw new TexError('IllegalAlign', 'Illegal alignment specified in %1', parser.currentCS);
  457. }
  458. if (lr) {
  459. // @test Right Fraction, Left Fraction
  460. NodeUtil.setProperties(frac, {numalign: lr, denomalign: lr});
  461. }
  462. // @test Center Fraction
  463. parser.Push(frac);
  464. };
  465. /**
  466. * Implement AMS generalized fraction.
  467. * @param {TexParser} parser The calling parser.
  468. * @param {string} name The macro name.
  469. * @param {string} left Left delimiter.
  470. * @param {string} right Right delimiter.
  471. * @param {string} thick Line thickness.
  472. * @param {string} style Math style.
  473. */
  474. AmsMethods.Genfrac = function(parser: TexParser, name: string, left: string,
  475. right: string, thick: string, style: string) {
  476. if (left == null) { // @test Genfrac
  477. left = parser.GetDelimiterArg(name);
  478. }
  479. if (right == null) { // @test Genfrac
  480. right = parser.GetDelimiterArg(name);
  481. }
  482. if (thick == null) { // @test Genfrac
  483. thick = parser.GetArgument(name);
  484. }
  485. if (style == null) { // @test Genfrac
  486. style = ParseUtil.trimSpaces(parser.GetArgument(name));
  487. }
  488. let num = parser.ParseArg(name);
  489. let den = parser.ParseArg(name);
  490. let frac = parser.create('node', 'mfrac', [num, den]);
  491. if (thick !== '') {
  492. // @test Normal Binomial, Text Binomial, Display Binomial
  493. NodeUtil.setAttribute(frac, 'linethickness', thick);
  494. }
  495. if (left || right) {
  496. // @test Normal Binomial, Text Binomial, Display Binomial
  497. NodeUtil.setProperty(frac, 'withDelims', true);
  498. frac = ParseUtil.fixedFence(parser.configuration, left, frac, right);
  499. }
  500. if (style !== '') {
  501. let styleDigit = parseInt(style, 10);
  502. let styleAlpha = ['D', 'T', 'S', 'SS'][styleDigit];
  503. if (styleAlpha == null) {
  504. // @test Genfrac Error
  505. throw new TexError('BadMathStyleFor', 'Bad math style for %1', parser.currentCS);
  506. }
  507. frac = parser.create('node', 'mstyle', [frac]);
  508. if (styleAlpha === 'D') {
  509. // @test Display Fraction, Display Sub Fraction, Display Binomial,
  510. // Display Sub Binomial
  511. NodeUtil.setProperties(frac, {displaystyle: true, scriptlevel: 0});
  512. }
  513. else {
  514. // @test Text Fraction, Text Sub Fraction, Text Binomial,
  515. // Text Sub Binomial
  516. NodeUtil.setProperties(frac, {displaystyle: false,
  517. scriptlevel: styleDigit - 1});
  518. }
  519. }
  520. // @test Text Fraction, Normal Sub Binomial, Normal Binomial
  521. parser.Push(frac);
  522. };
  523. /**
  524. * Add the tag to the environment (to be added to the table row later).
  525. * @param {TexParser} parser The calling parser.
  526. * @param {string} name The macro name.
  527. */
  528. AmsMethods.HandleTag = function(parser: TexParser, name: string) {
  529. if (!parser.tags.currentTag.taggable && parser.tags.env) {
  530. // @test Illegal Tag Error
  531. throw new TexError('CommandNotAllowedInEnv',
  532. '%1 not allowed in %2 environment',
  533. parser.currentCS, parser.tags.env);
  534. }
  535. if (parser.tags.currentTag.tag) {
  536. // @test Double Tag Error
  537. throw new TexError('MultipleCommand', 'Multiple %1', parser.currentCS);
  538. }
  539. let star = parser.GetStar();
  540. let tagId = ParseUtil.trimSpaces(parser.GetArgument(name));
  541. parser.tags.tag(tagId, star);
  542. };
  543. AmsMethods.HandleNoTag = BaseMethods.HandleNoTag;
  544. AmsMethods.HandleRef = BaseMethods.HandleRef;
  545. AmsMethods.Macro = BaseMethods.Macro;
  546. AmsMethods.Accent = BaseMethods.Accent;
  547. AmsMethods.Tilde = BaseMethods.Tilde;
  548. AmsMethods.Array = BaseMethods.Array;
  549. AmsMethods.Spacer = BaseMethods.Spacer;
  550. AmsMethods.NamedOp = BaseMethods.NamedOp;
  551. AmsMethods.EqnArray = BaseMethods.EqnArray;
  552. AmsMethods.Equation = BaseMethods.Equation;