@vincenttam/showdown-katex
Version:
Showdown extension that adds LaTeX, ASCIImath and mhchem support
2,291 lines (2,248 loc) • 53.7 kB
JavaScript
/**!
* @preserve
Based on ASCIIMathTeXImg.js but now part of https://github.com/obedm503/showdown-katex
Based on ASCIIMathML, Version 1.4.7 Aug 30, 2005, (c) Peter Jipsen http://www.chapman.edu/~jipsen
Modified with TeX conversion for IMG rendering Sept 6, 2006 (c) David Lippman http://www.pierce.ctc.edu/dlippman
Updated to match ver 2.2 Mar 3, 2014
Latest at https://github.com/mathjax/asciimathml
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the 'Software'), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// @ts-check
// token types
const tokens = {
CONST: 0,
UNARY: 1,
BINARY: 2,
INFIX: 3,
LEFTBRACKET: 4,
RIGHTBRACKET: 5,
SPACE: 6,
UNDEROVER: 7,
DEFINITION: 8,
LEFTRIGHT: 9,
TEXT: 10,
};
/**
* @typedef Symbol
* @property {string} input
* @property {string} tag
* @property {string} [output]
* @property {string} [tex]
* @property {number} [tType]
* @property {boolean} [val]
* @property {boolean} [noTexCopy]
* @property {boolean} [invisible]
* @property {boolean} [func]
* @property {string[]} [rewriteLeftRight]
* @property {boolean} [acc]
* @property {string} [atName]
* @property {string} [atVal]
*/
/** @type {Symbol} */
const quoteSymbol = {
input: "'",
tag: 'mtext',
output: 'mbox',
tex: null,
tType: tokens.TEXT,
};
/** @type {Array<Symbol>} */
const symbols = [
// some greek symbols
{
input: 'alpha',
tag: 'mi',
output: '\u03B1',
tex: null,
tType: tokens.CONST,
},
{
input: 'beta',
tag: 'mi',
output: '\u03B2',
tex: null,
tType: tokens.CONST,
},
{ input: 'chi', tag: 'mi', output: '\u03C7', tex: null, tType: tokens.CONST },
{
input: 'delta',
tag: 'mi',
output: '\u03B4',
tex: null,
tType: tokens.CONST,
},
{
input: 'Delta',
tag: 'mo',
output: '\u0394',
tex: null,
tType: tokens.CONST,
},
{
input: 'epsi',
tag: 'mi',
output: '\u03B5',
tex: 'epsilon',
tType: tokens.CONST,
},
{
input: 'varepsilon',
tag: 'mi',
output: '\u025B',
tex: null,
tType: tokens.CONST,
},
{ input: 'eta', tag: 'mi', output: '\u03B7', tex: null, tType: tokens.CONST },
{
input: 'gamma',
tag: 'mi',
output: '\u03B3',
tex: null,
tType: tokens.CONST,
},
{
input: 'Gamma',
tag: 'mo',
output: '\u0393',
tex: null,
tType: tokens.CONST,
},
{
input: 'iota',
tag: 'mi',
output: '\u03B9',
tex: null,
tType: tokens.CONST,
},
{
input: 'kappa',
tag: 'mi',
output: '\u03BA',
tex: null,
tType: tokens.CONST,
},
{
input: 'lambda',
tag: 'mi',
output: '\u03BB',
tex: null,
tType: tokens.CONST,
},
{
input: 'Lambda',
tag: 'mo',
output: '\u039B',
tex: null,
tType: tokens.CONST,
},
{
input: 'lamda',
tag: 'mi',
output: 'lambda',
tex: null,
tType: tokens.DEFINITION,
},
{
input: 'Lamda',
tag: 'mi',
output: 'Lambda',
tex: null,
tType: tokens.DEFINITION,
},
{ input: 'mu', tag: 'mi', output: '\u03BC', tex: null, tType: tokens.CONST },
{ input: 'nu', tag: 'mi', output: '\u03BD', tex: null, tType: tokens.CONST },
{
input: 'omega',
tag: 'mi',
output: '\u03C9',
tex: null,
tType: tokens.CONST,
},
{
input: 'Omega',
tag: 'mo',
output: '\u03A9',
tex: null,
tType: tokens.CONST,
},
{ input: 'phi', tag: 'mi', output: '\u03C6', tex: null, tType: tokens.CONST },
{
input: 'varphi',
tag: 'mi',
output: '\u03D5',
tex: null,
tType: tokens.CONST,
},
{ input: 'Phi', tag: 'mo', output: '\u03A6', tex: null, tType: tokens.CONST },
{ input: 'pi', tag: 'mi', output: '\u03C0', tex: null, tType: tokens.CONST },
{ input: 'Pi', tag: 'mo', output: '\u03A0', tex: null, tType: tokens.CONST },
{ input: 'psi', tag: 'mi', output: '\u03C8', tex: null, tType: tokens.CONST },
{ input: 'Psi', tag: 'mi', output: '\u03A8', tex: null, tType: tokens.CONST },
{ input: 'rho', tag: 'mi', output: '\u03C1', tex: null, tType: tokens.CONST },
{
input: 'sigma',
tag: 'mi',
output: '\u03C3',
tex: null,
tType: tokens.CONST,
},
{
input: 'Sigma',
tag: 'mo',
output: '\u03A3',
tex: null,
tType: tokens.CONST,
},
{ input: 'tau', tag: 'mi', output: '\u03C4', tex: null, tType: tokens.CONST },
{
input: 'theta',
tag: 'mi',
output: '\u03B8',
tex: null,
tType: tokens.CONST,
},
{
input: 'vartheta',
tag: 'mi',
output: '\u03D1',
tex: null,
tType: tokens.CONST,
},
{
input: 'Theta',
tag: 'mo',
output: '\u0398',
tex: null,
tType: tokens.CONST,
},
{
input: 'upsilon',
tag: 'mi',
output: '\u03C5',
tex: null,
tType: tokens.CONST,
},
{ input: 'xi', tag: 'mi', output: '\u03BE', tex: null, tType: tokens.CONST },
{ input: 'Xi', tag: 'mo', output: '\u039E', tex: null, tType: tokens.CONST },
{
input: 'zeta',
tag: 'mi',
output: '\u03B6',
tex: null,
tType: tokens.CONST,
},
// binary operation symbols
{ input: '*', tag: 'mo', output: '\u22C5', tex: 'cdot', tType: tokens.CONST },
{ input: '**', tag: 'mo', output: '\u2217', tex: 'ast', tType: tokens.CONST },
{
input: '***',
tag: 'mo',
output: '\u22C6',
tex: 'star',
tType: tokens.CONST,
},
{
input: '// ',
tag: 'mo',
output: '/',
tex: '/',
tType: tokens.CONST,
val: true,
noTexCopy: true,
},
{
input: '\\\\',
tag: 'mo',
output: '\\',
tex: 'backslash',
tType: tokens.CONST,
},
{
input: 'setminus',
tag: 'mo',
output: '\\',
tex: null,
tType: tokens.CONST,
},
{
input: 'xx',
tag: 'mo',
output: '\u00D7',
tex: 'times',
tType: tokens.CONST,
},
{
input: '|><',
tag: 'mo',
output: '\u22C9',
tex: 'ltimes',
tType: tokens.CONST,
},
{
input: '><|',
tag: 'mo',
output: '\u22CA',
tex: 'rtimes',
tType: tokens.CONST,
},
{
input: '|><|',
tag: 'mo',
output: '\u22C8',
tex: 'bowtie',
tType: tokens.CONST,
},
{ input: '-:', tag: 'mo', output: '\u00F7', tex: 'div', tType: tokens.CONST },
{
input: 'divide',
tag: 'mo',
output: '-:',
tex: null,
tType: tokens.DEFINITION,
},
{ input: '@', tag: 'mo', output: '\u2218', tex: 'circ', tType: tokens.CONST },
{
input: 'o+',
tag: 'mo',
output: '\u2295',
tex: 'oplus',
tType: tokens.CONST,
},
{
input: 'ox',
tag: 'mo',
output: '\u2297',
tex: 'otimes',
tType: tokens.CONST,
},
{
input: 'o.',
tag: 'mo',
output: '\u2299',
tex: 'odot',
tType: tokens.CONST,
},
{
input: 'sum',
tag: 'mo',
output: '\u2211',
tex: null,
tType: tokens.UNDEROVER,
},
{
input: 'prod',
tag: 'mo',
output: '\u220F',
tex: null,
tType: tokens.UNDEROVER,
},
{
input: '^^',
tag: 'mo',
output: '\u2227',
tex: 'wedge',
tType: tokens.CONST,
},
{
input: '^^^',
tag: 'mo',
output: '\u22C0',
tex: 'bigwedge',
tType: tokens.UNDEROVER,
},
{ input: 'vv', tag: 'mo', output: '\u2228', tex: 'vee', tType: tokens.CONST },
{
input: 'vvv',
tag: 'mo',
output: '\u22C1',
tex: 'bigvee',
tType: tokens.UNDEROVER,
},
{ input: 'nn', tag: 'mo', output: '\u2229', tex: 'cap', tType: tokens.CONST },
{
input: 'nnn',
tag: 'mo',
output: '\u22C2',
tex: 'bigcap',
tType: tokens.UNDEROVER,
},
{ input: 'uu', tag: 'mo', output: '\u222A', tex: 'cup', tType: tokens.CONST },
{
input: 'uuu',
tag: 'mo',
output: '\u22C3',
tex: 'bigcup',
tType: tokens.UNDEROVER,
},
{
input: 'overset',
tag: 'mover',
output: 'stackrel',
tex: null,
tType: tokens.BINARY,
},
{
input: 'underset',
tag: 'munder',
output: 'stackrel',
tex: null,
tType: tokens.BINARY,
},
// binary relation symbols
{ input: '!=', tag: 'mo', output: '\u2260', tex: 'ne', tType: tokens.CONST },
{ input: ':=', tag: 'mo', output: ':=', tex: null, tType: tokens.CONST },
{ input: 'lt', tag: 'mo', output: '<', tex: null, tType: tokens.CONST },
{ input: 'gt', tag: 'mo', output: '>', tex: null, tType: tokens.CONST },
{ input: '<=', tag: 'mo', output: '\u2264', tex: 'le', tType: tokens.CONST },
{
input: 'lt=',
tag: 'mo',
output: '\u2264',
tex: 'leq',
tType: tokens.CONST,
},
{
input: 'gt=',
tag: 'mo',
output: '\u2265',
tex: 'geq',
tType: tokens.CONST,
},
{ input: '>=', tag: 'mo', output: '\u2265', tex: 'ge', tType: tokens.CONST },
{
input: '-<',
tag: 'mo',
output: '\u227A',
tex: 'prec',
tType: tokens.CONST,
},
{ input: '-lt', tag: 'mo', output: '\u227A', tex: null, tType: tokens.CONST },
{
input: '>-',
tag: 'mo',
output: '\u227B',
tex: 'succ',
tType: tokens.CONST,
},
{
input: '-<=',
tag: 'mo',
output: '\u2AAF',
tex: 'preceq',
tType: tokens.CONST,
},
{
input: '>-=',
tag: 'mo',
output: '\u2AB0',
tex: 'succeq',
tType: tokens.CONST,
},
{ input: 'in', tag: 'mo', output: '\u2208', tex: null, tType: tokens.CONST },
{
input: '!in',
tag: 'mo',
output: '\u2209',
tex: 'notin',
tType: tokens.CONST,
},
{
input: 'sub',
tag: 'mo',
output: '\u2282',
tex: 'subset',
tType: tokens.CONST,
},
{
input: 'sup',
tag: 'mo',
output: '\u2283',
tex: 'supset',
tType: tokens.CONST,
},
{
input: 'sube',
tag: 'mo',
output: '\u2286',
tex: 'subseteq',
tType: tokens.CONST,
},
{
input: 'supe',
tag: 'mo',
output: '\u2287',
tex: 'supseteq',
tType: tokens.CONST,
},
{
input: '-=',
tag: 'mo',
output: '\u2261',
tex: 'equiv',
tType: tokens.CONST,
},
{
input: '~=',
tag: 'mo',
output: '\u2245',
tex: 'stackrel{\\sim}{=}',
tType: tokens.CONST,
}, // back hack b/c mimetex doesn't support /cong
{
input: 'cong',
tag: 'mo',
output: '~=',
tex: null,
tType: tokens.DEFINITION,
},
{
input: '~~',
tag: 'mo',
output: '\u2248',
tex: 'approx',
tType: tokens.CONST,
},
{
input: 'prop',
tag: 'mo',
output: '\u221D',
tex: 'propto',
tType: tokens.CONST,
},
// logical symbols
{ input: 'and', tag: 'mtext', output: 'and', tex: null, tType: tokens.SPACE },
{ input: 'or', tag: 'mtext', output: 'or', tex: null, tType: tokens.SPACE },
{
input: 'not',
tag: 'mo',
output: '\u00AC',
tex: 'neg',
tType: tokens.CONST,
},
{
input: '=>',
tag: 'mo',
output: '\u21D2',
tex: 'Rightarrow',
tType: tokens.CONST,
},
{
input: 'implies',
tag: 'mo',
output: '=>',
tex: null,
tType: tokens.DEFINITION,
},
{ input: 'if', tag: 'mo', output: 'if', tex: null, tType: tokens.SPACE },
{
input: '<=>',
tag: 'mo',
output: '\u21D4',
tex: 'Leftrightarrow',
tType: tokens.CONST,
},
{
input: 'iff',
tag: 'mo',
output: '<=>',
tex: null,
tType: tokens.DEFINITION,
},
{
input: 'AA',
tag: 'mo',
output: '\u2200',
tex: 'forall',
tType: tokens.CONST,
},
{
input: 'EE',
tag: 'mo',
output: '\u2203',
tex: 'exists',
tType: tokens.CONST,
},
{
input: '_|_',
tag: 'mo',
output: '\u22A5',
tex: 'bot',
tType: tokens.CONST,
},
{ input: 'TT', tag: 'mo', output: '\u22A4', tex: 'top', tType: tokens.CONST },
{
input: '|--',
tag: 'mo',
output: '\u22A2',
tex: 'vdash',
tType: tokens.CONST,
},
{
input: '|==',
tag: 'mo',
output: '\u22A8',
tex: 'models',
tType: tokens.CONST,
}, // mimetex doesn't support
// grouping brackets
{
input: '(',
tag: 'mo',
output: '(',
tex: null,
tType: tokens.LEFTBRACKET,
val: true,
},
{
input: ')',
tag: 'mo',
output: ')',
tex: null,
tType: tokens.RIGHTBRACKET,
val: true,
},
{
input: '[',
tag: 'mo',
output: '[',
tex: null,
tType: tokens.LEFTBRACKET,
val: true,
},
{
input: ']',
tag: 'mo',
output: ']',
tex: null,
tType: tokens.RIGHTBRACKET,
val: true,
},
{
input: '{',
tag: 'mo',
output: '{',
tex: 'lbrace',
tType: tokens.LEFTBRACKET,
},
{
input: '}',
tag: 'mo',
output: '}',
tex: 'rbrace',
tType: tokens.RIGHTBRACKET,
},
{
input: '|',
tag: 'mo',
output: '|',
tex: null,
tType: tokens.LEFTRIGHT,
val: true,
},
// {input:'||', tag:'mo', output:'||', tex:null, ttype:LEFTRIGHT},
{
input: '(:',
tag: 'mo',
output: '\u2329',
tex: 'langle',
tType: tokens.LEFTBRACKET,
},
{
input: ':)',
tag: 'mo',
output: '\u232A',
tex: 'rangle',
tType: tokens.RIGHTBRACKET,
},
{
input: '<<',
tag: 'mo',
output: '\u2329',
tex: 'langle',
tType: tokens.LEFTBRACKET,
},
{
input: '>>',
tag: 'mo',
output: '\u232A',
tex: 'rangle',
tType: tokens.RIGHTBRACKET,
},
{
input: '{:',
tag: 'mo',
output: '{:',
tex: null,
tType: tokens.LEFTBRACKET,
invisible: true,
},
{
input: ':}',
tag: 'mo',
output: ':}',
tex: null,
tType: tokens.RIGHTBRACKET,
invisible: true,
},
// miscellaneous symbols
{ input: 'int', tag: 'mo', output: '\u222B', tex: null, tType: tokens.CONST },
{
input: 'dx',
tag: 'mi',
output: '{:d x:}',
tex: null,
tType: tokens.DEFINITION,
},
{
input: 'dy',
tag: 'mi',
output: '{:d y:}',
tex: null,
tType: tokens.DEFINITION,
},
{
input: 'dz',
tag: 'mi',
output: '{:d z:}',
tex: null,
tType: tokens.DEFINITION,
},
{
input: 'dt',
tag: 'mi',
output: '{:d t:}',
tex: null,
tType: tokens.DEFINITION,
},
{
input: 'oint',
tag: 'mo',
output: '\u222E',
tex: null,
tType: tokens.CONST,
},
{
input: 'del',
tag: 'mo',
output: '\u2202',
tex: 'partial',
tType: tokens.CONST,
},
{
input: 'grad',
tag: 'mo',
output: '\u2207',
tex: 'nabla',
tType: tokens.CONST,
},
{ input: '+-', tag: 'mo', output: '\u00B1', tex: 'pm', tType: tokens.CONST },
{
input: 'O/',
tag: 'mo',
output: '\u2205',
tex: 'emptyset',
tType: tokens.CONST,
},
{
input: 'oo',
tag: 'mo',
output: '\u221E',
tex: 'infty',
tType: tokens.CONST,
},
{
input: 'aleph',
tag: 'mo',
output: '\u2135',
tex: null,
tType: tokens.CONST,
},
{ input: '...', tag: 'mo', output: '...', tex: 'ldots', tType: tokens.CONST },
{
input: ':.',
tag: 'mo',
output: '\u2234',
tex: 'therefore',
tType: tokens.CONST,
},
{
input: ":'",
tag: 'mo',
output: '\u2235',
tex: 'because',
tType: tokens.CONST,
},
{
input: '/_',
tag: 'mo',
output: '\u2220',
tex: 'angle',
tType: tokens.CONST,
},
{
input: '/_\\',
tag: 'mo',
output: '\u25B3',
tex: 'triangle',
tType: tokens.CONST,
},
{
input: '\\ ',
tag: 'mo',
output: '\u00A0',
tex: null,
tType: tokens.CONST,
val: true,
},
{
input: 'frown',
tag: 'mo',
output: '\u2322',
tex: null,
tType: tokens.CONST,
},
{
input: '%',
tag: 'mo',
output: '%',
tex: '%',
tType: tokens.CONST,
noTexCopy: true,
},
{
input: 'quad',
tag: 'mo',
output: '\u00A0\u00A0',
tex: null,
tType: tokens.CONST,
},
{
input: 'qquad',
tag: 'mo',
output: '\u00A0\u00A0\u00A0\u00A0',
tex: null,
tType: tokens.CONST,
},
{
input: 'cdots',
tag: 'mo',
output: '\u22EF',
tex: null,
tType: tokens.CONST,
},
{
input: 'vdots',
tag: 'mo',
output: '\u22EE',
tex: null,
tType: tokens.CONST,
},
{
input: 'ddots',
tag: 'mo',
output: '\u22F1',
tex: null,
tType: tokens.CONST,
},
{
input: 'diamond',
tag: 'mo',
output: '\u22C4',
tex: null,
tType: tokens.CONST,
},
{
input: 'square',
tag: 'mo',
output: '\u25A1',
tex: 'boxempty',
tType: tokens.CONST,
},
{
input: '|__',
tag: 'mo',
output: '\u230A',
tex: 'lfloor',
tType: tokens.CONST,
},
{
input: '__|',
tag: 'mo',
output: '\u230B',
tex: 'rfloor',
tType: tokens.CONST,
},
{
input: '|~',
tag: 'mo',
output: '\u2308',
tex: 'lceil',
tType: tokens.CONST,
},
{
input: 'lceiling',
tag: 'mo',
output: '|~',
tex: null,
tType: tokens.DEFINITION,
},
{
input: '~|',
tag: 'mo',
output: '\u2309',
tex: 'rceil',
tType: tokens.CONST,
},
{
input: 'rceiling',
tag: 'mo',
output: '~|',
tex: null,
tType: tokens.DEFINITION,
},
{
input: 'CC',
tag: 'mo',
output: '\u2102',
tex: 'mathbb{C}',
tType: tokens.CONST,
noTexCopy: true,
},
{
input: 'NN',
tag: 'mo',
output: '\u2115',
tex: 'mathbb{N}',
tType: tokens.CONST,
noTexCopy: true,
},
{
input: 'QQ',
tag: 'mo',
output: '\u211A',
tex: 'mathbb{Q}',
tType: tokens.CONST,
noTexCopy: true,
},
{
input: 'RR',
tag: 'mo',
output: '\u211D',
tex: 'mathbb{R}',
tType: tokens.CONST,
noTexCopy: true,
},
{
input: 'ZZ',
tag: 'mo',
output: '\u2124',
tex: 'mathbb{Z}',
tType: tokens.CONST,
noTexCopy: true,
},
{
input: 'f',
tag: 'mi',
output: 'f',
tex: null,
tType: tokens.UNARY,
func: true,
val: true,
},
{
input: 'g',
tag: 'mi',
output: 'g',
tex: null,
tType: tokens.UNARY,
func: true,
val: true,
},
{ input: "''", tag: 'mo', output: "''", tex: null, val: true },
{ input: "'''", tag: 'mo', output: "'''", tex: null, val: true },
{ input: "''''", tag: 'mo', output: "''''", tex: null, val: true },
// standard functions
{
input: 'lim',
tag: 'mo',
output: 'lim',
tex: null,
tType: tokens.UNDEROVER,
},
{
input: 'Lim',
tag: 'mo',
output: 'Lim',
tex: null,
tType: tokens.UNDEROVER,
},
{
input: 'sin',
tag: 'mo',
output: 'sin',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'cos',
tag: 'mo',
output: 'cos',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'tan',
tag: 'mo',
output: 'tan',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'arcsin',
tag: 'mo',
output: 'arcsin',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'arccos',
tag: 'mo',
output: 'arccos',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'arctan',
tag: 'mo',
output: 'arctan',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'sinh',
tag: 'mo',
output: 'sinh',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'cosh',
tag: 'mo',
output: 'cosh',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'tanh',
tag: 'mo',
output: 'tanh',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'cot',
tag: 'mo',
output: 'cot',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'coth',
tag: 'mo',
output: 'coth',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'sech',
tag: 'mo',
output: 'sech',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'csch',
tag: 'mo',
output: 'csch',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'sec',
tag: 'mo',
output: 'sec',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'csc',
tag: 'mo',
output: 'csc',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'log',
tag: 'mo',
output: 'log',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'ln',
tag: 'mo',
output: 'ln',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'abs',
tag: 'mo',
output: 'abs',
tex: null,
tType: tokens.UNARY,
noTexCopy: true,
rewriteLeftRight: ['|', '|'],
},
{
input: 'norm',
tag: 'mo',
output: 'norm',
tex: null,
tType: tokens.UNARY,
noTexCopy: true,
rewriteLeftRight: ['\\|', '\\|'],
},
{
input: 'floor',
tag: 'mo',
output: 'floor',
tex: null,
tType: tokens.UNARY,
noTexCopy: true,
rewriteLeftRight: ['\\lfloor', '\\rfloor'],
},
{
input: 'ceil',
tag: 'mo',
output: 'ceil',
tex: null,
tType: tokens.UNARY,
noTexCopy: true,
rewriteLeftRight: ['\\lceil', '\\rceil'],
},
{
input: 'Sin',
tag: 'mo',
output: 'sin',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'Cos',
tag: 'mo',
output: 'cos',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'Tan',
tag: 'mo',
output: 'tan',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'Arcsin',
tag: 'mo',
output: 'arcsin',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'Arccos',
tag: 'mo',
output: 'arccos',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'Arctan',
tag: 'mo',
output: 'arctan',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'Sinh',
tag: 'mo',
output: 'sinh',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'Sosh',
tag: 'mo',
output: 'cosh',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'Tanh',
tag: 'mo',
output: 'tanh',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'Cot',
tag: 'mo',
output: 'cot',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'Sec',
tag: 'mo',
output: 'sec',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'Csc',
tag: 'mo',
output: 'csc',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'Log',
tag: 'mo',
output: 'log',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'Ln',
tag: 'mo',
output: 'ln',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'Abs',
tag: 'mo',
output: 'abs',
tex: null,
tType: tokens.UNARY,
noTexCopy: true,
rewriteLeftRight: ['|', '|'],
},
{
input: 'det',
tag: 'mo',
output: 'det',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'exp',
tag: 'mo',
output: 'exp',
tex: null,
tType: tokens.UNARY,
func: true,
},
{ input: 'dim', tag: 'mo', output: 'dim', tex: null, tType: tokens.CONST },
{
input: 'mod',
tag: 'mo',
output: 'mod',
tex: 'text{mod}',
tType: tokens.CONST,
noTexCopy: true,
},
{
input: 'gcd',
tag: 'mo',
output: 'gcd',
tex: null,
tType: tokens.UNARY,
func: true,
},
{
input: 'lcm',
tag: 'mo',
output: 'lcm',
tex: 'text{lcm}',
tType: tokens.UNARY,
func: true,
noTexCopy: true,
},
{ input: 'lub', tag: 'mo', output: 'lub', tex: null, tType: tokens.CONST },
{ input: 'glb', tag: 'mo', output: 'glb', tex: null, tType: tokens.CONST },
{
input: 'min',
tag: 'mo',
output: 'min',
tex: null,
tType: tokens.UNDEROVER,
},
{
input: 'max',
tag: 'mo',
output: 'max',
tex: null,
tType: tokens.UNDEROVER,
},
// arrows
{
input: 'uarr',
tag: 'mo',
output: '\u2191',
tex: 'uparrow',
tType: tokens.CONST,
},
{
input: 'darr',
tag: 'mo',
output: '\u2193',
tex: 'downarrow',
tType: tokens.CONST,
},
{
input: 'rarr',
tag: 'mo',
output: '\u2192',
tex: 'rightarrow',
tType: tokens.CONST,
},
{ input: '->', tag: 'mo', output: '\u2192', tex: 'to', tType: tokens.CONST },
{
input: '>->',
tag: 'mo',
output: '\u21A3',
tex: 'rightarrowtail',
tType: tokens.CONST,
},
{
input: '->>',
tag: 'mo',
output: '\u21A0',
tex: 'twoheadrightarrow',
tType: tokens.CONST,
},
{
input: '>->>',
tag: 'mo',
output: '\u2916',
tex: 'twoheadrightarrowtail',
tType: tokens.CONST,
},
{
input: '|->',
tag: 'mo',
output: '\u21A6',
tex: 'mapsto',
tType: tokens.CONST,
},
{
input: 'larr',
tag: 'mo',
output: '\u2190',
tex: 'leftarrow',
tType: tokens.CONST,
},
{
input: 'harr',
tag: 'mo',
output: '\u2194',
tex: 'leftrightarrow',
tType: tokens.CONST,
},
{
input: 'rArr',
tag: 'mo',
output: '\u21D2',
tex: 'Rightarrow',
tType: tokens.CONST,
},
{
input: 'lArr',
tag: 'mo',
output: '\u21D0',
tex: 'Leftarrow',
tType: tokens.CONST,
},
{
input: 'hArr',
tag: 'mo',
output: '\u21D4',
tex: 'Leftrightarrow',
tType: tokens.CONST,
},
// commands with argument
{
input: 'sqrt',
tag: 'msqrt',
output: 'sqrt',
tex: null,
tType: tokens.UNARY,
},
{
input: 'root',
tag: 'mroot',
output: 'root',
tex: null,
tType: tokens.BINARY,
},
{ input: 'frac', tag: 'mfrac', output: '/', tex: null, tType: tokens.BINARY },
{ input: '/', tag: 'mfrac', output: '/', tex: null, tType: tokens.INFIX },
{
input: 'stackrel',
tag: 'mover',
output: 'stackrel',
tex: null,
tType: tokens.BINARY,
},
{ input: '_', tag: 'msub', output: '_', tex: null, tType: tokens.INFIX },
{ input: '^', tag: 'msup', output: '^', tex: null, tType: tokens.INFIX },
{
input: 'cancel',
tag: 'menclose',
output: 'cancel',
tex: null,
tType: tokens.UNARY,
},
{
input: 'Sqrt',
tag: 'msqrt',
output: 'sqrt',
tex: null,
tType: tokens.UNARY,
},
{
input: 'hat',
tag: 'mover',
output: '\u005E',
tex: null,
tType: tokens.UNARY,
acc: true,
},
{
input: 'bar',
tag: 'mover',
output: '\u00AF',
tex: 'overline',
tType: tokens.UNARY,
acc: true,
},
{
input: 'vec',
tag: 'mover',
output: '\u2192',
tex: null,
tType: tokens.UNARY,
acc: true,
},
{
input: 'tilde',
tag: 'mover',
output: '~',
tex: null,
tType: tokens.UNARY,
acc: true,
},
{
input: 'dot',
tag: 'mover',
output: '.',
tex: null,
tType: tokens.UNARY,
acc: true,
},
{
input: 'ddot',
tag: 'mover',
output: '..',
tex: null,
tType: tokens.UNARY,
acc: true,
},
{
input: 'ul',
tag: 'munder',
output: '\u0332',
tex: 'underline',
tType: tokens.UNARY,
acc: true,
},
{
input: 'ubrace',
tag: 'munder',
output: '\u23DF',
tex: 'underbrace',
tType: tokens.UNARY,
acc: true,
},
{
input: 'obrace',
tag: 'mover',
output: '\u23DE',
tex: 'overbrace',
tType: tokens.UNARY,
acc: true,
},
{
input: 'text',
tag: 'mtext',
output: 'text',
tex: null,
tType: tokens.TEXT,
},
{
input: 'mbox',
tag: 'mtext',
output: 'mbox',
tex: null,
tType: tokens.TEXT,
},
quoteSymbol,
// { input: 'var', tag: 'mstyle', atname: 'fontstyle', atval: 'italic', output: 'var', tex: null, ttype: tokens.UNARY },
{ input: 'color', tag: 'mstyle', tType: tokens.BINARY },
{
input: 'bb',
tag: 'mstyle',
atName: 'mathvariant',
atVal: 'bold',
output: 'bb',
tex: 'mathbf',
tType: tokens.UNARY,
noTexCopy: true,
},
{
input: 'mathbf',
tag: 'mstyle',
atName: 'mathvariant',
atVal: 'bold',
output: 'mathbf',
tex: null,
tType: tokens.UNARY,
},
{
input: 'sf',
tag: 'mstyle',
atName: 'mathvariant',
atVal: 'sans-serif',
output: 'sf',
tex: 'mathsf',
tType: tokens.UNARY,
noTexCopy: true,
},
{
input: 'mathsf',
tag: 'mstyle',
atName: 'mathvariant',
atVal: 'sans-serif',
output: 'mathsf',
tex: null,
tType: tokens.UNARY,
},
{
input: 'bbb',
tag: 'mstyle',
atName: 'mathvariant',
atVal: 'double-struck',
output: 'bbb',
tex: 'mathbb',
tType: tokens.UNARY,
noTexCopy: true,
},
{
input: 'mathbb',
tag: 'mstyle',
atName: 'mathvariant',
atVal: 'double-struck',
output: 'mathbb',
tex: null,
tType: tokens.UNARY,
},
{
input: 'cc',
tag: 'mstyle',
atName: 'mathvariant',
atVal: 'script',
output: 'cc',
tex: 'mathcal',
tType: tokens.UNARY,
noTexCopy: true,
},
{
input: 'mathcal',
tag: 'mstyle',
atName: 'mathvariant',
atVal: 'script',
output: 'mathcal',
tex: null,
tType: tokens.UNARY,
},
{
input: 'tt',
tag: 'mstyle',
atName: 'mathvariant',
atVal: 'monospace',
output: 'tt',
tex: 'mathtt',
tType: tokens.UNARY,
noTexCopy: true,
},
{
input: 'mathtt',
tag: 'mstyle',
atName: 'mathvariant',
atVal: 'monospace',
output: 'mathtt',
tex: null,
tType: tokens.UNARY,
},
{
input: 'fr',
tag: 'mstyle',
atName: 'mathvariant',
atVal: 'fraktur',
output: 'fr',
tex: 'mathfrak',
tType: tokens.UNARY,
noTexCopy: true,
},
{
input: 'mathfrak',
tag: 'mstyle',
atName: 'mathvariant',
atVal: 'fraktur',
output: 'mathfrak',
tex: null,
tType: tokens.UNARY,
},
];
/** @type {Array<string>} */
let inputSymbols = [];
{
/** @type {Array<Symbol>} */
const otherSymbols = symbols
.filter(item => item.tex && item.noTexCopy !== true)
.map(item => ({
input: item.tex,
tag: item.tag,
output: item.output,
tType: item.tType,
acc: item.acc || false,
}));
symbols.push(...otherSymbols);
symbols.sort((s1, s2) => (s1.input > s2.input ? 1 : -1));
inputSymbols = symbols.map(item => item.input);
}
/**
* @param {string} str
* @param {number} n
* @returns {string}
*/
function removeCharsAndBlanks(str, n) {
// remove n characters and any following blanks
let st;
if (
str.charAt(n) === '\\' &&
str.charAt(n + 1) !== '\\' &&
str.charAt(n + 1) !== ' '
) {
st = str.slice(n + 1);
} else {
st = str.slice(n);
}
let i = 0;
while (i < st.length && st.charCodeAt(i) <= 32) {
i++;
}
return st.slice(i);
}
/**
* @param {string[]} arr
* @param {string} str
* @param {number} n
* @returns {number}
*/
function position(arr, str, n) {
// return position >=n where str appears or would be inserted
// assumes arr is sorted
if (n === 0) {
let len = arr.length;
n = -1;
while (n + 1 < len) {
let m = (n + len) >> 1;
if (arr[m] < str) {
n = m;
} else {
len = m;
}
}
return len;
}
let i = n;
while (i < arr.length && arr[i] < str) {
i++;
}
return i; // i=arr.length || arr[i]>=str
}
/**
* @param {string} str
* @returns {Symbol}
*/
function getSymbol(str) {
let previousSymbol;
let currentSymbol;
// return maximal initial substring of str that appears in names
// return null if there is none
let newPos = 0; // new pos
let oldPos = 0; // old pos
let matchPos; // match pos
let st;
let tagst;
let match = '';
let more = true;
for (let i = 1; i <= str.length && more; i++) {
st = str.slice(0, i); // initial substring of length i
oldPos = newPos;
newPos = position(inputSymbols, st, oldPos);
if (
newPos < inputSymbols.length &&
str.slice(0, inputSymbols[newPos].length) === inputSymbols[newPos]
) {
match = inputSymbols[newPos];
matchPos = newPos;
i = match.length;
}
more =
newPos < inputSymbols.length &&
str.slice(0, inputSymbols[newPos].length) >= inputSymbols[newPos];
}
previousSymbol = currentSymbol;
if (match !== '') {
currentSymbol = symbols[matchPos].tType;
return symbols[matchPos];
}
// if str[0] is a digit or - return maxsubstring of digits.digits
currentSymbol = tokens.CONST;
newPos = 1;
st = str.slice(0, 1);
let integ = true;
while ('0' <= st && st <= '9' && newPos <= str.length) {
st = str.slice(newPos, newPos + 1);
newPos++;
}
if (st === '.') {
st = str.slice(newPos, newPos + 1);
if ('0' <= st && st <= '9') {
integ = false;
newPos++;
while ('0' <= st && st <= '9' && newPos <= str.length) {
st = str.slice(newPos, newPos + 1);
newPos++;
}
}
}
if ((integ && newPos > 1) || newPos > 2) {
st = str.slice(0, newPos - 1);
tagst = 'mn';
} else {
newPos = 2;
st = str.slice(0, 1); // take 1 character
tagst = ('A' > st || st > 'Z') && ('a' > st || st > 'z') ? 'mo' : 'mi';
}
if (st === '-' && previousSymbol === tokens.INFIX) {
currentSymbol = tokens.INFIX;
return {
input: st,
tag: tagst,
output: st,
tType: tokens.UNARY,
func: true,
val: true,
};
}
// added val bit
return { input: st, tag: tagst, output: st, tType: tokens.CONST, val: true };
}
/**
* @param {string} node
* @returns {string}
*/
function removeBrackets(node) {
if (node.charAt(0) === '{' && node.charAt(node.length - 1) === '}') {
let leftchop = 0;
let st = node.substr(1, 5);
if (st === '\\left') {
st = node.charAt(6);
if (st === '(' || st === '[' || st === '{') {
leftchop = 7;
} else {
st = node.substr(6, 7);
if (st === '\\lbrace') {
leftchop = 13;
}
}
} else {
st = node.charAt(1);
if (st === '(' || st === '[') {
leftchop = 2;
}
}
if (leftchop > 0) {
// st = node.charAt(node.length-7);
st = node.substr(node.length - 8);
if (st === '\\right)}' || st === '\\right]}' || st === '\\right.}') {
node = '{' + node.substr(leftchop);
node = node.substr(0, node.length - 8) + '}';
} else if (st === '\\rbrace}') {
node = '{' + node.substr(leftchop);
node = node.substr(0, node.length - 14) + '}';
}
}
}
return node;
}
/*
Parsing ASCII math expressions with the following grammar
v ::= [A-Za-z] | greek letters | numbers | other constant symbols
u ::= sqrt | text | bb | other unary symbols for font commands
b ::= frac | root | stackrel binary symbols
l ::= ( | [ | { | (: | {: left brackets
r ::= ) | ] | } | :) | :} right brackets
S ::= v | lEr | uS | bSS Simple expression
I ::= S_S | S^S | S_S^S | S Intermediate expression
E ::= IE | I/I Expression
Each terminal symbol is translated into a corresponding mathml node.
*/
/**
* @param {Symbol} symb
* @returns {string}
*/
function getTeXsymbol(symb) {
let pre = '';
if (typeof symb.val === 'boolean' && symb.val) {
pre = '';
} else {
pre = '\\';
}
if (!symb.tex) {
// can't remember why this was here. Breaks /delta /Delta to removed
// return (pre + (pre == '' ? symb.input : symb.input.toLowerCase()));
return pre + symb.input;
} else {
return pre + symb.tex;
}
}
/**
* @param {string} str
* @param {number} depth
* @returns {[string, string]}
*/
function parseSexpr(str, depth) {
// parses str and returns [node,tailstr]
// const rightvert = false,
str = removeCharsAndBlanks(str, 0);
let symbol = getSymbol(str); // either a token or a bracket or empty
if (!symbol || (symbol.tType === tokens.RIGHTBRACKET && depth > 0)) {
return [null, str];
}
if (symbol.tType === tokens.DEFINITION) {
str = symbol.output + removeCharsAndBlanks(str, symbol.input.length);
symbol = getSymbol(str);
}
switch (symbol.tType) {
case tokens.UNDEROVER:
case tokens.CONST:
str = removeCharsAndBlanks(str, symbol.input.length);
var texsymbol = getTeXsymbol(symbol);
if (texsymbol.charAt(0) === '\\' || symbol.tag === 'mo') {
return [texsymbol, str];
} else {
return [`{${texsymbol}}`, str];
}
case tokens.LEFTBRACKET: {
// read (expr+)
depth++;
str = removeCharsAndBlanks(str, symbol.input.length);
const result = parseExpr(str, true, depth);
depth--;
var leftchop = 0;
if (result[0].substr(0, 6) === '\\right') {
st = result[0].charAt(6);
if (st === ')' || st === ']' || st === '}') {
leftchop = 6;
} else if (st === '.') {
leftchop = 7;
} else {
st = result[0].substr(6, 7);
if (st === '\\rbrace') {
leftchop = 13;
}
}
}
let node = '';
if (leftchop > 0) {
result[0] = result[0].substr(leftchop);
if (symbol.invisible) {
node = `{${result[0]}}`;
} else {
node = `{${getTeXsymbol(symbol)}${result[0]}}`;
}
} else {
if (symbol.invisible) {
node = `{\\left.${result[0]}}`;
} else {
node = `{\\left${getTeXsymbol(symbol)}${result[0]}}`;
}
}
return [node, result[1]];
}
case tokens.TEXT: {
if (symbol !== quoteSymbol) {
str = removeCharsAndBlanks(str, symbol.input.length);
}
/** @type {number} */
let i;
if (str.charAt(0) === '{') {
i = str.indexOf('}');
} else if (str.charAt(0) === '(') {
i = str.indexOf(')');
} else if (str.charAt(0) === '[') {
i = str.indexOf(']');
} else if (symbol === quoteSymbol) {
i = str.slice(1).indexOf("'") + 1;
} else {
i = 0;
}
if (i === -1) {
i = str.length;
}
st = str.slice(1, i);
let newFrag = '';
if (st.charAt(0) === ' ') {
newFrag = '\\ ';
}
newFrag += `\\text{${st}}`;
if (st.charAt(st.length - 1) === ' ') {
newFrag += '\\ ';
}
str = removeCharsAndBlanks(str, i + 1);
return [newFrag, str];
}
case tokens.UNARY: {
str = removeCharsAndBlanks(str, symbol.input.length);
const result = parseSexpr(str, depth);
if (result[0] === null) {
return [`{${getTeXsymbol(symbol)}}`, str];
}
if (symbol.func === true) {
// functions hack
st = str.charAt(0);
if (
st === '^' ||
st === '_' ||
st === '/' ||
st === '|' ||
st === ',' ||
(symbol.input.length === 1 && symbol.input.match(/\w/) && st !== '(')
) {
return [`{${getTeXsymbol(symbol)}}`, str];
} else {
const node = `{${getTeXsymbol(symbol)}{${result[0]}}}`;
return [node, result[1]];
}
}
result[0] = removeBrackets(result[0]);
if (symbol.input === 'sqrt') {
// sqrt
return [`\\sqrt{${result[0]}}`, result[1]];
} else if (symbol.input === 'cancel') {
// cancel
return [`\\cancel{${result[0]}}`, result[1]];
} else if (typeof symbol.rewriteLeftRight !== 'undefined') {
// abs, floor, ceil
return [
`{\\left${symbol.rewriteLeftRight[0]}${result[0]}\\right${
symbol.rewriteLeftRight[1]
}}`,
result[1],
];
} else if (symbol.acc === true) {
// accent
// return ['{'+getTeXsymbol(symbol)+'{'+result[0]+'}}',result[1]];
return [`${getTeXsymbol(symbol)}{${result[0]}}`, result[1]];
} else {
// font change command
return [`{${getTeXsymbol(symbol)}{${result[0]}}}`, result[1]];
}
}
case tokens.BINARY: {
str = removeCharsAndBlanks(str, symbol.input.length);
const result = parseSexpr(str, depth);
if (result[0] === null) {
return [`{${getTeXsymbol(symbol)}}`, str];
}
result[0] = removeBrackets(result[0]);
var result2 = parseSexpr(result[1], depth);
if (result2[0] === null) {
return [`{${getTeXsymbol(symbol)}}`, str];
}
result2[0] = removeBrackets(result2[0]);
let newFrag = '';
if (symbol.input === 'color') {
newFrag = `{\\color{${result[0].replace(/[{}]/g, '')}}${result2[0]}}`;
} else if (symbol.input === 'root') {
newFrag = `{\\sqrt[${result[0]}]{${result2[0]}}}`;
} else {
newFrag = `{${getTeXsymbol(symbol)}{${result[0]}}{${result2[0]}}}`;
}
return [newFrag, result2[1]];
}
case tokens.INFIX:
str = removeCharsAndBlanks(str, symbol.input.length);
return [symbol.output, str];
case tokens.SPACE:
str = removeCharsAndBlanks(str, symbol.input.length);
return [`{\\quad\\text{${symbol.input}}\\quad}`, str];
case tokens.LEFTRIGHT: {
// if (rightvert) return [null,str]; else rightvert = true;
depth++;
str = removeCharsAndBlanks(str, symbol.input.length);
const result = parseExpr(str, false, depth);
depth--;
var st = '';
st = result[0].charAt(result[0].length - 1);
// alert(result[0].lastChild+'***'+st);
if (st === '|') {
// its an absolute value subterm
const node = `{\\left|${result[0]}}`;
return [node, result[1]];
} else {
// the '|' is a \mid
const node = '{\\mid}';
return [node, str];
}
}
default:
// alert('default');
str = removeCharsAndBlanks(str, symbol.input.length);
return [`{${getTeXsymbol(symbol)}}`, str];
}
}
/**
* @param {string} str
* @param {number} depth
* @returns {[string, string]}
*/
function parseIexpr(str, depth) {
let sym2;
str = removeCharsAndBlanks(str, 0);
const sym1 = getSymbol(str);
let result = parseSexpr(str, depth);
let node = result[0];
str = result[1];
const symbol = getSymbol(str);
if (!(symbol.tType === tokens.INFIX && symbol.input !== '/')) {
return [node, str];
}
str = removeCharsAndBlanks(str, symbol.input.length);
// if (symbol.input === '/') result = parseIexpr(str); else
result = parseSexpr(str, depth);
// show box in place of missing argument
if (result[0] === null) {
result[0] = '{}';
} else {
result[0] = removeBrackets(result[0]);
}
str = result[1];
// if (symbol.input === '/') removeBrackets(node);
if (symbol.input === '_') {
sym2 = getSymbol(str);
if (sym2.input === '^') {
str = removeCharsAndBlanks(str, sym2.input.length);
const res2 = parseSexpr(str, depth);
res2[0] = removeBrackets(res2[0]);
str = res2[1];
node = `{${node}`;
node += `_{${result[0]}}`;
node += `^{${res2[0]}}`;
node += '}';
} else {
node += `_{${result[0]}}`;
}
} else {
// must be ^
// node = '{'+node+'}^{'+result[0]+'}';
node = `${node}^{${result[0]}}`;
}
if (typeof sym1.func !== 'undefined' && sym1.func) {
sym2 = getSymbol(str);
if (sym2.tType !== tokens.INFIX && sym2.tType !== tokens.RIGHTBRACKET) {
result = parseIexpr(str, depth);
node = `{${node}${result[0]}}`;
str = result[1];
}
}
return [node, str];
}
/**
* @param {string} str
* @param {boolean} rightbracket
* @param {number} depth
* @returns {[string, string]}
*/
function parseExpr(str, rightbracket, depth) {
let symbol;
let node;
let result;
let i;
// const nodeList = [];
let newFrag = '';
let addedright = false;
do {
str = removeCharsAndBlanks(str, 0);
result = parseIexpr(str, depth);
node = result[0];
str = result[1];
symbol = getSymbol(str);
if (symbol.tType === tokens.INFIX && symbol.input === '/') {
str = removeCharsAndBlanks(str, symbol.input.length);
result = parseIexpr(str, depth);
// show box in place of missing argument
if (result[0] === null) {
result[0] = '{}';
} else {
result[0] = removeBrackets(result[0]);
}
str = result[1];
node = removeBrackets(node);
node = `${'\\frac' + '{'}${node}}`;
node += `{${result[0]}}`;
newFrag += node;
symbol = getSymbol(str);
} else if (node) {
newFrag += node;
}
} while (
((symbol.tType !== tokens.RIGHTBRACKET &&
(symbol.tType !== tokens.LEFTRIGHT || rightbracket)) ||
depth === 0) &&
symbol &&
symbol.output
);
if (
symbol.tType === tokens.RIGHTBRACKET ||
symbol.tType === tokens.LEFTRIGHT
) {
// if (depth > 0) depth--;
const len = newFrag.length;
if (len > 2 && newFrag.charAt(0) === '{' && newFrag.indexOf(',') > 0) {
// could be matrix (total rewrite from .js)
const right = newFrag.charAt(len - 2);
if (right === ')' || right === ']') {
const left = newFrag.charAt(6);
if (
(left === '(' && right === ')' && symbol.output !== '}') ||
(left === '[' && right === ']')
) {
let mxout = '\\matrix{';
const pos = new Array(); // position of commas
pos.push(0);
let matrix = true;
let mxnestingd = 0;
const subpos = [];
subpos[0] = [0];
let lastsubposstart = 0;
let mxanynestingd = 0;
for (i = 1; i < len - 1; i++) {
if (newFrag.charAt(i) === left) {
mxnestingd++;
}
if (newFrag.charAt(i) === right) {
mxnestingd--;
if (
mxnestingd === 0 &&
newFrag.charAt(i + 2) === ',' &&
newFrag.charAt(i + 3) === '{'
) {
pos.push(i + 2);
lastsubposstart = i + 2;
subpos[lastsubposstart] = [i + 2];
}
}
if (
newFrag.charAt(i) === '[' ||
newFrag.charAt(i) === '(' ||
newFrag.charAt(i) === '{'
) {
mxanynestingd++;
}
if (
newFrag.charAt(i) === ']' ||
newFrag.charAt(i) === ')' ||
newFrag.charAt(i) === '}'
) {
mxanynestingd--;
}
if (newFrag.charAt(i) === ',' && mxanynestingd === 1) {
subpos[lastsubposstart].push(i);
}
if (mxanynestingd < 0) {
// happens at the end of the row
if (lastsubposstart === i + 1) {
// if at end of row, skip to next row
i++;