chore: 添加虚拟环境到仓库
- 添加 backend_service/venv 虚拟环境 - 包含所有Python依赖包 - 注意:虚拟环境约393MB,包含12655个文件
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright 2016, latex2sympy
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
ANTLR4 LaTeX Math Grammar
|
||||
|
||||
Ported from latex2sympy by @augustt198 https://github.com/augustt198/latex2sympy See license in
|
||||
LICENSE.txt
|
||||
*/
|
||||
|
||||
/*
|
||||
After changing this file, it is necessary to run `python setup.py antlr` in the root directory of
|
||||
the repository. This will regenerate the code in `sympy/parsing/latex/_antlr/*.py`.
|
||||
*/
|
||||
|
||||
grammar LaTeX;
|
||||
|
||||
options {
|
||||
language = Python3;
|
||||
}
|
||||
|
||||
WS: [ \t\r\n]+ -> skip;
|
||||
THINSPACE: ('\\,' | '\\thinspace') -> skip;
|
||||
MEDSPACE: ('\\:' | '\\medspace') -> skip;
|
||||
THICKSPACE: ('\\;' | '\\thickspace') -> skip;
|
||||
QUAD: '\\quad' -> skip;
|
||||
QQUAD: '\\qquad' -> skip;
|
||||
NEGTHINSPACE: ('\\!' | '\\negthinspace') -> skip;
|
||||
NEGMEDSPACE: '\\negmedspace' -> skip;
|
||||
NEGTHICKSPACE: '\\negthickspace' -> skip;
|
||||
CMD_LEFT: '\\left' -> skip;
|
||||
CMD_RIGHT: '\\right' -> skip;
|
||||
|
||||
IGNORE:
|
||||
(
|
||||
'\\vrule'
|
||||
| '\\vcenter'
|
||||
| '\\vbox'
|
||||
| '\\vskip'
|
||||
| '\\vspace'
|
||||
| '\\hfil'
|
||||
| '\\*'
|
||||
| '\\-'
|
||||
| '\\.'
|
||||
| '\\/'
|
||||
| '\\"'
|
||||
| '\\('
|
||||
| '\\='
|
||||
) -> skip;
|
||||
|
||||
ADD: '+';
|
||||
SUB: '-';
|
||||
MUL: '*';
|
||||
DIV: '/';
|
||||
|
||||
L_PAREN: '(';
|
||||
R_PAREN: ')';
|
||||
L_BRACE: '{';
|
||||
R_BRACE: '}';
|
||||
L_BRACE_LITERAL: '\\{';
|
||||
R_BRACE_LITERAL: '\\}';
|
||||
L_BRACKET: '[';
|
||||
R_BRACKET: ']';
|
||||
|
||||
BAR: '|';
|
||||
|
||||
R_BAR: '\\right|';
|
||||
L_BAR: '\\left|';
|
||||
|
||||
L_ANGLE: '\\langle';
|
||||
R_ANGLE: '\\rangle';
|
||||
FUNC_LIM: '\\lim';
|
||||
LIM_APPROACH_SYM:
|
||||
'\\to'
|
||||
| '\\rightarrow'
|
||||
| '\\Rightarrow'
|
||||
| '\\longrightarrow'
|
||||
| '\\Longrightarrow';
|
||||
FUNC_INT:
|
||||
'\\int'
|
||||
| '\\int\\limits';
|
||||
FUNC_SUM: '\\sum';
|
||||
FUNC_PROD: '\\prod';
|
||||
|
||||
FUNC_EXP: '\\exp';
|
||||
FUNC_LOG: '\\log';
|
||||
FUNC_LG: '\\lg';
|
||||
FUNC_LN: '\\ln';
|
||||
FUNC_SIN: '\\sin';
|
||||
FUNC_COS: '\\cos';
|
||||
FUNC_TAN: '\\tan';
|
||||
FUNC_CSC: '\\csc';
|
||||
FUNC_SEC: '\\sec';
|
||||
FUNC_COT: '\\cot';
|
||||
|
||||
FUNC_ARCSIN: '\\arcsin';
|
||||
FUNC_ARCCOS: '\\arccos';
|
||||
FUNC_ARCTAN: '\\arctan';
|
||||
FUNC_ARCCSC: '\\arccsc';
|
||||
FUNC_ARCSEC: '\\arcsec';
|
||||
FUNC_ARCCOT: '\\arccot';
|
||||
|
||||
FUNC_SINH: '\\sinh';
|
||||
FUNC_COSH: '\\cosh';
|
||||
FUNC_TANH: '\\tanh';
|
||||
FUNC_ARSINH: '\\arsinh';
|
||||
FUNC_ARCOSH: '\\arcosh';
|
||||
FUNC_ARTANH: '\\artanh';
|
||||
|
||||
L_FLOOR: '\\lfloor';
|
||||
R_FLOOR: '\\rfloor';
|
||||
L_CEIL: '\\lceil';
|
||||
R_CEIL: '\\rceil';
|
||||
|
||||
FUNC_SQRT: '\\sqrt';
|
||||
FUNC_OVERLINE: '\\overline';
|
||||
|
||||
CMD_TIMES: '\\times';
|
||||
CMD_CDOT: '\\cdot';
|
||||
CMD_DIV: '\\div';
|
||||
CMD_FRAC:
|
||||
'\\frac'
|
||||
| '\\dfrac'
|
||||
| '\\tfrac';
|
||||
CMD_BINOM: '\\binom';
|
||||
CMD_DBINOM: '\\dbinom';
|
||||
CMD_TBINOM: '\\tbinom';
|
||||
|
||||
CMD_MATHIT: '\\mathit';
|
||||
|
||||
UNDERSCORE: '_';
|
||||
CARET: '^';
|
||||
COLON: ':';
|
||||
|
||||
fragment WS_CHAR: [ \t\r\n];
|
||||
DIFFERENTIAL: 'd' WS_CHAR*? ([a-zA-Z] | '\\' [a-zA-Z]+);
|
||||
|
||||
LETTER: [a-zA-Z];
|
||||
DIGIT: [0-9];
|
||||
|
||||
EQUAL: (('&' WS_CHAR*?)? '=') | ('=' (WS_CHAR*? '&')?);
|
||||
NEQ: '\\neq';
|
||||
|
||||
LT: '<';
|
||||
LTE: ('\\leq' | '\\le' | LTE_Q | LTE_S);
|
||||
LTE_Q: '\\leqq';
|
||||
LTE_S: '\\leqslant';
|
||||
|
||||
GT: '>';
|
||||
GTE: ('\\geq' | '\\ge' | GTE_Q | GTE_S);
|
||||
GTE_Q: '\\geqq';
|
||||
GTE_S: '\\geqslant';
|
||||
|
||||
BANG: '!';
|
||||
|
||||
SINGLE_QUOTES: '\''+;
|
||||
|
||||
SYMBOL: '\\' [a-zA-Z]+;
|
||||
|
||||
math: relation;
|
||||
|
||||
relation:
|
||||
relation (EQUAL | LT | LTE | GT | GTE | NEQ) relation
|
||||
| expr;
|
||||
|
||||
equality: expr EQUAL expr;
|
||||
|
||||
expr: additive;
|
||||
|
||||
additive: additive (ADD | SUB) additive | mp;
|
||||
|
||||
// mult part
|
||||
mp:
|
||||
mp (MUL | CMD_TIMES | CMD_CDOT | DIV | CMD_DIV | COLON) mp
|
||||
| unary;
|
||||
|
||||
mp_nofunc:
|
||||
mp_nofunc (
|
||||
MUL
|
||||
| CMD_TIMES
|
||||
| CMD_CDOT
|
||||
| DIV
|
||||
| CMD_DIV
|
||||
| COLON
|
||||
) mp_nofunc
|
||||
| unary_nofunc;
|
||||
|
||||
unary: (ADD | SUB) unary | postfix+;
|
||||
|
||||
unary_nofunc:
|
||||
(ADD | SUB) unary_nofunc
|
||||
| postfix postfix_nofunc*;
|
||||
|
||||
postfix: exp postfix_op*;
|
||||
postfix_nofunc: exp_nofunc postfix_op*;
|
||||
postfix_op: BANG | eval_at;
|
||||
|
||||
eval_at:
|
||||
BAR (eval_at_sup | eval_at_sub | eval_at_sup eval_at_sub);
|
||||
|
||||
eval_at_sub: UNDERSCORE L_BRACE (expr | equality) R_BRACE;
|
||||
|
||||
eval_at_sup: CARET L_BRACE (expr | equality) R_BRACE;
|
||||
|
||||
exp: exp CARET (atom | L_BRACE expr R_BRACE) subexpr? | comp;
|
||||
|
||||
exp_nofunc:
|
||||
exp_nofunc CARET (atom | L_BRACE expr R_BRACE) subexpr?
|
||||
| comp_nofunc;
|
||||
|
||||
comp:
|
||||
group
|
||||
| abs_group
|
||||
| func
|
||||
| atom
|
||||
| floor
|
||||
| ceil;
|
||||
|
||||
comp_nofunc:
|
||||
group
|
||||
| abs_group
|
||||
| atom
|
||||
| floor
|
||||
| ceil;
|
||||
|
||||
group:
|
||||
L_PAREN expr R_PAREN
|
||||
| L_BRACKET expr R_BRACKET
|
||||
| L_BRACE expr R_BRACE
|
||||
| L_BRACE_LITERAL expr R_BRACE_LITERAL;
|
||||
|
||||
abs_group: BAR expr BAR;
|
||||
|
||||
number: DIGIT+ (',' DIGIT DIGIT DIGIT)* ('.' DIGIT+)?;
|
||||
|
||||
atom: (LETTER | SYMBOL) (subexpr? SINGLE_QUOTES? | SINGLE_QUOTES? subexpr?)
|
||||
| number
|
||||
| DIFFERENTIAL
|
||||
| mathit
|
||||
| frac
|
||||
| binom
|
||||
| bra
|
||||
| ket;
|
||||
|
||||
bra: L_ANGLE expr (R_BAR | BAR);
|
||||
ket: (L_BAR | BAR) expr R_ANGLE;
|
||||
|
||||
mathit: CMD_MATHIT L_BRACE mathit_text R_BRACE;
|
||||
mathit_text: LETTER*;
|
||||
|
||||
frac: CMD_FRAC (upperd = DIGIT | L_BRACE upper = expr R_BRACE)
|
||||
(lowerd = DIGIT | L_BRACE lower = expr R_BRACE);
|
||||
|
||||
binom:
|
||||
(CMD_BINOM | CMD_DBINOM | CMD_TBINOM) L_BRACE n = expr R_BRACE L_BRACE k = expr R_BRACE;
|
||||
|
||||
floor: L_FLOOR val = expr R_FLOOR;
|
||||
ceil: L_CEIL val = expr R_CEIL;
|
||||
|
||||
func_normal:
|
||||
FUNC_EXP
|
||||
| FUNC_LOG
|
||||
| FUNC_LG
|
||||
| FUNC_LN
|
||||
| FUNC_SIN
|
||||
| FUNC_COS
|
||||
| FUNC_TAN
|
||||
| FUNC_CSC
|
||||
| FUNC_SEC
|
||||
| FUNC_COT
|
||||
| FUNC_ARCSIN
|
||||
| FUNC_ARCCOS
|
||||
| FUNC_ARCTAN
|
||||
| FUNC_ARCCSC
|
||||
| FUNC_ARCSEC
|
||||
| FUNC_ARCCOT
|
||||
| FUNC_SINH
|
||||
| FUNC_COSH
|
||||
| FUNC_TANH
|
||||
| FUNC_ARSINH
|
||||
| FUNC_ARCOSH
|
||||
| FUNC_ARTANH;
|
||||
|
||||
func:
|
||||
func_normal (subexpr? supexpr? | supexpr? subexpr?) (
|
||||
L_PAREN func_arg R_PAREN
|
||||
| func_arg_noparens
|
||||
)
|
||||
| (LETTER | SYMBOL) (subexpr? SINGLE_QUOTES? | SINGLE_QUOTES? subexpr?) // e.g. f(x), f_1'(x)
|
||||
L_PAREN args R_PAREN
|
||||
| FUNC_INT (subexpr supexpr | supexpr subexpr)? (
|
||||
additive? DIFFERENTIAL
|
||||
| frac
|
||||
| additive
|
||||
)
|
||||
| FUNC_SQRT (L_BRACKET root = expr R_BRACKET)? L_BRACE base = expr R_BRACE
|
||||
| FUNC_OVERLINE L_BRACE base = expr R_BRACE
|
||||
| (FUNC_SUM | FUNC_PROD) (subeq supexpr | supexpr subeq) mp
|
||||
| FUNC_LIM limit_sub mp;
|
||||
|
||||
args: (expr ',' args) | expr;
|
||||
|
||||
limit_sub:
|
||||
UNDERSCORE L_BRACE (LETTER | SYMBOL) LIM_APPROACH_SYM expr (
|
||||
CARET ((L_BRACE (ADD | SUB) R_BRACE) | ADD | SUB)
|
||||
)? R_BRACE;
|
||||
|
||||
func_arg: expr | (expr ',' func_arg);
|
||||
func_arg_noparens: mp_nofunc;
|
||||
|
||||
subexpr: UNDERSCORE (atom | L_BRACE expr R_BRACE);
|
||||
supexpr: CARET (atom | L_BRACE expr R_BRACE);
|
||||
|
||||
subeq: UNDERSCORE L_BRACE equality R_BRACE;
|
||||
supeq: UNDERSCORE L_BRACE equality R_BRACE;
|
||||
@@ -0,0 +1,204 @@
|
||||
from sympy.external import import_module
|
||||
from sympy.utilities.decorator import doctest_depends_on
|
||||
from re import compile as rcompile
|
||||
|
||||
from sympy.parsing.latex.lark import LarkLaTeXParser, TransformToSymPyExpr, parse_latex_lark # noqa
|
||||
|
||||
from .errors import LaTeXParsingError # noqa
|
||||
|
||||
|
||||
IGNORE_L = r"\s*[{]*\s*"
|
||||
IGNORE_R = r"\s*[}]*\s*"
|
||||
NO_LEFT = r"(?<!\\left)"
|
||||
BEGIN_AMS_MAT = r"\\begin{matrix}"
|
||||
END_AMS_MAT = r"\\end{matrix}"
|
||||
BEGIN_ARR = r"\\begin{array}{.*?}"
|
||||
END_ARR = r"\\end{array}"
|
||||
|
||||
# begin_delim_regex: end_delim_regex
|
||||
MATRIX_DELIMS = {fr"\\left\({IGNORE_L}{BEGIN_AMS_MAT}": fr"{END_AMS_MAT}{IGNORE_R}\\right\)",
|
||||
fr"{NO_LEFT}\({IGNORE_L}{BEGIN_AMS_MAT}": fr"{END_AMS_MAT}{IGNORE_R}\)",
|
||||
fr"\\left\[{IGNORE_L}{BEGIN_AMS_MAT}": fr"{END_AMS_MAT}{IGNORE_R}\\right\]",
|
||||
fr"{NO_LEFT}\[{IGNORE_L}{BEGIN_AMS_MAT}": fr"{END_AMS_MAT}{IGNORE_R}\]",
|
||||
fr"\\left\|{IGNORE_L}{BEGIN_AMS_MAT}": fr"{END_AMS_MAT}{IGNORE_R}\\right\|",
|
||||
fr"{NO_LEFT}\|{IGNORE_L}{BEGIN_AMS_MAT}": fr"{END_AMS_MAT}{IGNORE_R}\|",
|
||||
r"\\begin{pmatrix}": r"\\end{pmatrix}",
|
||||
r"\\begin{bmatrix}": r"\\end{bmatrix}",
|
||||
r"\\begin{vmatrix}": r"\\end{vmatrix}",
|
||||
fr"\\left\({IGNORE_L}{BEGIN_ARR}": fr"{END_ARR}{IGNORE_R}\\right\)",
|
||||
fr"{NO_LEFT}\({IGNORE_L}{BEGIN_ARR}": fr"{END_ARR}{IGNORE_R}\)",
|
||||
fr"\\left\[{IGNORE_L}{BEGIN_ARR}": fr"{END_ARR}{IGNORE_R}\\right\]",
|
||||
fr"{NO_LEFT}\[{IGNORE_L}{BEGIN_ARR}": fr"{END_ARR}{IGNORE_R}\]",
|
||||
fr"\\left\|{IGNORE_L}{BEGIN_ARR}": fr"{END_ARR}{IGNORE_R}\\right\|",
|
||||
fr"{NO_LEFT}\|{IGNORE_L}{BEGIN_ARR}": fr"{END_ARR}{IGNORE_R}\|"
|
||||
}
|
||||
|
||||
MATRIX_DELIMS_INV = {v: k for k, v in MATRIX_DELIMS.items()}
|
||||
|
||||
# begin_delim_regex: ideal_begin_delim_representative
|
||||
BEGIN_DELIM_REPR = {fr"\\left\({IGNORE_L}{BEGIN_AMS_MAT}": "\\left(\\begin{matrix}",
|
||||
fr"{NO_LEFT}\({IGNORE_L}{BEGIN_AMS_MAT}": "(\\begin{matrix}",
|
||||
fr"\\left\[{IGNORE_L}{BEGIN_AMS_MAT}": "\\left[\\begin{matrix}",
|
||||
fr"{NO_LEFT}\[{IGNORE_L}{BEGIN_AMS_MAT}": "[\\begin{matrix}",
|
||||
fr"\\left\|{IGNORE_L}{BEGIN_AMS_MAT}": "\\left|\\begin{matrix}",
|
||||
fr"{NO_LEFT}\|{IGNORE_L}{BEGIN_AMS_MAT}": "|\\begin{matrix}",
|
||||
r"\\begin{pmatrix}": "\\begin{pmatrix}",
|
||||
r"\\begin{bmatrix}": "\\begin{bmatrix}",
|
||||
r"\\begin{vmatrix}": "\\begin{vmatrix}",
|
||||
fr"\\left\({IGNORE_L}{BEGIN_ARR}": "\\left(\\begin{array}{COLUMN_SPECIFIERS}",
|
||||
fr"{NO_LEFT}\({IGNORE_L}{BEGIN_ARR}": "(\\begin{array}{COLUMN_SPECIFIERS}",
|
||||
fr"\\left\[{IGNORE_L}{BEGIN_ARR}": "\\left[\\begin{array}{COLUMN_SPECIFIERS}",
|
||||
fr"{NO_LEFT}\[{IGNORE_L}{BEGIN_ARR}": "[\\begin{array}{COLUMN_SPECIFIERS}",
|
||||
fr"\\left\|{IGNORE_L}{BEGIN_ARR}": "\\left|\\begin{array}{COLUMN_SPECIFIERS}",
|
||||
fr"{NO_LEFT}\|{IGNORE_L}{BEGIN_ARR}": "|\\begin{array}{COLUMN_SPECIFIERS}"
|
||||
}
|
||||
|
||||
# end_delim_regex: ideal_end_delim_representative
|
||||
END_DELIM_REPR = {fr"{END_AMS_MAT}{IGNORE_R}\\right\)": "\\end{matrix}\\right)",
|
||||
fr"{END_AMS_MAT}{IGNORE_R}\)": "\\end{matrix})",
|
||||
fr"{END_AMS_MAT}{IGNORE_R}\\right\]": "\\end{matrix}\\right]",
|
||||
fr"{END_AMS_MAT}{IGNORE_R}\]": "\\end{matrix}]",
|
||||
fr"{END_AMS_MAT}{IGNORE_R}\\right\|": "\\end{matrix}\\right|",
|
||||
fr"{END_AMS_MAT}{IGNORE_R}\|": "\\end{matrix}|",
|
||||
r"\\end{pmatrix}": "\\end{pmatrix}",
|
||||
r"\\end{bmatrix}": "\\end{bmatrix}",
|
||||
r"\\end{vmatrix}": "\\end{vmatrix}",
|
||||
fr"{END_ARR}{IGNORE_R}\\right\)": "\\end{array}\\right)",
|
||||
fr"{END_ARR}{IGNORE_R}\)": "\\end{array})",
|
||||
fr"{END_ARR}{IGNORE_R}\\right\]": "\\end{array}\\right]",
|
||||
fr"{END_ARR}{IGNORE_R}\]": "\\end{array}]",
|
||||
fr"{END_ARR}{IGNORE_R}\\right\|": "\\end{array}\\right|",
|
||||
fr"{END_ARR}{IGNORE_R}\|": "\\end{array}|"
|
||||
}
|
||||
|
||||
|
||||
def check_matrix_delimiters(latex_str):
|
||||
"""Report mismatched, excess, or missing matrix delimiters."""
|
||||
spans = []
|
||||
for begin_delim in MATRIX_DELIMS:
|
||||
end_delim = MATRIX_DELIMS[begin_delim]
|
||||
|
||||
p = rcompile(begin_delim)
|
||||
q = rcompile(end_delim)
|
||||
|
||||
spans.extend([(*m.span(), m.group(),
|
||||
begin_delim) for m in p.finditer(latex_str)])
|
||||
spans.extend([(*m.span(), m.group(),
|
||||
end_delim) for m in q.finditer(latex_str)])
|
||||
|
||||
spans.sort(key=(lambda x: x[0]))
|
||||
if len(spans) % 2 == 1:
|
||||
# Odd number of delimiters; therefore something
|
||||
# is wrong. We do not complain yet; let's see if
|
||||
# we can pinpoint the actual error.
|
||||
spans.append((None, None, None, None))
|
||||
|
||||
spans = [(*x, *y) for (x, y) in zip(spans[::2], spans[1::2])]
|
||||
for x in spans:
|
||||
# x is supposed to be an 8-tuple of the following form:
|
||||
#
|
||||
# (begin_delim_span_start, begin_delim_span_end,
|
||||
# begin_delim_match, begin_delim_regex,
|
||||
# end_delim_span_start, end_delim_span_end,
|
||||
# end_delim_match, end_delim_regex)
|
||||
|
||||
sellipsis = "..."
|
||||
s = x[0] - 10
|
||||
if s < 0:
|
||||
s = 0
|
||||
sellipsis = ""
|
||||
|
||||
eellipsis = "..."
|
||||
e = x[1] + 10
|
||||
if e > len(latex_str):
|
||||
e = len(latex_str)
|
||||
eellipsis = ""
|
||||
|
||||
if x[3] in END_DELIM_REPR:
|
||||
err = (f"Extra '{x[2]}' at index {x[0]} or "
|
||||
"missing corresponding "
|
||||
f"'{BEGIN_DELIM_REPR[MATRIX_DELIMS_INV[x[3]]]}' "
|
||||
f"in LaTeX string: {sellipsis}{latex_str[s:e]}"
|
||||
f"{eellipsis}")
|
||||
raise LaTeXParsingError(err)
|
||||
|
||||
if x[7] is None:
|
||||
err = (f"Extra '{x[2]}' at index {x[0]} or "
|
||||
"missing corresponding "
|
||||
f"'{END_DELIM_REPR[MATRIX_DELIMS[x[3]]]}' "
|
||||
f"in LaTeX string: {sellipsis}{latex_str[s:e]}"
|
||||
f"{eellipsis}")
|
||||
raise LaTeXParsingError(err)
|
||||
|
||||
correct_end_regex = MATRIX_DELIMS[x[3]]
|
||||
sellipsis = "..." if x[0] > 0 else ""
|
||||
eellipsis = "..." if x[5] < len(latex_str) else ""
|
||||
if x[7] != correct_end_regex:
|
||||
err = ("Expected "
|
||||
f"'{END_DELIM_REPR[correct_end_regex]}' "
|
||||
f"to close the '{x[2]}' at index {x[0]} but "
|
||||
f"found '{x[6]}' at index {x[4]} of LaTeX "
|
||||
f"string instead: {sellipsis}{latex_str[x[0]:x[5]]}"
|
||||
f"{eellipsis}")
|
||||
raise LaTeXParsingError(err)
|
||||
|
||||
__doctest_requires__ = {('parse_latex',): ['antlr4', 'lark']}
|
||||
|
||||
|
||||
@doctest_depends_on(modules=('antlr4', 'lark'))
|
||||
def parse_latex(s, strict=False, backend="antlr"):
|
||||
r"""Converts the input LaTeX string ``s`` to a SymPy ``Expr``.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
s : str
|
||||
The LaTeX string to parse. In Python source containing LaTeX,
|
||||
*raw strings* (denoted with ``r"``, like this one) are preferred,
|
||||
as LaTeX makes liberal use of the ``\`` character, which would
|
||||
trigger escaping in normal Python strings.
|
||||
backend : str, optional
|
||||
Currently, there are two backends supported: ANTLR, and Lark.
|
||||
The default setting is to use the ANTLR backend, which can be
|
||||
changed to Lark if preferred.
|
||||
|
||||
Use ``backend="antlr"`` for the ANTLR-based parser, and
|
||||
``backend="lark"`` for the Lark-based parser.
|
||||
|
||||
The ``backend`` option is case-sensitive, and must be in
|
||||
all lowercase.
|
||||
strict : bool, optional
|
||||
This option is only available with the ANTLR backend.
|
||||
|
||||
If True, raise an exception if the string cannot be parsed as
|
||||
valid LaTeX. If False, try to recover gracefully from common
|
||||
mistakes.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.parsing.latex import parse_latex
|
||||
>>> expr = parse_latex(r"\frac {1 + \sqrt {\a}} {\b}")
|
||||
>>> expr
|
||||
(sqrt(a) + 1)/b
|
||||
>>> expr.evalf(4, subs=dict(a=5, b=2))
|
||||
1.618
|
||||
>>> func = parse_latex(r"\int_1^\alpha \dfrac{\mathrm{d}t}{t}", backend="lark")
|
||||
>>> func.evalf(subs={"alpha": 2})
|
||||
0.693147180559945
|
||||
"""
|
||||
|
||||
check_matrix_delimiters(s)
|
||||
|
||||
if backend == "antlr":
|
||||
_latex = import_module(
|
||||
'sympy.parsing.latex._parse_latex_antlr',
|
||||
import_kwargs={'fromlist': ['X']})
|
||||
|
||||
if _latex is not None:
|
||||
return _latex.parse_latex(s, strict)
|
||||
elif backend == "lark":
|
||||
return parse_latex_lark(s)
|
||||
else:
|
||||
raise NotImplementedError(f"Using the '{backend}' backend in the LaTeX" \
|
||||
" parser is not supported.")
|
||||
@@ -0,0 +1,9 @@
|
||||
# *** GENERATED BY `setup.py antlr`, DO NOT EDIT BY HAND ***
|
||||
#
|
||||
# Generated from ../LaTeX.g4, derived from latex2sympy
|
||||
# latex2sympy is licensed under the MIT license
|
||||
# https://github.com/augustt198/latex2sympy/blob/master/LICENSE.txt
|
||||
#
|
||||
# Generated with antlr4
|
||||
# antlr4 is licensed under the BSD-3-Clause License
|
||||
# https://github.com/antlr/antlr4/blob/master/LICENSE.txt
|
||||
@@ -0,0 +1,512 @@
|
||||
# *** GENERATED BY `setup.py antlr`, DO NOT EDIT BY HAND ***
|
||||
#
|
||||
# Generated from ../LaTeX.g4, derived from latex2sympy
|
||||
# latex2sympy is licensed under the MIT license
|
||||
# https://github.com/augustt198/latex2sympy/blob/master/LICENSE.txt
|
||||
#
|
||||
# Generated with antlr4
|
||||
# antlr4 is licensed under the BSD-3-Clause License
|
||||
# https://github.com/antlr/antlr4/blob/master/LICENSE.txt
|
||||
from antlr4 import *
|
||||
from io import StringIO
|
||||
import sys
|
||||
if sys.version_info[1] > 5:
|
||||
from typing import TextIO
|
||||
else:
|
||||
from typing.io import TextIO
|
||||
|
||||
|
||||
def serializedATN():
|
||||
return [
|
||||
4,0,91,911,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,
|
||||
2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,
|
||||
13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,
|
||||
19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,
|
||||
26,7,26,2,27,7,27,2,28,7,28,2,29,7,29,2,30,7,30,2,31,7,31,2,32,7,
|
||||
32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,7,38,2,
|
||||
39,7,39,2,40,7,40,2,41,7,41,2,42,7,42,2,43,7,43,2,44,7,44,2,45,7,
|
||||
45,2,46,7,46,2,47,7,47,2,48,7,48,2,49,7,49,2,50,7,50,2,51,7,51,2,
|
||||
52,7,52,2,53,7,53,2,54,7,54,2,55,7,55,2,56,7,56,2,57,7,57,2,58,7,
|
||||
58,2,59,7,59,2,60,7,60,2,61,7,61,2,62,7,62,2,63,7,63,2,64,7,64,2,
|
||||
65,7,65,2,66,7,66,2,67,7,67,2,68,7,68,2,69,7,69,2,70,7,70,2,71,7,
|
||||
71,2,72,7,72,2,73,7,73,2,74,7,74,2,75,7,75,2,76,7,76,2,77,7,77,2,
|
||||
78,7,78,2,79,7,79,2,80,7,80,2,81,7,81,2,82,7,82,2,83,7,83,2,84,7,
|
||||
84,2,85,7,85,2,86,7,86,2,87,7,87,2,88,7,88,2,89,7,89,2,90,7,90,2,
|
||||
91,7,91,1,0,1,0,1,1,1,1,1,2,4,2,191,8,2,11,2,12,2,192,1,2,1,2,1,
|
||||
3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,3,3,209,8,3,1,3,1,
|
||||
3,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,3,4,224,8,4,1,4,1,
|
||||
4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,241,8,
|
||||
5,1,5,1,5,1,6,1,6,1,6,1,6,1,6,1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,
|
||||
7,1,7,1,7,1,7,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,
|
||||
8,1,8,1,8,3,8,277,8,8,1,8,1,8,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,
|
||||
9,1,9,1,9,1,9,1,9,1,9,1,9,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,
|
||||
1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,11,1,11,1,11,1,11,
|
||||
1,11,1,11,1,11,1,11,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,
|
||||
1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,
|
||||
1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,
|
||||
1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,
|
||||
1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,3,13,
|
||||
381,8,13,1,13,1,13,1,14,1,14,1,15,1,15,1,16,1,16,1,17,1,17,1,18,
|
||||
1,18,1,19,1,19,1,20,1,20,1,21,1,21,1,22,1,22,1,22,1,23,1,23,1,23,
|
||||
1,24,1,24,1,25,1,25,1,26,1,26,1,27,1,27,1,27,1,27,1,27,1,27,1,27,
|
||||
1,27,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,29,1,29,1,29,1,29,1,29,
|
||||
1,29,1,29,1,29,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,31,1,31,
|
||||
1,31,1,31,1,31,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,
|
||||
1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,
|
||||
1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,
|
||||
1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,
|
||||
1,32,1,32,1,32,1,32,1,32,1,32,3,32,504,8,32,1,33,1,33,1,33,1,33,
|
||||
1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,3,33,521,
|
||||
8,33,1,34,1,34,1,34,1,34,1,34,1,35,1,35,1,35,1,35,1,35,1,35,1,36,
|
||||
1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,1,37,1,38,1,38,1,38,1,38,
|
||||
1,39,1,39,1,39,1,39,1,40,1,40,1,40,1,40,1,40,1,41,1,41,1,41,1,41,
|
||||
1,41,1,42,1,42,1,42,1,42,1,42,1,43,1,43,1,43,1,43,1,43,1,44,1,44,
|
||||
1,44,1,44,1,44,1,45,1,45,1,45,1,45,1,45,1,46,1,46,1,46,1,46,1,46,
|
||||
1,46,1,46,1,46,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,48,1,48,
|
||||
1,48,1,48,1,48,1,48,1,48,1,48,1,49,1,49,1,49,1,49,1,49,1,49,1,49,
|
||||
1,49,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,51,1,51,1,51,1,51,
|
||||
1,51,1,51,1,51,1,51,1,52,1,52,1,52,1,52,1,52,1,52,1,53,1,53,1,53,
|
||||
1,53,1,53,1,53,1,54,1,54,1,54,1,54,1,54,1,54,1,55,1,55,1,55,1,55,
|
||||
1,55,1,55,1,55,1,55,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,57,
|
||||
1,57,1,57,1,57,1,57,1,57,1,57,1,57,1,58,1,58,1,58,1,58,1,58,1,58,
|
||||
1,58,1,58,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,60,1,60,1,60,
|
||||
1,60,1,60,1,60,1,60,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1,62,1,62,
|
||||
1,62,1,62,1,62,1,62,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,
|
||||
1,63,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,65,1,65,1,65,1,65,1,65,
|
||||
1,65,1,66,1,66,1,66,1,66,1,66,1,67,1,67,1,67,1,67,1,67,1,67,1,67,
|
||||
1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,3,67,753,8,67,
|
||||
1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,69,1,69,1,69,1,69,1,69,1,69,
|
||||
1,69,1,69,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,71,1,71,1,71,
|
||||
1,71,1,71,1,71,1,71,1,71,1,72,1,72,1,73,1,73,1,74,1,74,1,75,1,75,
|
||||
1,76,1,76,5,76,796,8,76,10,76,12,76,799,9,76,1,76,1,76,1,76,4,76,
|
||||
804,8,76,11,76,12,76,805,3,76,808,8,76,1,77,1,77,1,78,1,78,1,79,
|
||||
1,79,5,79,816,8,79,10,79,12,79,819,9,79,3,79,821,8,79,1,79,1,79,
|
||||
1,79,5,79,826,8,79,10,79,12,79,829,9,79,1,79,3,79,832,8,79,3,79,
|
||||
834,8,79,1,80,1,80,1,80,1,80,1,80,1,81,1,81,1,82,1,82,1,82,1,82,
|
||||
1,82,1,82,1,82,1,82,1,82,3,82,852,8,82,1,83,1,83,1,83,1,83,1,83,
|
||||
1,83,1,84,1,84,1,84,1,84,1,84,1,84,1,84,1,84,1,84,1,84,1,85,1,85,
|
||||
1,86,1,86,1,86,1,86,1,86,1,86,1,86,1,86,1,86,3,86,881,8,86,1,87,
|
||||
1,87,1,87,1,87,1,87,1,87,1,88,1,88,1,88,1,88,1,88,1,88,1,88,1,88,
|
||||
1,88,1,88,1,89,1,89,1,90,4,90,902,8,90,11,90,12,90,903,1,91,1,91,
|
||||
4,91,908,8,91,11,91,12,91,909,3,797,817,827,0,92,1,1,3,2,5,3,7,4,
|
||||
9,5,11,6,13,7,15,8,17,9,19,10,21,11,23,12,25,13,27,14,29,15,31,16,
|
||||
33,17,35,18,37,19,39,20,41,21,43,22,45,23,47,24,49,25,51,26,53,27,
|
||||
55,28,57,29,59,30,61,31,63,32,65,33,67,34,69,35,71,36,73,37,75,38,
|
||||
77,39,79,40,81,41,83,42,85,43,87,44,89,45,91,46,93,47,95,48,97,49,
|
||||
99,50,101,51,103,52,105,53,107,54,109,55,111,56,113,57,115,58,117,
|
||||
59,119,60,121,61,123,62,125,63,127,64,129,65,131,66,133,67,135,68,
|
||||
137,69,139,70,141,71,143,72,145,73,147,74,149,75,151,0,153,76,155,
|
||||
77,157,78,159,79,161,80,163,81,165,82,167,83,169,84,171,85,173,86,
|
||||
175,87,177,88,179,89,181,90,183,91,1,0,3,3,0,9,10,13,13,32,32,2,
|
||||
0,65,90,97,122,1,0,48,57,949,0,1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,
|
||||
0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,0,
|
||||
17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,
|
||||
27,1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,
|
||||
37,1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,
|
||||
47,1,0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,
|
||||
57,1,0,0,0,0,59,1,0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,
|
||||
67,1,0,0,0,0,69,1,0,0,0,0,71,1,0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,
|
||||
77,1,0,0,0,0,79,1,0,0,0,0,81,1,0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,
|
||||
87,1,0,0,0,0,89,1,0,0,0,0,91,1,0,0,0,0,93,1,0,0,0,0,95,1,0,0,0,0,
|
||||
97,1,0,0,0,0,99,1,0,0,0,0,101,1,0,0,0,0,103,1,0,0,0,0,105,1,0,0,
|
||||
0,0,107,1,0,0,0,0,109,1,0,0,0,0,111,1,0,0,0,0,113,1,0,0,0,0,115,
|
||||
1,0,0,0,0,117,1,0,0,0,0,119,1,0,0,0,0,121,1,0,0,0,0,123,1,0,0,0,
|
||||
0,125,1,0,0,0,0,127,1,0,0,0,0,129,1,0,0,0,0,131,1,0,0,0,0,133,1,
|
||||
0,0,0,0,135,1,0,0,0,0,137,1,0,0,0,0,139,1,0,0,0,0,141,1,0,0,0,0,
|
||||
143,1,0,0,0,0,145,1,0,0,0,0,147,1,0,0,0,0,149,1,0,0,0,0,153,1,0,
|
||||
0,0,0,155,1,0,0,0,0,157,1,0,0,0,0,159,1,0,0,0,0,161,1,0,0,0,0,163,
|
||||
1,0,0,0,0,165,1,0,0,0,0,167,1,0,0,0,0,169,1,0,0,0,0,171,1,0,0,0,
|
||||
0,173,1,0,0,0,0,175,1,0,0,0,0,177,1,0,0,0,0,179,1,0,0,0,0,181,1,
|
||||
0,0,0,0,183,1,0,0,0,1,185,1,0,0,0,3,187,1,0,0,0,5,190,1,0,0,0,7,
|
||||
208,1,0,0,0,9,223,1,0,0,0,11,240,1,0,0,0,13,244,1,0,0,0,15,252,1,
|
||||
0,0,0,17,276,1,0,0,0,19,280,1,0,0,0,21,295,1,0,0,0,23,312,1,0,0,
|
||||
0,25,320,1,0,0,0,27,380,1,0,0,0,29,384,1,0,0,0,31,386,1,0,0,0,33,
|
||||
388,1,0,0,0,35,390,1,0,0,0,37,392,1,0,0,0,39,394,1,0,0,0,41,396,
|
||||
1,0,0,0,43,398,1,0,0,0,45,400,1,0,0,0,47,403,1,0,0,0,49,406,1,0,
|
||||
0,0,51,408,1,0,0,0,53,410,1,0,0,0,55,412,1,0,0,0,57,420,1,0,0,0,
|
||||
59,427,1,0,0,0,61,435,1,0,0,0,63,443,1,0,0,0,65,503,1,0,0,0,67,520,
|
||||
1,0,0,0,69,522,1,0,0,0,71,527,1,0,0,0,73,533,1,0,0,0,75,538,1,0,
|
||||
0,0,77,543,1,0,0,0,79,547,1,0,0,0,81,551,1,0,0,0,83,556,1,0,0,0,
|
||||
85,561,1,0,0,0,87,566,1,0,0,0,89,571,1,0,0,0,91,576,1,0,0,0,93,581,
|
||||
1,0,0,0,95,589,1,0,0,0,97,597,1,0,0,0,99,605,1,0,0,0,101,613,1,0,
|
||||
0,0,103,621,1,0,0,0,105,629,1,0,0,0,107,635,1,0,0,0,109,641,1,0,
|
||||
0,0,111,647,1,0,0,0,113,655,1,0,0,0,115,663,1,0,0,0,117,671,1,0,
|
||||
0,0,119,679,1,0,0,0,121,687,1,0,0,0,123,694,1,0,0,0,125,701,1,0,
|
||||
0,0,127,707,1,0,0,0,129,717,1,0,0,0,131,724,1,0,0,0,133,730,1,0,
|
||||
0,0,135,752,1,0,0,0,137,754,1,0,0,0,139,761,1,0,0,0,141,769,1,0,
|
||||
0,0,143,777,1,0,0,0,145,785,1,0,0,0,147,787,1,0,0,0,149,789,1,0,
|
||||
0,0,151,791,1,0,0,0,153,793,1,0,0,0,155,809,1,0,0,0,157,811,1,0,
|
||||
0,0,159,833,1,0,0,0,161,835,1,0,0,0,163,840,1,0,0,0,165,851,1,0,
|
||||
0,0,167,853,1,0,0,0,169,859,1,0,0,0,171,869,1,0,0,0,173,880,1,0,
|
||||
0,0,175,882,1,0,0,0,177,888,1,0,0,0,179,898,1,0,0,0,181,901,1,0,
|
||||
0,0,183,905,1,0,0,0,185,186,5,44,0,0,186,2,1,0,0,0,187,188,5,46,
|
||||
0,0,188,4,1,0,0,0,189,191,7,0,0,0,190,189,1,0,0,0,191,192,1,0,0,
|
||||
0,192,190,1,0,0,0,192,193,1,0,0,0,193,194,1,0,0,0,194,195,6,2,0,
|
||||
0,195,6,1,0,0,0,196,197,5,92,0,0,197,209,5,44,0,0,198,199,5,92,0,
|
||||
0,199,200,5,116,0,0,200,201,5,104,0,0,201,202,5,105,0,0,202,203,
|
||||
5,110,0,0,203,204,5,115,0,0,204,205,5,112,0,0,205,206,5,97,0,0,206,
|
||||
207,5,99,0,0,207,209,5,101,0,0,208,196,1,0,0,0,208,198,1,0,0,0,209,
|
||||
210,1,0,0,0,210,211,6,3,0,0,211,8,1,0,0,0,212,213,5,92,0,0,213,224,
|
||||
5,58,0,0,214,215,5,92,0,0,215,216,5,109,0,0,216,217,5,101,0,0,217,
|
||||
218,5,100,0,0,218,219,5,115,0,0,219,220,5,112,0,0,220,221,5,97,0,
|
||||
0,221,222,5,99,0,0,222,224,5,101,0,0,223,212,1,0,0,0,223,214,1,0,
|
||||
0,0,224,225,1,0,0,0,225,226,6,4,0,0,226,10,1,0,0,0,227,228,5,92,
|
||||
0,0,228,241,5,59,0,0,229,230,5,92,0,0,230,231,5,116,0,0,231,232,
|
||||
5,104,0,0,232,233,5,105,0,0,233,234,5,99,0,0,234,235,5,107,0,0,235,
|
||||
236,5,115,0,0,236,237,5,112,0,0,237,238,5,97,0,0,238,239,5,99,0,
|
||||
0,239,241,5,101,0,0,240,227,1,0,0,0,240,229,1,0,0,0,241,242,1,0,
|
||||
0,0,242,243,6,5,0,0,243,12,1,0,0,0,244,245,5,92,0,0,245,246,5,113,
|
||||
0,0,246,247,5,117,0,0,247,248,5,97,0,0,248,249,5,100,0,0,249,250,
|
||||
1,0,0,0,250,251,6,6,0,0,251,14,1,0,0,0,252,253,5,92,0,0,253,254,
|
||||
5,113,0,0,254,255,5,113,0,0,255,256,5,117,0,0,256,257,5,97,0,0,257,
|
||||
258,5,100,0,0,258,259,1,0,0,0,259,260,6,7,0,0,260,16,1,0,0,0,261,
|
||||
262,5,92,0,0,262,277,5,33,0,0,263,264,5,92,0,0,264,265,5,110,0,0,
|
||||
265,266,5,101,0,0,266,267,5,103,0,0,267,268,5,116,0,0,268,269,5,
|
||||
104,0,0,269,270,5,105,0,0,270,271,5,110,0,0,271,272,5,115,0,0,272,
|
||||
273,5,112,0,0,273,274,5,97,0,0,274,275,5,99,0,0,275,277,5,101,0,
|
||||
0,276,261,1,0,0,0,276,263,1,0,0,0,277,278,1,0,0,0,278,279,6,8,0,
|
||||
0,279,18,1,0,0,0,280,281,5,92,0,0,281,282,5,110,0,0,282,283,5,101,
|
||||
0,0,283,284,5,103,0,0,284,285,5,109,0,0,285,286,5,101,0,0,286,287,
|
||||
5,100,0,0,287,288,5,115,0,0,288,289,5,112,0,0,289,290,5,97,0,0,290,
|
||||
291,5,99,0,0,291,292,5,101,0,0,292,293,1,0,0,0,293,294,6,9,0,0,294,
|
||||
20,1,0,0,0,295,296,5,92,0,0,296,297,5,110,0,0,297,298,5,101,0,0,
|
||||
298,299,5,103,0,0,299,300,5,116,0,0,300,301,5,104,0,0,301,302,5,
|
||||
105,0,0,302,303,5,99,0,0,303,304,5,107,0,0,304,305,5,115,0,0,305,
|
||||
306,5,112,0,0,306,307,5,97,0,0,307,308,5,99,0,0,308,309,5,101,0,
|
||||
0,309,310,1,0,0,0,310,311,6,10,0,0,311,22,1,0,0,0,312,313,5,92,0,
|
||||
0,313,314,5,108,0,0,314,315,5,101,0,0,315,316,5,102,0,0,316,317,
|
||||
5,116,0,0,317,318,1,0,0,0,318,319,6,11,0,0,319,24,1,0,0,0,320,321,
|
||||
5,92,0,0,321,322,5,114,0,0,322,323,5,105,0,0,323,324,5,103,0,0,324,
|
||||
325,5,104,0,0,325,326,5,116,0,0,326,327,1,0,0,0,327,328,6,12,0,0,
|
||||
328,26,1,0,0,0,329,330,5,92,0,0,330,331,5,118,0,0,331,332,5,114,
|
||||
0,0,332,333,5,117,0,0,333,334,5,108,0,0,334,381,5,101,0,0,335,336,
|
||||
5,92,0,0,336,337,5,118,0,0,337,338,5,99,0,0,338,339,5,101,0,0,339,
|
||||
340,5,110,0,0,340,341,5,116,0,0,341,342,5,101,0,0,342,381,5,114,
|
||||
0,0,343,344,5,92,0,0,344,345,5,118,0,0,345,346,5,98,0,0,346,347,
|
||||
5,111,0,0,347,381,5,120,0,0,348,349,5,92,0,0,349,350,5,118,0,0,350,
|
||||
351,5,115,0,0,351,352,5,107,0,0,352,353,5,105,0,0,353,381,5,112,
|
||||
0,0,354,355,5,92,0,0,355,356,5,118,0,0,356,357,5,115,0,0,357,358,
|
||||
5,112,0,0,358,359,5,97,0,0,359,360,5,99,0,0,360,381,5,101,0,0,361,
|
||||
362,5,92,0,0,362,363,5,104,0,0,363,364,5,102,0,0,364,365,5,105,0,
|
||||
0,365,381,5,108,0,0,366,367,5,92,0,0,367,381,5,42,0,0,368,369,5,
|
||||
92,0,0,369,381,5,45,0,0,370,371,5,92,0,0,371,381,5,46,0,0,372,373,
|
||||
5,92,0,0,373,381,5,47,0,0,374,375,5,92,0,0,375,381,5,34,0,0,376,
|
||||
377,5,92,0,0,377,381,5,40,0,0,378,379,5,92,0,0,379,381,5,61,0,0,
|
||||
380,329,1,0,0,0,380,335,1,0,0,0,380,343,1,0,0,0,380,348,1,0,0,0,
|
||||
380,354,1,0,0,0,380,361,1,0,0,0,380,366,1,0,0,0,380,368,1,0,0,0,
|
||||
380,370,1,0,0,0,380,372,1,0,0,0,380,374,1,0,0,0,380,376,1,0,0,0,
|
||||
380,378,1,0,0,0,381,382,1,0,0,0,382,383,6,13,0,0,383,28,1,0,0,0,
|
||||
384,385,5,43,0,0,385,30,1,0,0,0,386,387,5,45,0,0,387,32,1,0,0,0,
|
||||
388,389,5,42,0,0,389,34,1,0,0,0,390,391,5,47,0,0,391,36,1,0,0,0,
|
||||
392,393,5,40,0,0,393,38,1,0,0,0,394,395,5,41,0,0,395,40,1,0,0,0,
|
||||
396,397,5,123,0,0,397,42,1,0,0,0,398,399,5,125,0,0,399,44,1,0,0,
|
||||
0,400,401,5,92,0,0,401,402,5,123,0,0,402,46,1,0,0,0,403,404,5,92,
|
||||
0,0,404,405,5,125,0,0,405,48,1,0,0,0,406,407,5,91,0,0,407,50,1,0,
|
||||
0,0,408,409,5,93,0,0,409,52,1,0,0,0,410,411,5,124,0,0,411,54,1,0,
|
||||
0,0,412,413,5,92,0,0,413,414,5,114,0,0,414,415,5,105,0,0,415,416,
|
||||
5,103,0,0,416,417,5,104,0,0,417,418,5,116,0,0,418,419,5,124,0,0,
|
||||
419,56,1,0,0,0,420,421,5,92,0,0,421,422,5,108,0,0,422,423,5,101,
|
||||
0,0,423,424,5,102,0,0,424,425,5,116,0,0,425,426,5,124,0,0,426,58,
|
||||
1,0,0,0,427,428,5,92,0,0,428,429,5,108,0,0,429,430,5,97,0,0,430,
|
||||
431,5,110,0,0,431,432,5,103,0,0,432,433,5,108,0,0,433,434,5,101,
|
||||
0,0,434,60,1,0,0,0,435,436,5,92,0,0,436,437,5,114,0,0,437,438,5,
|
||||
97,0,0,438,439,5,110,0,0,439,440,5,103,0,0,440,441,5,108,0,0,441,
|
||||
442,5,101,0,0,442,62,1,0,0,0,443,444,5,92,0,0,444,445,5,108,0,0,
|
||||
445,446,5,105,0,0,446,447,5,109,0,0,447,64,1,0,0,0,448,449,5,92,
|
||||
0,0,449,450,5,116,0,0,450,504,5,111,0,0,451,452,5,92,0,0,452,453,
|
||||
5,114,0,0,453,454,5,105,0,0,454,455,5,103,0,0,455,456,5,104,0,0,
|
||||
456,457,5,116,0,0,457,458,5,97,0,0,458,459,5,114,0,0,459,460,5,114,
|
||||
0,0,460,461,5,111,0,0,461,504,5,119,0,0,462,463,5,92,0,0,463,464,
|
||||
5,82,0,0,464,465,5,105,0,0,465,466,5,103,0,0,466,467,5,104,0,0,467,
|
||||
468,5,116,0,0,468,469,5,97,0,0,469,470,5,114,0,0,470,471,5,114,0,
|
||||
0,471,472,5,111,0,0,472,504,5,119,0,0,473,474,5,92,0,0,474,475,5,
|
||||
108,0,0,475,476,5,111,0,0,476,477,5,110,0,0,477,478,5,103,0,0,478,
|
||||
479,5,114,0,0,479,480,5,105,0,0,480,481,5,103,0,0,481,482,5,104,
|
||||
0,0,482,483,5,116,0,0,483,484,5,97,0,0,484,485,5,114,0,0,485,486,
|
||||
5,114,0,0,486,487,5,111,0,0,487,504,5,119,0,0,488,489,5,92,0,0,489,
|
||||
490,5,76,0,0,490,491,5,111,0,0,491,492,5,110,0,0,492,493,5,103,0,
|
||||
0,493,494,5,114,0,0,494,495,5,105,0,0,495,496,5,103,0,0,496,497,
|
||||
5,104,0,0,497,498,5,116,0,0,498,499,5,97,0,0,499,500,5,114,0,0,500,
|
||||
501,5,114,0,0,501,502,5,111,0,0,502,504,5,119,0,0,503,448,1,0,0,
|
||||
0,503,451,1,0,0,0,503,462,1,0,0,0,503,473,1,0,0,0,503,488,1,0,0,
|
||||
0,504,66,1,0,0,0,505,506,5,92,0,0,506,507,5,105,0,0,507,508,5,110,
|
||||
0,0,508,521,5,116,0,0,509,510,5,92,0,0,510,511,5,105,0,0,511,512,
|
||||
5,110,0,0,512,513,5,116,0,0,513,514,5,92,0,0,514,515,5,108,0,0,515,
|
||||
516,5,105,0,0,516,517,5,109,0,0,517,518,5,105,0,0,518,519,5,116,
|
||||
0,0,519,521,5,115,0,0,520,505,1,0,0,0,520,509,1,0,0,0,521,68,1,0,
|
||||
0,0,522,523,5,92,0,0,523,524,5,115,0,0,524,525,5,117,0,0,525,526,
|
||||
5,109,0,0,526,70,1,0,0,0,527,528,5,92,0,0,528,529,5,112,0,0,529,
|
||||
530,5,114,0,0,530,531,5,111,0,0,531,532,5,100,0,0,532,72,1,0,0,0,
|
||||
533,534,5,92,0,0,534,535,5,101,0,0,535,536,5,120,0,0,536,537,5,112,
|
||||
0,0,537,74,1,0,0,0,538,539,5,92,0,0,539,540,5,108,0,0,540,541,5,
|
||||
111,0,0,541,542,5,103,0,0,542,76,1,0,0,0,543,544,5,92,0,0,544,545,
|
||||
5,108,0,0,545,546,5,103,0,0,546,78,1,0,0,0,547,548,5,92,0,0,548,
|
||||
549,5,108,0,0,549,550,5,110,0,0,550,80,1,0,0,0,551,552,5,92,0,0,
|
||||
552,553,5,115,0,0,553,554,5,105,0,0,554,555,5,110,0,0,555,82,1,0,
|
||||
0,0,556,557,5,92,0,0,557,558,5,99,0,0,558,559,5,111,0,0,559,560,
|
||||
5,115,0,0,560,84,1,0,0,0,561,562,5,92,0,0,562,563,5,116,0,0,563,
|
||||
564,5,97,0,0,564,565,5,110,0,0,565,86,1,0,0,0,566,567,5,92,0,0,567,
|
||||
568,5,99,0,0,568,569,5,115,0,0,569,570,5,99,0,0,570,88,1,0,0,0,571,
|
||||
572,5,92,0,0,572,573,5,115,0,0,573,574,5,101,0,0,574,575,5,99,0,
|
||||
0,575,90,1,0,0,0,576,577,5,92,0,0,577,578,5,99,0,0,578,579,5,111,
|
||||
0,0,579,580,5,116,0,0,580,92,1,0,0,0,581,582,5,92,0,0,582,583,5,
|
||||
97,0,0,583,584,5,114,0,0,584,585,5,99,0,0,585,586,5,115,0,0,586,
|
||||
587,5,105,0,0,587,588,5,110,0,0,588,94,1,0,0,0,589,590,5,92,0,0,
|
||||
590,591,5,97,0,0,591,592,5,114,0,0,592,593,5,99,0,0,593,594,5,99,
|
||||
0,0,594,595,5,111,0,0,595,596,5,115,0,0,596,96,1,0,0,0,597,598,5,
|
||||
92,0,0,598,599,5,97,0,0,599,600,5,114,0,0,600,601,5,99,0,0,601,602,
|
||||
5,116,0,0,602,603,5,97,0,0,603,604,5,110,0,0,604,98,1,0,0,0,605,
|
||||
606,5,92,0,0,606,607,5,97,0,0,607,608,5,114,0,0,608,609,5,99,0,0,
|
||||
609,610,5,99,0,0,610,611,5,115,0,0,611,612,5,99,0,0,612,100,1,0,
|
||||
0,0,613,614,5,92,0,0,614,615,5,97,0,0,615,616,5,114,0,0,616,617,
|
||||
5,99,0,0,617,618,5,115,0,0,618,619,5,101,0,0,619,620,5,99,0,0,620,
|
||||
102,1,0,0,0,621,622,5,92,0,0,622,623,5,97,0,0,623,624,5,114,0,0,
|
||||
624,625,5,99,0,0,625,626,5,99,0,0,626,627,5,111,0,0,627,628,5,116,
|
||||
0,0,628,104,1,0,0,0,629,630,5,92,0,0,630,631,5,115,0,0,631,632,5,
|
||||
105,0,0,632,633,5,110,0,0,633,634,5,104,0,0,634,106,1,0,0,0,635,
|
||||
636,5,92,0,0,636,637,5,99,0,0,637,638,5,111,0,0,638,639,5,115,0,
|
||||
0,639,640,5,104,0,0,640,108,1,0,0,0,641,642,5,92,0,0,642,643,5,116,
|
||||
0,0,643,644,5,97,0,0,644,645,5,110,0,0,645,646,5,104,0,0,646,110,
|
||||
1,0,0,0,647,648,5,92,0,0,648,649,5,97,0,0,649,650,5,114,0,0,650,
|
||||
651,5,115,0,0,651,652,5,105,0,0,652,653,5,110,0,0,653,654,5,104,
|
||||
0,0,654,112,1,0,0,0,655,656,5,92,0,0,656,657,5,97,0,0,657,658,5,
|
||||
114,0,0,658,659,5,99,0,0,659,660,5,111,0,0,660,661,5,115,0,0,661,
|
||||
662,5,104,0,0,662,114,1,0,0,0,663,664,5,92,0,0,664,665,5,97,0,0,
|
||||
665,666,5,114,0,0,666,667,5,116,0,0,667,668,5,97,0,0,668,669,5,110,
|
||||
0,0,669,670,5,104,0,0,670,116,1,0,0,0,671,672,5,92,0,0,672,673,5,
|
||||
108,0,0,673,674,5,102,0,0,674,675,5,108,0,0,675,676,5,111,0,0,676,
|
||||
677,5,111,0,0,677,678,5,114,0,0,678,118,1,0,0,0,679,680,5,92,0,0,
|
||||
680,681,5,114,0,0,681,682,5,102,0,0,682,683,5,108,0,0,683,684,5,
|
||||
111,0,0,684,685,5,111,0,0,685,686,5,114,0,0,686,120,1,0,0,0,687,
|
||||
688,5,92,0,0,688,689,5,108,0,0,689,690,5,99,0,0,690,691,5,101,0,
|
||||
0,691,692,5,105,0,0,692,693,5,108,0,0,693,122,1,0,0,0,694,695,5,
|
||||
92,0,0,695,696,5,114,0,0,696,697,5,99,0,0,697,698,5,101,0,0,698,
|
||||
699,5,105,0,0,699,700,5,108,0,0,700,124,1,0,0,0,701,702,5,92,0,0,
|
||||
702,703,5,115,0,0,703,704,5,113,0,0,704,705,5,114,0,0,705,706,5,
|
||||
116,0,0,706,126,1,0,0,0,707,708,5,92,0,0,708,709,5,111,0,0,709,710,
|
||||
5,118,0,0,710,711,5,101,0,0,711,712,5,114,0,0,712,713,5,108,0,0,
|
||||
713,714,5,105,0,0,714,715,5,110,0,0,715,716,5,101,0,0,716,128,1,
|
||||
0,0,0,717,718,5,92,0,0,718,719,5,116,0,0,719,720,5,105,0,0,720,721,
|
||||
5,109,0,0,721,722,5,101,0,0,722,723,5,115,0,0,723,130,1,0,0,0,724,
|
||||
725,5,92,0,0,725,726,5,99,0,0,726,727,5,100,0,0,727,728,5,111,0,
|
||||
0,728,729,5,116,0,0,729,132,1,0,0,0,730,731,5,92,0,0,731,732,5,100,
|
||||
0,0,732,733,5,105,0,0,733,734,5,118,0,0,734,134,1,0,0,0,735,736,
|
||||
5,92,0,0,736,737,5,102,0,0,737,738,5,114,0,0,738,739,5,97,0,0,739,
|
||||
753,5,99,0,0,740,741,5,92,0,0,741,742,5,100,0,0,742,743,5,102,0,
|
||||
0,743,744,5,114,0,0,744,745,5,97,0,0,745,753,5,99,0,0,746,747,5,
|
||||
92,0,0,747,748,5,116,0,0,748,749,5,102,0,0,749,750,5,114,0,0,750,
|
||||
751,5,97,0,0,751,753,5,99,0,0,752,735,1,0,0,0,752,740,1,0,0,0,752,
|
||||
746,1,0,0,0,753,136,1,0,0,0,754,755,5,92,0,0,755,756,5,98,0,0,756,
|
||||
757,5,105,0,0,757,758,5,110,0,0,758,759,5,111,0,0,759,760,5,109,
|
||||
0,0,760,138,1,0,0,0,761,762,5,92,0,0,762,763,5,100,0,0,763,764,5,
|
||||
98,0,0,764,765,5,105,0,0,765,766,5,110,0,0,766,767,5,111,0,0,767,
|
||||
768,5,109,0,0,768,140,1,0,0,0,769,770,5,92,0,0,770,771,5,116,0,0,
|
||||
771,772,5,98,0,0,772,773,5,105,0,0,773,774,5,110,0,0,774,775,5,111,
|
||||
0,0,775,776,5,109,0,0,776,142,1,0,0,0,777,778,5,92,0,0,778,779,5,
|
||||
109,0,0,779,780,5,97,0,0,780,781,5,116,0,0,781,782,5,104,0,0,782,
|
||||
783,5,105,0,0,783,784,5,116,0,0,784,144,1,0,0,0,785,786,5,95,0,0,
|
||||
786,146,1,0,0,0,787,788,5,94,0,0,788,148,1,0,0,0,789,790,5,58,0,
|
||||
0,790,150,1,0,0,0,791,792,7,0,0,0,792,152,1,0,0,0,793,797,5,100,
|
||||
0,0,794,796,3,151,75,0,795,794,1,0,0,0,796,799,1,0,0,0,797,798,1,
|
||||
0,0,0,797,795,1,0,0,0,798,807,1,0,0,0,799,797,1,0,0,0,800,808,7,
|
||||
1,0,0,801,803,5,92,0,0,802,804,7,1,0,0,803,802,1,0,0,0,804,805,1,
|
||||
0,0,0,805,803,1,0,0,0,805,806,1,0,0,0,806,808,1,0,0,0,807,800,1,
|
||||
0,0,0,807,801,1,0,0,0,808,154,1,0,0,0,809,810,7,1,0,0,810,156,1,
|
||||
0,0,0,811,812,7,2,0,0,812,158,1,0,0,0,813,817,5,38,0,0,814,816,3,
|
||||
151,75,0,815,814,1,0,0,0,816,819,1,0,0,0,817,818,1,0,0,0,817,815,
|
||||
1,0,0,0,818,821,1,0,0,0,819,817,1,0,0,0,820,813,1,0,0,0,820,821,
|
||||
1,0,0,0,821,822,1,0,0,0,822,834,5,61,0,0,823,831,5,61,0,0,824,826,
|
||||
3,151,75,0,825,824,1,0,0,0,826,829,1,0,0,0,827,828,1,0,0,0,827,825,
|
||||
1,0,0,0,828,830,1,0,0,0,829,827,1,0,0,0,830,832,5,38,0,0,831,827,
|
||||
1,0,0,0,831,832,1,0,0,0,832,834,1,0,0,0,833,820,1,0,0,0,833,823,
|
||||
1,0,0,0,834,160,1,0,0,0,835,836,5,92,0,0,836,837,5,110,0,0,837,838,
|
||||
5,101,0,0,838,839,5,113,0,0,839,162,1,0,0,0,840,841,5,60,0,0,841,
|
||||
164,1,0,0,0,842,843,5,92,0,0,843,844,5,108,0,0,844,845,5,101,0,0,
|
||||
845,852,5,113,0,0,846,847,5,92,0,0,847,848,5,108,0,0,848,852,5,101,
|
||||
0,0,849,852,3,167,83,0,850,852,3,169,84,0,851,842,1,0,0,0,851,846,
|
||||
1,0,0,0,851,849,1,0,0,0,851,850,1,0,0,0,852,166,1,0,0,0,853,854,
|
||||
5,92,0,0,854,855,5,108,0,0,855,856,5,101,0,0,856,857,5,113,0,0,857,
|
||||
858,5,113,0,0,858,168,1,0,0,0,859,860,5,92,0,0,860,861,5,108,0,0,
|
||||
861,862,5,101,0,0,862,863,5,113,0,0,863,864,5,115,0,0,864,865,5,
|
||||
108,0,0,865,866,5,97,0,0,866,867,5,110,0,0,867,868,5,116,0,0,868,
|
||||
170,1,0,0,0,869,870,5,62,0,0,870,172,1,0,0,0,871,872,5,92,0,0,872,
|
||||
873,5,103,0,0,873,874,5,101,0,0,874,881,5,113,0,0,875,876,5,92,0,
|
||||
0,876,877,5,103,0,0,877,881,5,101,0,0,878,881,3,175,87,0,879,881,
|
||||
3,177,88,0,880,871,1,0,0,0,880,875,1,0,0,0,880,878,1,0,0,0,880,879,
|
||||
1,0,0,0,881,174,1,0,0,0,882,883,5,92,0,0,883,884,5,103,0,0,884,885,
|
||||
5,101,0,0,885,886,5,113,0,0,886,887,5,113,0,0,887,176,1,0,0,0,888,
|
||||
889,5,92,0,0,889,890,5,103,0,0,890,891,5,101,0,0,891,892,5,113,0,
|
||||
0,892,893,5,115,0,0,893,894,5,108,0,0,894,895,5,97,0,0,895,896,5,
|
||||
110,0,0,896,897,5,116,0,0,897,178,1,0,0,0,898,899,5,33,0,0,899,180,
|
||||
1,0,0,0,900,902,5,39,0,0,901,900,1,0,0,0,902,903,1,0,0,0,903,901,
|
||||
1,0,0,0,903,904,1,0,0,0,904,182,1,0,0,0,905,907,5,92,0,0,906,908,
|
||||
7,1,0,0,907,906,1,0,0,0,908,909,1,0,0,0,909,907,1,0,0,0,909,910,
|
||||
1,0,0,0,910,184,1,0,0,0,22,0,192,208,223,240,276,380,503,520,752,
|
||||
797,805,807,817,820,827,831,833,851,880,903,909,1,6,0,0
|
||||
]
|
||||
|
||||
class LaTeXLexer(Lexer):
|
||||
|
||||
atn = ATNDeserializer().deserialize(serializedATN())
|
||||
|
||||
decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ]
|
||||
|
||||
T__0 = 1
|
||||
T__1 = 2
|
||||
WS = 3
|
||||
THINSPACE = 4
|
||||
MEDSPACE = 5
|
||||
THICKSPACE = 6
|
||||
QUAD = 7
|
||||
QQUAD = 8
|
||||
NEGTHINSPACE = 9
|
||||
NEGMEDSPACE = 10
|
||||
NEGTHICKSPACE = 11
|
||||
CMD_LEFT = 12
|
||||
CMD_RIGHT = 13
|
||||
IGNORE = 14
|
||||
ADD = 15
|
||||
SUB = 16
|
||||
MUL = 17
|
||||
DIV = 18
|
||||
L_PAREN = 19
|
||||
R_PAREN = 20
|
||||
L_BRACE = 21
|
||||
R_BRACE = 22
|
||||
L_BRACE_LITERAL = 23
|
||||
R_BRACE_LITERAL = 24
|
||||
L_BRACKET = 25
|
||||
R_BRACKET = 26
|
||||
BAR = 27
|
||||
R_BAR = 28
|
||||
L_BAR = 29
|
||||
L_ANGLE = 30
|
||||
R_ANGLE = 31
|
||||
FUNC_LIM = 32
|
||||
LIM_APPROACH_SYM = 33
|
||||
FUNC_INT = 34
|
||||
FUNC_SUM = 35
|
||||
FUNC_PROD = 36
|
||||
FUNC_EXP = 37
|
||||
FUNC_LOG = 38
|
||||
FUNC_LG = 39
|
||||
FUNC_LN = 40
|
||||
FUNC_SIN = 41
|
||||
FUNC_COS = 42
|
||||
FUNC_TAN = 43
|
||||
FUNC_CSC = 44
|
||||
FUNC_SEC = 45
|
||||
FUNC_COT = 46
|
||||
FUNC_ARCSIN = 47
|
||||
FUNC_ARCCOS = 48
|
||||
FUNC_ARCTAN = 49
|
||||
FUNC_ARCCSC = 50
|
||||
FUNC_ARCSEC = 51
|
||||
FUNC_ARCCOT = 52
|
||||
FUNC_SINH = 53
|
||||
FUNC_COSH = 54
|
||||
FUNC_TANH = 55
|
||||
FUNC_ARSINH = 56
|
||||
FUNC_ARCOSH = 57
|
||||
FUNC_ARTANH = 58
|
||||
L_FLOOR = 59
|
||||
R_FLOOR = 60
|
||||
L_CEIL = 61
|
||||
R_CEIL = 62
|
||||
FUNC_SQRT = 63
|
||||
FUNC_OVERLINE = 64
|
||||
CMD_TIMES = 65
|
||||
CMD_CDOT = 66
|
||||
CMD_DIV = 67
|
||||
CMD_FRAC = 68
|
||||
CMD_BINOM = 69
|
||||
CMD_DBINOM = 70
|
||||
CMD_TBINOM = 71
|
||||
CMD_MATHIT = 72
|
||||
UNDERSCORE = 73
|
||||
CARET = 74
|
||||
COLON = 75
|
||||
DIFFERENTIAL = 76
|
||||
LETTER = 77
|
||||
DIGIT = 78
|
||||
EQUAL = 79
|
||||
NEQ = 80
|
||||
LT = 81
|
||||
LTE = 82
|
||||
LTE_Q = 83
|
||||
LTE_S = 84
|
||||
GT = 85
|
||||
GTE = 86
|
||||
GTE_Q = 87
|
||||
GTE_S = 88
|
||||
BANG = 89
|
||||
SINGLE_QUOTES = 90
|
||||
SYMBOL = 91
|
||||
|
||||
channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN" ]
|
||||
|
||||
modeNames = [ "DEFAULT_MODE" ]
|
||||
|
||||
literalNames = [ "<INVALID>",
|
||||
"','", "'.'", "'\\quad'", "'\\qquad'", "'\\negmedspace'", "'\\negthickspace'",
|
||||
"'\\left'", "'\\right'", "'+'", "'-'", "'*'", "'/'", "'('",
|
||||
"')'", "'{'", "'}'", "'\\{'", "'\\}'", "'['", "']'", "'|'",
|
||||
"'\\right|'", "'\\left|'", "'\\langle'", "'\\rangle'", "'\\lim'",
|
||||
"'\\sum'", "'\\prod'", "'\\exp'", "'\\log'", "'\\lg'", "'\\ln'",
|
||||
"'\\sin'", "'\\cos'", "'\\tan'", "'\\csc'", "'\\sec'", "'\\cot'",
|
||||
"'\\arcsin'", "'\\arccos'", "'\\arctan'", "'\\arccsc'", "'\\arcsec'",
|
||||
"'\\arccot'", "'\\sinh'", "'\\cosh'", "'\\tanh'", "'\\arsinh'",
|
||||
"'\\arcosh'", "'\\artanh'", "'\\lfloor'", "'\\rfloor'", "'\\lceil'",
|
||||
"'\\rceil'", "'\\sqrt'", "'\\overline'", "'\\times'", "'\\cdot'",
|
||||
"'\\div'", "'\\binom'", "'\\dbinom'", "'\\tbinom'", "'\\mathit'",
|
||||
"'_'", "'^'", "':'", "'\\neq'", "'<'", "'\\leqq'", "'\\leqslant'",
|
||||
"'>'", "'\\geqq'", "'\\geqslant'", "'!'" ]
|
||||
|
||||
symbolicNames = [ "<INVALID>",
|
||||
"WS", "THINSPACE", "MEDSPACE", "THICKSPACE", "QUAD", "QQUAD",
|
||||
"NEGTHINSPACE", "NEGMEDSPACE", "NEGTHICKSPACE", "CMD_LEFT",
|
||||
"CMD_RIGHT", "IGNORE", "ADD", "SUB", "MUL", "DIV", "L_PAREN",
|
||||
"R_PAREN", "L_BRACE", "R_BRACE", "L_BRACE_LITERAL", "R_BRACE_LITERAL",
|
||||
"L_BRACKET", "R_BRACKET", "BAR", "R_BAR", "L_BAR", "L_ANGLE",
|
||||
"R_ANGLE", "FUNC_LIM", "LIM_APPROACH_SYM", "FUNC_INT", "FUNC_SUM",
|
||||
"FUNC_PROD", "FUNC_EXP", "FUNC_LOG", "FUNC_LG", "FUNC_LN", "FUNC_SIN",
|
||||
"FUNC_COS", "FUNC_TAN", "FUNC_CSC", "FUNC_SEC", "FUNC_COT",
|
||||
"FUNC_ARCSIN", "FUNC_ARCCOS", "FUNC_ARCTAN", "FUNC_ARCCSC",
|
||||
"FUNC_ARCSEC", "FUNC_ARCCOT", "FUNC_SINH", "FUNC_COSH", "FUNC_TANH",
|
||||
"FUNC_ARSINH", "FUNC_ARCOSH", "FUNC_ARTANH", "L_FLOOR", "R_FLOOR",
|
||||
"L_CEIL", "R_CEIL", "FUNC_SQRT", "FUNC_OVERLINE", "CMD_TIMES",
|
||||
"CMD_CDOT", "CMD_DIV", "CMD_FRAC", "CMD_BINOM", "CMD_DBINOM",
|
||||
"CMD_TBINOM", "CMD_MATHIT", "UNDERSCORE", "CARET", "COLON",
|
||||
"DIFFERENTIAL", "LETTER", "DIGIT", "EQUAL", "NEQ", "LT", "LTE",
|
||||
"LTE_Q", "LTE_S", "GT", "GTE", "GTE_Q", "GTE_S", "BANG", "SINGLE_QUOTES",
|
||||
"SYMBOL" ]
|
||||
|
||||
ruleNames = [ "T__0", "T__1", "WS", "THINSPACE", "MEDSPACE", "THICKSPACE",
|
||||
"QUAD", "QQUAD", "NEGTHINSPACE", "NEGMEDSPACE", "NEGTHICKSPACE",
|
||||
"CMD_LEFT", "CMD_RIGHT", "IGNORE", "ADD", "SUB", "MUL",
|
||||
"DIV", "L_PAREN", "R_PAREN", "L_BRACE", "R_BRACE", "L_BRACE_LITERAL",
|
||||
"R_BRACE_LITERAL", "L_BRACKET", "R_BRACKET", "BAR", "R_BAR",
|
||||
"L_BAR", "L_ANGLE", "R_ANGLE", "FUNC_LIM", "LIM_APPROACH_SYM",
|
||||
"FUNC_INT", "FUNC_SUM", "FUNC_PROD", "FUNC_EXP", "FUNC_LOG",
|
||||
"FUNC_LG", "FUNC_LN", "FUNC_SIN", "FUNC_COS", "FUNC_TAN",
|
||||
"FUNC_CSC", "FUNC_SEC", "FUNC_COT", "FUNC_ARCSIN", "FUNC_ARCCOS",
|
||||
"FUNC_ARCTAN", "FUNC_ARCCSC", "FUNC_ARCSEC", "FUNC_ARCCOT",
|
||||
"FUNC_SINH", "FUNC_COSH", "FUNC_TANH", "FUNC_ARSINH",
|
||||
"FUNC_ARCOSH", "FUNC_ARTANH", "L_FLOOR", "R_FLOOR", "L_CEIL",
|
||||
"R_CEIL", "FUNC_SQRT", "FUNC_OVERLINE", "CMD_TIMES", "CMD_CDOT",
|
||||
"CMD_DIV", "CMD_FRAC", "CMD_BINOM", "CMD_DBINOM", "CMD_TBINOM",
|
||||
"CMD_MATHIT", "UNDERSCORE", "CARET", "COLON", "WS_CHAR",
|
||||
"DIFFERENTIAL", "LETTER", "DIGIT", "EQUAL", "NEQ", "LT",
|
||||
"LTE", "LTE_Q", "LTE_S", "GT", "GTE", "GTE_Q", "GTE_S",
|
||||
"BANG", "SINGLE_QUOTES", "SYMBOL" ]
|
||||
|
||||
grammarFileName = "LaTeX.g4"
|
||||
|
||||
def __init__(self, input=None, output:TextIO = sys.stdout):
|
||||
super().__init__(input, output)
|
||||
self.checkVersion("4.11.1")
|
||||
self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache())
|
||||
self._actions = None
|
||||
self._predicates = None
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,91 @@
|
||||
import os
|
||||
import subprocess
|
||||
import glob
|
||||
|
||||
from sympy.utilities.misc import debug
|
||||
|
||||
here = os.path.dirname(__file__)
|
||||
grammar_file = os.path.abspath(os.path.join(here, "LaTeX.g4"))
|
||||
dir_latex_antlr = os.path.join(here, "_antlr")
|
||||
|
||||
header = '''\
|
||||
# *** GENERATED BY `setup.py antlr`, DO NOT EDIT BY HAND ***
|
||||
#
|
||||
# Generated from ../LaTeX.g4, derived from latex2sympy
|
||||
# latex2sympy is licensed under the MIT license
|
||||
# https://github.com/augustt198/latex2sympy/blob/master/LICENSE.txt
|
||||
#
|
||||
# Generated with antlr4
|
||||
# antlr4 is licensed under the BSD-3-Clause License
|
||||
# https://github.com/antlr/antlr4/blob/master/LICENSE.txt
|
||||
'''
|
||||
|
||||
|
||||
def check_antlr_version():
|
||||
debug("Checking antlr4 version...")
|
||||
|
||||
try:
|
||||
debug(subprocess.check_output(["antlr4"])
|
||||
.decode('utf-8').split("\n")[0])
|
||||
return True
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
debug("The 'antlr4' command line tool is not installed, "
|
||||
"or not on your PATH.\n"
|
||||
"> Please refer to the README.md file for more information.")
|
||||
return False
|
||||
|
||||
|
||||
def build_parser(output_dir=dir_latex_antlr):
|
||||
check_antlr_version()
|
||||
|
||||
debug("Updating ANTLR-generated code in {}".format(output_dir))
|
||||
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
with open(os.path.join(output_dir, "__init__.py"), "w+") as fp:
|
||||
fp.write(header)
|
||||
|
||||
args = [
|
||||
"antlr4",
|
||||
grammar_file,
|
||||
"-o", output_dir,
|
||||
# for now, not generating these as latex2sympy did not use them
|
||||
"-no-visitor",
|
||||
"-no-listener",
|
||||
]
|
||||
|
||||
debug("Running code generation...\n\t$ {}".format(" ".join(args)))
|
||||
subprocess.check_output(args, cwd=output_dir)
|
||||
|
||||
debug("Applying headers, removing unnecessary files and renaming...")
|
||||
# Handle case insensitive file systems. If the files are already
|
||||
# generated, they will be written to latex* but LaTeX*.* won't match them.
|
||||
for path in (glob.glob(os.path.join(output_dir, "LaTeX*.*")) or
|
||||
glob.glob(os.path.join(output_dir, "latex*.*"))):
|
||||
|
||||
# Remove files ending in .interp or .tokens as they are not needed.
|
||||
if not path.endswith(".py"):
|
||||
os.unlink(path)
|
||||
continue
|
||||
|
||||
new_path = os.path.join(output_dir, os.path.basename(path).lower())
|
||||
with open(path, 'r') as f:
|
||||
lines = [line.rstrip() + '\n' for line in f]
|
||||
|
||||
os.unlink(path)
|
||||
|
||||
with open(new_path, "w") as out_file:
|
||||
offset = 0
|
||||
while lines[offset].startswith('#'):
|
||||
offset += 1
|
||||
out_file.write(header)
|
||||
out_file.writelines(lines[offset:])
|
||||
|
||||
debug("\t{}".format(new_path))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
build_parser()
|
||||
@@ -0,0 +1,607 @@
|
||||
# Ported from latex2sympy by @augustt198
|
||||
# https://github.com/augustt198/latex2sympy
|
||||
# See license in LICENSE.txt
|
||||
from importlib.metadata import version
|
||||
import sympy
|
||||
from sympy.external import import_module
|
||||
from sympy.printing.str import StrPrinter
|
||||
from sympy.physics.quantum.state import Bra, Ket
|
||||
|
||||
from .errors import LaTeXParsingError
|
||||
|
||||
|
||||
LaTeXParser = LaTeXLexer = MathErrorListener = None
|
||||
|
||||
try:
|
||||
LaTeXParser = import_module('sympy.parsing.latex._antlr.latexparser',
|
||||
import_kwargs={'fromlist': ['LaTeXParser']}).LaTeXParser
|
||||
LaTeXLexer = import_module('sympy.parsing.latex._antlr.latexlexer',
|
||||
import_kwargs={'fromlist': ['LaTeXLexer']}).LaTeXLexer
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
ErrorListener = import_module('antlr4.error.ErrorListener',
|
||||
warn_not_installed=True,
|
||||
import_kwargs={'fromlist': ['ErrorListener']}
|
||||
)
|
||||
|
||||
|
||||
|
||||
if ErrorListener:
|
||||
class MathErrorListener(ErrorListener.ErrorListener): # type:ignore # noqa:F811
|
||||
def __init__(self, src):
|
||||
super(ErrorListener.ErrorListener, self).__init__()
|
||||
self.src = src
|
||||
|
||||
def syntaxError(self, recog, symbol, line, col, msg, e):
|
||||
fmt = "%s\n%s\n%s"
|
||||
marker = "~" * col + "^"
|
||||
|
||||
if msg.startswith("missing"):
|
||||
err = fmt % (msg, self.src, marker)
|
||||
elif msg.startswith("no viable"):
|
||||
err = fmt % ("I expected something else here", self.src, marker)
|
||||
elif msg.startswith("mismatched"):
|
||||
names = LaTeXParser.literalNames
|
||||
expected = [
|
||||
names[i] for i in e.getExpectedTokens() if i < len(names)
|
||||
]
|
||||
if len(expected) < 10:
|
||||
expected = " ".join(expected)
|
||||
err = (fmt % ("I expected one of these: " + expected, self.src,
|
||||
marker))
|
||||
else:
|
||||
err = (fmt % ("I expected something else here", self.src,
|
||||
marker))
|
||||
else:
|
||||
err = fmt % ("I don't understand this", self.src, marker)
|
||||
raise LaTeXParsingError(err)
|
||||
|
||||
|
||||
def parse_latex(sympy, strict=False):
|
||||
antlr4 = import_module('antlr4')
|
||||
|
||||
if None in [antlr4, MathErrorListener] or \
|
||||
not version('antlr4-python3-runtime').startswith('4.11'):
|
||||
raise ImportError("LaTeX parsing requires the antlr4 Python package,"
|
||||
" provided by pip (antlr4-python3-runtime) or"
|
||||
" conda (antlr-python-runtime), version 4.11")
|
||||
|
||||
sympy = sympy.strip()
|
||||
matherror = MathErrorListener(sympy)
|
||||
|
||||
stream = antlr4.InputStream(sympy)
|
||||
lex = LaTeXLexer(stream)
|
||||
lex.removeErrorListeners()
|
||||
lex.addErrorListener(matherror)
|
||||
|
||||
tokens = antlr4.CommonTokenStream(lex)
|
||||
parser = LaTeXParser(tokens)
|
||||
|
||||
# remove default console error listener
|
||||
parser.removeErrorListeners()
|
||||
parser.addErrorListener(matherror)
|
||||
|
||||
relation = parser.math().relation()
|
||||
if strict and (relation.start.start != 0 or relation.stop.stop != len(sympy) - 1):
|
||||
raise LaTeXParsingError("Invalid LaTeX")
|
||||
expr = convert_relation(relation)
|
||||
|
||||
return expr
|
||||
|
||||
|
||||
def convert_relation(rel):
|
||||
if rel.expr():
|
||||
return convert_expr(rel.expr())
|
||||
|
||||
lh = convert_relation(rel.relation(0))
|
||||
rh = convert_relation(rel.relation(1))
|
||||
if rel.LT():
|
||||
return sympy.StrictLessThan(lh, rh)
|
||||
elif rel.LTE():
|
||||
return sympy.LessThan(lh, rh)
|
||||
elif rel.GT():
|
||||
return sympy.StrictGreaterThan(lh, rh)
|
||||
elif rel.GTE():
|
||||
return sympy.GreaterThan(lh, rh)
|
||||
elif rel.EQUAL():
|
||||
return sympy.Eq(lh, rh)
|
||||
elif rel.NEQ():
|
||||
return sympy.Ne(lh, rh)
|
||||
|
||||
|
||||
def convert_expr(expr):
|
||||
return convert_add(expr.additive())
|
||||
|
||||
|
||||
def convert_add(add):
|
||||
if add.ADD():
|
||||
lh = convert_add(add.additive(0))
|
||||
rh = convert_add(add.additive(1))
|
||||
return sympy.Add(lh, rh, evaluate=False)
|
||||
elif add.SUB():
|
||||
lh = convert_add(add.additive(0))
|
||||
rh = convert_add(add.additive(1))
|
||||
if hasattr(rh, "is_Atom") and rh.is_Atom:
|
||||
return sympy.Add(lh, -1 * rh, evaluate=False)
|
||||
return sympy.Add(lh, sympy.Mul(-1, rh, evaluate=False), evaluate=False)
|
||||
else:
|
||||
return convert_mp(add.mp())
|
||||
|
||||
|
||||
def convert_mp(mp):
|
||||
if hasattr(mp, 'mp'):
|
||||
mp_left = mp.mp(0)
|
||||
mp_right = mp.mp(1)
|
||||
else:
|
||||
mp_left = mp.mp_nofunc(0)
|
||||
mp_right = mp.mp_nofunc(1)
|
||||
|
||||
if mp.MUL() or mp.CMD_TIMES() or mp.CMD_CDOT():
|
||||
lh = convert_mp(mp_left)
|
||||
rh = convert_mp(mp_right)
|
||||
return sympy.Mul(lh, rh, evaluate=False)
|
||||
elif mp.DIV() or mp.CMD_DIV() or mp.COLON():
|
||||
lh = convert_mp(mp_left)
|
||||
rh = convert_mp(mp_right)
|
||||
return sympy.Mul(lh, sympy.Pow(rh, -1, evaluate=False), evaluate=False)
|
||||
else:
|
||||
if hasattr(mp, 'unary'):
|
||||
return convert_unary(mp.unary())
|
||||
else:
|
||||
return convert_unary(mp.unary_nofunc())
|
||||
|
||||
|
||||
def convert_unary(unary):
|
||||
if hasattr(unary, 'unary'):
|
||||
nested_unary = unary.unary()
|
||||
else:
|
||||
nested_unary = unary.unary_nofunc()
|
||||
if hasattr(unary, 'postfix_nofunc'):
|
||||
first = unary.postfix()
|
||||
tail = unary.postfix_nofunc()
|
||||
postfix = [first] + tail
|
||||
else:
|
||||
postfix = unary.postfix()
|
||||
|
||||
if unary.ADD():
|
||||
return convert_unary(nested_unary)
|
||||
elif unary.SUB():
|
||||
numabs = convert_unary(nested_unary)
|
||||
# Use Integer(-n) instead of Mul(-1, n)
|
||||
return -numabs
|
||||
elif postfix:
|
||||
return convert_postfix_list(postfix)
|
||||
|
||||
|
||||
def convert_postfix_list(arr, i=0):
|
||||
if i >= len(arr):
|
||||
raise LaTeXParsingError("Index out of bounds")
|
||||
|
||||
res = convert_postfix(arr[i])
|
||||
if isinstance(res, sympy.Expr):
|
||||
if i == len(arr) - 1:
|
||||
return res # nothing to multiply by
|
||||
else:
|
||||
if i > 0:
|
||||
left = convert_postfix(arr[i - 1])
|
||||
right = convert_postfix(arr[i + 1])
|
||||
if isinstance(left, sympy.Expr) and isinstance(
|
||||
right, sympy.Expr):
|
||||
left_syms = convert_postfix(arr[i - 1]).atoms(sympy.Symbol)
|
||||
right_syms = convert_postfix(arr[i + 1]).atoms(
|
||||
sympy.Symbol)
|
||||
# if the left and right sides contain no variables and the
|
||||
# symbol in between is 'x', treat as multiplication.
|
||||
if not (left_syms or right_syms) and str(res) == 'x':
|
||||
return convert_postfix_list(arr, i + 1)
|
||||
# multiply by next
|
||||
return sympy.Mul(
|
||||
res, convert_postfix_list(arr, i + 1), evaluate=False)
|
||||
else: # must be derivative
|
||||
wrt = res[0]
|
||||
if i == len(arr) - 1:
|
||||
raise LaTeXParsingError("Expected expression for derivative")
|
||||
else:
|
||||
expr = convert_postfix_list(arr, i + 1)
|
||||
return sympy.Derivative(expr, wrt)
|
||||
|
||||
|
||||
def do_subs(expr, at):
|
||||
if at.expr():
|
||||
at_expr = convert_expr(at.expr())
|
||||
syms = at_expr.atoms(sympy.Symbol)
|
||||
if len(syms) == 0:
|
||||
return expr
|
||||
elif len(syms) > 0:
|
||||
sym = next(iter(syms))
|
||||
return expr.subs(sym, at_expr)
|
||||
elif at.equality():
|
||||
lh = convert_expr(at.equality().expr(0))
|
||||
rh = convert_expr(at.equality().expr(1))
|
||||
return expr.subs(lh, rh)
|
||||
|
||||
|
||||
def convert_postfix(postfix):
|
||||
if hasattr(postfix, 'exp'):
|
||||
exp_nested = postfix.exp()
|
||||
else:
|
||||
exp_nested = postfix.exp_nofunc()
|
||||
|
||||
exp = convert_exp(exp_nested)
|
||||
for op in postfix.postfix_op():
|
||||
if op.BANG():
|
||||
if isinstance(exp, list):
|
||||
raise LaTeXParsingError("Cannot apply postfix to derivative")
|
||||
exp = sympy.factorial(exp, evaluate=False)
|
||||
elif op.eval_at():
|
||||
ev = op.eval_at()
|
||||
at_b = None
|
||||
at_a = None
|
||||
if ev.eval_at_sup():
|
||||
at_b = do_subs(exp, ev.eval_at_sup())
|
||||
if ev.eval_at_sub():
|
||||
at_a = do_subs(exp, ev.eval_at_sub())
|
||||
if at_b is not None and at_a is not None:
|
||||
exp = sympy.Add(at_b, -1 * at_a, evaluate=False)
|
||||
elif at_b is not None:
|
||||
exp = at_b
|
||||
elif at_a is not None:
|
||||
exp = at_a
|
||||
|
||||
return exp
|
||||
|
||||
|
||||
def convert_exp(exp):
|
||||
if hasattr(exp, 'exp'):
|
||||
exp_nested = exp.exp()
|
||||
else:
|
||||
exp_nested = exp.exp_nofunc()
|
||||
|
||||
if exp_nested:
|
||||
base = convert_exp(exp_nested)
|
||||
if isinstance(base, list):
|
||||
raise LaTeXParsingError("Cannot raise derivative to power")
|
||||
if exp.atom():
|
||||
exponent = convert_atom(exp.atom())
|
||||
elif exp.expr():
|
||||
exponent = convert_expr(exp.expr())
|
||||
return sympy.Pow(base, exponent, evaluate=False)
|
||||
else:
|
||||
if hasattr(exp, 'comp'):
|
||||
return convert_comp(exp.comp())
|
||||
else:
|
||||
return convert_comp(exp.comp_nofunc())
|
||||
|
||||
|
||||
def convert_comp(comp):
|
||||
if comp.group():
|
||||
return convert_expr(comp.group().expr())
|
||||
elif comp.abs_group():
|
||||
return sympy.Abs(convert_expr(comp.abs_group().expr()), evaluate=False)
|
||||
elif comp.atom():
|
||||
return convert_atom(comp.atom())
|
||||
elif comp.floor():
|
||||
return convert_floor(comp.floor())
|
||||
elif comp.ceil():
|
||||
return convert_ceil(comp.ceil())
|
||||
elif comp.func():
|
||||
return convert_func(comp.func())
|
||||
|
||||
|
||||
def convert_atom(atom):
|
||||
if atom.LETTER():
|
||||
sname = atom.LETTER().getText()
|
||||
if atom.subexpr():
|
||||
if atom.subexpr().expr(): # subscript is expr
|
||||
subscript = convert_expr(atom.subexpr().expr())
|
||||
else: # subscript is atom
|
||||
subscript = convert_atom(atom.subexpr().atom())
|
||||
sname += '_{' + StrPrinter().doprint(subscript) + '}'
|
||||
if atom.SINGLE_QUOTES():
|
||||
sname += atom.SINGLE_QUOTES().getText() # put after subscript for easy identify
|
||||
return sympy.Symbol(sname)
|
||||
elif atom.SYMBOL():
|
||||
s = atom.SYMBOL().getText()[1:]
|
||||
if s == "infty":
|
||||
return sympy.oo
|
||||
else:
|
||||
if atom.subexpr():
|
||||
subscript = None
|
||||
if atom.subexpr().expr(): # subscript is expr
|
||||
subscript = convert_expr(atom.subexpr().expr())
|
||||
else: # subscript is atom
|
||||
subscript = convert_atom(atom.subexpr().atom())
|
||||
subscriptName = StrPrinter().doprint(subscript)
|
||||
s += '_{' + subscriptName + '}'
|
||||
return sympy.Symbol(s)
|
||||
elif atom.number():
|
||||
s = atom.number().getText().replace(",", "")
|
||||
return sympy.Number(s)
|
||||
elif atom.DIFFERENTIAL():
|
||||
var = get_differential_var(atom.DIFFERENTIAL())
|
||||
return sympy.Symbol('d' + var.name)
|
||||
elif atom.mathit():
|
||||
text = rule2text(atom.mathit().mathit_text())
|
||||
return sympy.Symbol(text)
|
||||
elif atom.frac():
|
||||
return convert_frac(atom.frac())
|
||||
elif atom.binom():
|
||||
return convert_binom(atom.binom())
|
||||
elif atom.bra():
|
||||
val = convert_expr(atom.bra().expr())
|
||||
return Bra(val)
|
||||
elif atom.ket():
|
||||
val = convert_expr(atom.ket().expr())
|
||||
return Ket(val)
|
||||
|
||||
|
||||
def rule2text(ctx):
|
||||
stream = ctx.start.getInputStream()
|
||||
# starting index of starting token
|
||||
startIdx = ctx.start.start
|
||||
# stopping index of stopping token
|
||||
stopIdx = ctx.stop.stop
|
||||
|
||||
return stream.getText(startIdx, stopIdx)
|
||||
|
||||
|
||||
def convert_frac(frac):
|
||||
diff_op = False
|
||||
partial_op = False
|
||||
if frac.lower and frac.upper:
|
||||
lower_itv = frac.lower.getSourceInterval()
|
||||
lower_itv_len = lower_itv[1] - lower_itv[0] + 1
|
||||
if (frac.lower.start == frac.lower.stop
|
||||
and frac.lower.start.type == LaTeXLexer.DIFFERENTIAL):
|
||||
wrt = get_differential_var_str(frac.lower.start.text)
|
||||
diff_op = True
|
||||
elif (lower_itv_len == 2 and frac.lower.start.type == LaTeXLexer.SYMBOL
|
||||
and frac.lower.start.text == '\\partial'
|
||||
and (frac.lower.stop.type == LaTeXLexer.LETTER
|
||||
or frac.lower.stop.type == LaTeXLexer.SYMBOL)):
|
||||
partial_op = True
|
||||
wrt = frac.lower.stop.text
|
||||
if frac.lower.stop.type == LaTeXLexer.SYMBOL:
|
||||
wrt = wrt[1:]
|
||||
|
||||
if diff_op or partial_op:
|
||||
wrt = sympy.Symbol(wrt)
|
||||
if (diff_op and frac.upper.start == frac.upper.stop
|
||||
and frac.upper.start.type == LaTeXLexer.LETTER
|
||||
and frac.upper.start.text == 'd'):
|
||||
return [wrt]
|
||||
elif (partial_op and frac.upper.start == frac.upper.stop
|
||||
and frac.upper.start.type == LaTeXLexer.SYMBOL
|
||||
and frac.upper.start.text == '\\partial'):
|
||||
return [wrt]
|
||||
upper_text = rule2text(frac.upper)
|
||||
|
||||
expr_top = None
|
||||
if diff_op and upper_text.startswith('d'):
|
||||
expr_top = parse_latex(upper_text[1:])
|
||||
elif partial_op and frac.upper.start.text == '\\partial':
|
||||
expr_top = parse_latex(upper_text[len('\\partial'):])
|
||||
if expr_top:
|
||||
return sympy.Derivative(expr_top, wrt)
|
||||
if frac.upper:
|
||||
expr_top = convert_expr(frac.upper)
|
||||
else:
|
||||
expr_top = sympy.Number(frac.upperd.text)
|
||||
if frac.lower:
|
||||
expr_bot = convert_expr(frac.lower)
|
||||
else:
|
||||
expr_bot = sympy.Number(frac.lowerd.text)
|
||||
inverse_denom = sympy.Pow(expr_bot, -1, evaluate=False)
|
||||
if expr_top == 1:
|
||||
return inverse_denom
|
||||
else:
|
||||
return sympy.Mul(expr_top, inverse_denom, evaluate=False)
|
||||
|
||||
def convert_binom(binom):
|
||||
expr_n = convert_expr(binom.n)
|
||||
expr_k = convert_expr(binom.k)
|
||||
return sympy.binomial(expr_n, expr_k, evaluate=False)
|
||||
|
||||
def convert_floor(floor):
|
||||
val = convert_expr(floor.val)
|
||||
return sympy.floor(val, evaluate=False)
|
||||
|
||||
def convert_ceil(ceil):
|
||||
val = convert_expr(ceil.val)
|
||||
return sympy.ceiling(val, evaluate=False)
|
||||
|
||||
def convert_func(func):
|
||||
if func.func_normal():
|
||||
if func.L_PAREN(): # function called with parenthesis
|
||||
arg = convert_func_arg(func.func_arg())
|
||||
else:
|
||||
arg = convert_func_arg(func.func_arg_noparens())
|
||||
|
||||
name = func.func_normal().start.text[1:]
|
||||
|
||||
# change arc<trig> -> a<trig>
|
||||
if name in [
|
||||
"arcsin", "arccos", "arctan", "arccsc", "arcsec", "arccot"
|
||||
]:
|
||||
name = "a" + name[3:]
|
||||
expr = getattr(sympy.functions, name)(arg, evaluate=False)
|
||||
if name in ["arsinh", "arcosh", "artanh"]:
|
||||
name = "a" + name[2:]
|
||||
expr = getattr(sympy.functions, name)(arg, evaluate=False)
|
||||
|
||||
if name == "exp":
|
||||
expr = sympy.exp(arg, evaluate=False)
|
||||
|
||||
if name in ("log", "lg", "ln"):
|
||||
if func.subexpr():
|
||||
if func.subexpr().expr():
|
||||
base = convert_expr(func.subexpr().expr())
|
||||
else:
|
||||
base = convert_atom(func.subexpr().atom())
|
||||
elif name == "lg": # ISO 80000-2:2019
|
||||
base = 10
|
||||
elif name in ("ln", "log"): # SymPy's latex printer prints ln as log by default
|
||||
base = sympy.E
|
||||
expr = sympy.log(arg, base, evaluate=False)
|
||||
|
||||
func_pow = None
|
||||
should_pow = True
|
||||
if func.supexpr():
|
||||
if func.supexpr().expr():
|
||||
func_pow = convert_expr(func.supexpr().expr())
|
||||
else:
|
||||
func_pow = convert_atom(func.supexpr().atom())
|
||||
|
||||
if name in [
|
||||
"sin", "cos", "tan", "csc", "sec", "cot", "sinh", "cosh",
|
||||
"tanh"
|
||||
]:
|
||||
if func_pow == -1:
|
||||
name = "a" + name
|
||||
should_pow = False
|
||||
expr = getattr(sympy.functions, name)(arg, evaluate=False)
|
||||
|
||||
if func_pow and should_pow:
|
||||
expr = sympy.Pow(expr, func_pow, evaluate=False)
|
||||
|
||||
return expr
|
||||
elif func.LETTER() or func.SYMBOL():
|
||||
if func.LETTER():
|
||||
fname = func.LETTER().getText()
|
||||
elif func.SYMBOL():
|
||||
fname = func.SYMBOL().getText()[1:]
|
||||
fname = str(fname) # can't be unicode
|
||||
if func.subexpr():
|
||||
if func.subexpr().expr(): # subscript is expr
|
||||
subscript = convert_expr(func.subexpr().expr())
|
||||
else: # subscript is atom
|
||||
subscript = convert_atom(func.subexpr().atom())
|
||||
subscriptName = StrPrinter().doprint(subscript)
|
||||
fname += '_{' + subscriptName + '}'
|
||||
if func.SINGLE_QUOTES():
|
||||
fname += func.SINGLE_QUOTES().getText()
|
||||
input_args = func.args()
|
||||
output_args = []
|
||||
while input_args.args(): # handle multiple arguments to function
|
||||
output_args.append(convert_expr(input_args.expr()))
|
||||
input_args = input_args.args()
|
||||
output_args.append(convert_expr(input_args.expr()))
|
||||
return sympy.Function(fname)(*output_args)
|
||||
elif func.FUNC_INT():
|
||||
return handle_integral(func)
|
||||
elif func.FUNC_SQRT():
|
||||
expr = convert_expr(func.base)
|
||||
if func.root:
|
||||
r = convert_expr(func.root)
|
||||
return sympy.root(expr, r, evaluate=False)
|
||||
else:
|
||||
return sympy.sqrt(expr, evaluate=False)
|
||||
elif func.FUNC_OVERLINE():
|
||||
expr = convert_expr(func.base)
|
||||
return sympy.conjugate(expr, evaluate=False)
|
||||
elif func.FUNC_SUM():
|
||||
return handle_sum_or_prod(func, "summation")
|
||||
elif func.FUNC_PROD():
|
||||
return handle_sum_or_prod(func, "product")
|
||||
elif func.FUNC_LIM():
|
||||
return handle_limit(func)
|
||||
|
||||
|
||||
def convert_func_arg(arg):
|
||||
if hasattr(arg, 'expr'):
|
||||
return convert_expr(arg.expr())
|
||||
else:
|
||||
return convert_mp(arg.mp_nofunc())
|
||||
|
||||
|
||||
def handle_integral(func):
|
||||
if func.additive():
|
||||
integrand = convert_add(func.additive())
|
||||
elif func.frac():
|
||||
integrand = convert_frac(func.frac())
|
||||
else:
|
||||
integrand = 1
|
||||
|
||||
int_var = None
|
||||
if func.DIFFERENTIAL():
|
||||
int_var = get_differential_var(func.DIFFERENTIAL())
|
||||
else:
|
||||
for sym in integrand.atoms(sympy.Symbol):
|
||||
s = str(sym)
|
||||
if len(s) > 1 and s[0] == 'd':
|
||||
if s[1] == '\\':
|
||||
int_var = sympy.Symbol(s[2:])
|
||||
else:
|
||||
int_var = sympy.Symbol(s[1:])
|
||||
int_sym = sym
|
||||
if int_var:
|
||||
integrand = integrand.subs(int_sym, 1)
|
||||
else:
|
||||
# Assume dx by default
|
||||
int_var = sympy.Symbol('x')
|
||||
|
||||
if func.subexpr():
|
||||
if func.subexpr().atom():
|
||||
lower = convert_atom(func.subexpr().atom())
|
||||
else:
|
||||
lower = convert_expr(func.subexpr().expr())
|
||||
if func.supexpr().atom():
|
||||
upper = convert_atom(func.supexpr().atom())
|
||||
else:
|
||||
upper = convert_expr(func.supexpr().expr())
|
||||
return sympy.Integral(integrand, (int_var, lower, upper))
|
||||
else:
|
||||
return sympy.Integral(integrand, int_var)
|
||||
|
||||
|
||||
def handle_sum_or_prod(func, name):
|
||||
val = convert_mp(func.mp())
|
||||
iter_var = convert_expr(func.subeq().equality().expr(0))
|
||||
start = convert_expr(func.subeq().equality().expr(1))
|
||||
if func.supexpr().expr(): # ^{expr}
|
||||
end = convert_expr(func.supexpr().expr())
|
||||
else: # ^atom
|
||||
end = convert_atom(func.supexpr().atom())
|
||||
|
||||
if name == "summation":
|
||||
return sympy.Sum(val, (iter_var, start, end))
|
||||
elif name == "product":
|
||||
return sympy.Product(val, (iter_var, start, end))
|
||||
|
||||
|
||||
def handle_limit(func):
|
||||
sub = func.limit_sub()
|
||||
if sub.LETTER():
|
||||
var = sympy.Symbol(sub.LETTER().getText())
|
||||
elif sub.SYMBOL():
|
||||
var = sympy.Symbol(sub.SYMBOL().getText()[1:])
|
||||
else:
|
||||
var = sympy.Symbol('x')
|
||||
if sub.SUB():
|
||||
direction = "-"
|
||||
elif sub.ADD():
|
||||
direction = "+"
|
||||
else:
|
||||
direction = "+-"
|
||||
approaching = convert_expr(sub.expr())
|
||||
content = convert_mp(func.mp())
|
||||
|
||||
return sympy.Limit(content, var, approaching, direction)
|
||||
|
||||
|
||||
def get_differential_var(d):
|
||||
text = get_differential_var_str(d.getText())
|
||||
return sympy.Symbol(text)
|
||||
|
||||
|
||||
def get_differential_var_str(text):
|
||||
for i in range(1, len(text)):
|
||||
c = text[i]
|
||||
if not (c == " " or c == "\r" or c == "\n" or c == "\t"):
|
||||
idx = i
|
||||
break
|
||||
text = text[idx:]
|
||||
if text[0] == "\\":
|
||||
text = text[1:]
|
||||
return text
|
||||
@@ -0,0 +1,2 @@
|
||||
class LaTeXParsingError(Exception):
|
||||
pass
|
||||
@@ -0,0 +1,2 @@
|
||||
from .latex_parser import parse_latex_lark, LarkLaTeXParser # noqa
|
||||
from .transformer import TransformToSymPyExpr # noqa
|
||||
@@ -0,0 +1,28 @@
|
||||
// Greek symbols
|
||||
// TODO: Shouold we include the uppercase variants for the symbols where the uppercase variant doesn't have a separate meaning?
|
||||
ALPHA: "\\alpha"
|
||||
BETA: "\\beta"
|
||||
GAMMA: "\\gamma"
|
||||
DELTA: "\\delta" // TODO: Should this be included? Delta usually denotes other things.
|
||||
EPSILON: "\\epsilon" | "\\varepsilon"
|
||||
ZETA: "\\zeta"
|
||||
ETA: "\\eta"
|
||||
THETA: "\\theta" | "\\vartheta"
|
||||
// TODO: Should I add iota to the list?
|
||||
KAPPA: "\\kappa"
|
||||
LAMBDA: "\\lambda" // TODO: What about the uppercase variant?
|
||||
MU: "\\mu"
|
||||
NU: "\\nu"
|
||||
XI: "\\xi"
|
||||
// TODO: Should there be a separate note for transforming \pi into sympy.pi?
|
||||
RHO: "\\rho" | "\\varrho"
|
||||
// TODO: What should we do about sigma?
|
||||
TAU: "\\tau"
|
||||
UPSILON: "\\upsilon"
|
||||
PHI: "\\phi" | "\\varphi"
|
||||
CHI: "\\chi"
|
||||
PSI: "\\psi"
|
||||
OMEGA: "\\omega"
|
||||
|
||||
GREEK_SYMBOL: ALPHA | BETA | GAMMA | DELTA | EPSILON | ZETA | ETA | THETA | KAPPA
|
||||
| LAMBDA | MU | NU | XI | RHO | TAU | UPSILON | PHI | CHI | PSI | OMEGA
|
||||
@@ -0,0 +1,403 @@
|
||||
%ignore /[ \t\n\r]+/
|
||||
|
||||
%ignore "\\," | "\\thinspace" | "\\:" | "\\medspace" | "\\;" | "\\thickspace"
|
||||
%ignore "\\quad" | "\\qquad"
|
||||
%ignore "\\!" | "\\negthinspace" | "\\negmedspace" | "\\negthickspace"
|
||||
%ignore "\\vrule" | "\\vcenter" | "\\vbox" | "\\vskip" | "\\vspace" | "\\hfill"
|
||||
%ignore "\\*" | "\\-" | "\\." | "\\/" | "\\(" | "\\="
|
||||
|
||||
%ignore "\\left" | "\\right"
|
||||
%ignore "\\limits" | "\\nolimits"
|
||||
%ignore "\\displaystyle"
|
||||
|
||||
///////////////////// tokens ///////////////////////
|
||||
|
||||
// basic binary operators
|
||||
ADD: "+"
|
||||
SUB: "-"
|
||||
MUL: "*"
|
||||
DIV: "/"
|
||||
|
||||
// tokens with distinct left and right symbols
|
||||
L_BRACE: "{"
|
||||
R_BRACE: "}"
|
||||
L_BRACE_LITERAL: "\\{"
|
||||
R_BRACE_LITERAL: "\\}"
|
||||
L_BRACKET: "["
|
||||
R_BRACKET: "]"
|
||||
L_CEIL: "\\lceil"
|
||||
R_CEIL: "\\rceil"
|
||||
L_FLOOR: "\\lfloor"
|
||||
R_FLOOR: "\\rfloor"
|
||||
L_PAREN: "("
|
||||
R_PAREN: ")"
|
||||
|
||||
// limit, integral, sum, and product symbols
|
||||
FUNC_LIM: "\\lim"
|
||||
LIM_APPROACH_SYM: "\\to" | "\\rightarrow" | "\\Rightarrow" | "\\longrightarrow" | "\\Longrightarrow"
|
||||
FUNC_INT: "\\int" | "\\intop"
|
||||
FUNC_SUM: "\\sum"
|
||||
FUNC_PROD: "\\prod"
|
||||
|
||||
// common functions
|
||||
FUNC_EXP: "\\exp"
|
||||
FUNC_LOG: "\\log"
|
||||
FUNC_LN: "\\ln"
|
||||
FUNC_LG: "\\lg"
|
||||
FUNC_MIN: "\\min"
|
||||
FUNC_MAX: "\\max"
|
||||
|
||||
// trigonometric functions
|
||||
FUNC_SIN: "\\sin"
|
||||
FUNC_COS: "\\cos"
|
||||
FUNC_TAN: "\\tan"
|
||||
FUNC_CSC: "\\csc"
|
||||
FUNC_SEC: "\\sec"
|
||||
FUNC_COT: "\\cot"
|
||||
|
||||
// inverse trigonometric functions
|
||||
FUNC_ARCSIN: "\\arcsin"
|
||||
FUNC_ARCCOS: "\\arccos"
|
||||
FUNC_ARCTAN: "\\arctan"
|
||||
FUNC_ARCCSC: "\\arccsc"
|
||||
FUNC_ARCSEC: "\\arcsec"
|
||||
FUNC_ARCCOT: "\\arccot"
|
||||
|
||||
// hyperbolic trigonometric functions
|
||||
FUNC_SINH: "\\sinh"
|
||||
FUNC_COSH: "\\cosh"
|
||||
FUNC_TANH: "\\tanh"
|
||||
FUNC_ARSINH: "\\arsinh"
|
||||
FUNC_ARCOSH: "\\arcosh"
|
||||
FUNC_ARTANH: "\\artanh"
|
||||
|
||||
FUNC_SQRT: "\\sqrt"
|
||||
|
||||
// miscellaneous symbols
|
||||
CMD_TIMES: "\\times"
|
||||
CMD_CDOT: "\\cdot"
|
||||
CMD_DIV: "\\div"
|
||||
CMD_FRAC: "\\frac" | "\\dfrac" | "\\tfrac" | "\\nicefrac"
|
||||
CMD_BINOM: "\\binom" | "\\dbinom" | "\\tbinom"
|
||||
CMD_OVERLINE: "\\overline"
|
||||
CMD_LANGLE: "\\langle"
|
||||
CMD_RANGLE: "\\rangle"
|
||||
|
||||
CMD_MATHIT: "\\mathit"
|
||||
|
||||
CMD_INFTY: "\\infty"
|
||||
|
||||
BANG: "!"
|
||||
BAR: "|"
|
||||
CARET: "^"
|
||||
COLON: ":"
|
||||
UNDERSCORE: "_"
|
||||
|
||||
// relational symbols
|
||||
EQUAL: "="
|
||||
NOT_EQUAL: "\\neq" | "\\ne"
|
||||
LT: "<"
|
||||
LTE: "\\leq" | "\\le" | "\\leqslant"
|
||||
GT: ">"
|
||||
GTE: "\\geq" | "\\ge" | "\\geqslant"
|
||||
|
||||
DIV_SYMBOL: CMD_DIV | DIV
|
||||
MUL_SYMBOL: MUL | CMD_TIMES | CMD_CDOT
|
||||
|
||||
%import .greek_symbols.GREEK_SYMBOL
|
||||
|
||||
UPRIGHT_DIFFERENTIAL_SYMBOL: "\\text{d}" | "\\mathrm{d}"
|
||||
DIFFERENTIAL_SYMBOL: "d" | UPRIGHT_DIFFERENTIAL_SYMBOL
|
||||
|
||||
// disallow "d" as a variable name because we want to parse "d" as a differential symbol.
|
||||
SYMBOL: /[a-zA-Z]'*/
|
||||
GREEK_SYMBOL_WITH_PRIMES: GREEK_SYMBOL "'"*
|
||||
LATIN_SYMBOL_WITH_LATIN_SUBSCRIPT: /([a-zA-Z]'*)_(([A-Za-z0-9]|[a-zA-Z]+)|\{([A-Za-z0-9]|[a-zA-Z]+'*)\})/
|
||||
LATIN_SYMBOL_WITH_GREEK_SUBSCRIPT: /([a-zA-Z]'*)_/ GREEK_SYMBOL | /([a-zA-Z]'*)_/ L_BRACE GREEK_SYMBOL_WITH_PRIMES R_BRACE
|
||||
// best to define the variant with braces like that instead of shoving it all into one case like in
|
||||
// /([a-zA-Z])_/ L_BRACE? GREEK_SYMBOL R_BRACE? because then we can easily error out on input like
|
||||
// r"h_{\theta"
|
||||
GREEK_SYMBOL_WITH_LATIN_SUBSCRIPT: GREEK_SYMBOL_WITH_PRIMES /_(([A-Za-z0-9]|[a-zA-Z]+)|\{([A-Za-z0-9]|[a-zA-Z]+'*)\})/
|
||||
GREEK_SYMBOL_WITH_GREEK_SUBSCRIPT: GREEK_SYMBOL_WITH_PRIMES /_/ (GREEK_SYMBOL | L_BRACE GREEK_SYMBOL_WITH_PRIMES R_BRACE)
|
||||
MULTI_LETTER_SYMBOL: /[a-zA-Z]+(\s+[a-zA-Z]+)*'*/
|
||||
|
||||
%import common.DIGIT -> DIGIT
|
||||
|
||||
CMD_PRIME: "\\prime"
|
||||
CMD_ASTERISK: "\\ast"
|
||||
|
||||
PRIMES: "'"+
|
||||
STARS: "*"+
|
||||
PRIMES_VIA_CMD: CMD_PRIME+
|
||||
STARS_VIA_CMD: CMD_ASTERISK+
|
||||
|
||||
CMD_IMAGINARY_UNIT: "\\imaginaryunit"
|
||||
|
||||
CMD_BEGIN: "\\begin"
|
||||
CMD_END: "\\end"
|
||||
|
||||
// matrices
|
||||
IGNORE_L: /[ \t\n\r]*/ L_BRACE* /[ \t\n\r]*/
|
||||
IGNORE_R: /[ \t\n\r]*/ R_BRACE* /[ \t\n\r]*/
|
||||
ARRAY_MATRIX_BEGIN: L_BRACE "array" R_BRACE L_BRACE /[^}]*/ R_BRACE
|
||||
ARRAY_MATRIX_END: L_BRACE "array" R_BRACE
|
||||
AMSMATH_MATRIX: L_BRACE "matrix" R_BRACE
|
||||
AMSMATH_PMATRIX: L_BRACE "pmatrix" R_BRACE
|
||||
AMSMATH_BMATRIX: L_BRACE "bmatrix" R_BRACE
|
||||
// Without the (L|R)_PARENs and (L|R)_BRACKETs, a matrix defined using
|
||||
// \begin{array}...\end{array} or \begin{matrix}...\end{matrix} must
|
||||
// not qualify as a complete matrix expression; this is done so that
|
||||
// if we have \begin{array}...\end{array} or \begin{matrix}...\end{matrix}
|
||||
// between BAR pairs, then they should be interpreted as determinants as
|
||||
// opposed to sympy.Abs (absolute value) applied to a matrix.
|
||||
CMD_BEGIN_AMSPMATRIX_AMSBMATRIX: CMD_BEGIN (AMSMATH_PMATRIX | AMSMATH_BMATRIX)
|
||||
CMD_BEGIN_ARRAY_AMSMATRIX: (L_PAREN | L_BRACKET) IGNORE_L CMD_BEGIN (ARRAY_MATRIX_BEGIN | AMSMATH_MATRIX)
|
||||
CMD_MATRIX_BEGIN: CMD_BEGIN_AMSPMATRIX_AMSBMATRIX | CMD_BEGIN_ARRAY_AMSMATRIX
|
||||
CMD_END_AMSPMATRIX_AMSBMATRIX: CMD_END (AMSMATH_PMATRIX | AMSMATH_BMATRIX)
|
||||
CMD_END_ARRAY_AMSMATRIX: CMD_END (ARRAY_MATRIX_END | AMSMATH_MATRIX) IGNORE_R "\\right"? (R_PAREN | R_BRACKET)
|
||||
CMD_MATRIX_END: CMD_END_AMSPMATRIX_AMSBMATRIX | CMD_END_ARRAY_AMSMATRIX
|
||||
MATRIX_COL_DELIM: "&"
|
||||
MATRIX_ROW_DELIM: "\\\\"
|
||||
FUNC_MATRIX_TRACE: "\\trace"
|
||||
FUNC_MATRIX_ADJUGATE: "\\adjugate"
|
||||
|
||||
// determinants
|
||||
AMSMATH_VMATRIX: L_BRACE "vmatrix" R_BRACE
|
||||
CMD_DETERMINANT_BEGIN_SIMPLE: CMD_BEGIN AMSMATH_VMATRIX
|
||||
CMD_DETERMINANT_BEGIN_VARIANT: BAR IGNORE_L CMD_BEGIN (ARRAY_MATRIX_BEGIN | AMSMATH_MATRIX)
|
||||
CMD_DETERMINANT_BEGIN: CMD_DETERMINANT_BEGIN_SIMPLE | CMD_DETERMINANT_BEGIN_VARIANT
|
||||
CMD_DETERMINANT_END_SIMPLE: CMD_END AMSMATH_VMATRIX
|
||||
CMD_DETERMINANT_END_VARIANT: CMD_END (ARRAY_MATRIX_END | AMSMATH_MATRIX) IGNORE_R "\\right"? BAR
|
||||
CMD_DETERMINANT_END: CMD_DETERMINANT_END_SIMPLE | CMD_DETERMINANT_END_VARIANT
|
||||
FUNC_DETERMINANT: "\\det"
|
||||
|
||||
//////////////////// grammar //////////////////////
|
||||
|
||||
latex_string: _relation | _expression
|
||||
|
||||
_one_letter_symbol: SYMBOL
|
||||
| LATIN_SYMBOL_WITH_LATIN_SUBSCRIPT
|
||||
| LATIN_SYMBOL_WITH_GREEK_SUBSCRIPT
|
||||
| GREEK_SYMBOL_WITH_LATIN_SUBSCRIPT
|
||||
| GREEK_SYMBOL_WITH_GREEK_SUBSCRIPT
|
||||
| GREEK_SYMBOL_WITH_PRIMES
|
||||
// LuaTeX-generated outputs of \mathit{foo'} and \mathit{foo}'
|
||||
// seem to be the same on the surface. We allow both styles.
|
||||
multi_letter_symbol: CMD_MATHIT L_BRACE MULTI_LETTER_SYMBOL R_BRACE
|
||||
| CMD_MATHIT L_BRACE MULTI_LETTER_SYMBOL R_BRACE /'+/
|
||||
number: /\d+(\.\d*)?/ | CMD_IMAGINARY_UNIT
|
||||
|
||||
_atomic_expr: _one_letter_symbol
|
||||
| multi_letter_symbol
|
||||
| number
|
||||
| CMD_INFTY
|
||||
|
||||
group_round_parentheses: L_PAREN _expression R_PAREN
|
||||
group_square_brackets: L_BRACKET _expression R_BRACKET
|
||||
group_curly_parentheses: L_BRACE _expression R_BRACE
|
||||
|
||||
_relation: eq | ne | lt | lte | gt | gte
|
||||
|
||||
eq: _expression EQUAL _expression
|
||||
ne: _expression NOT_EQUAL _expression
|
||||
lt: _expression LT _expression
|
||||
lte: _expression LTE _expression
|
||||
gt: _expression GT _expression
|
||||
gte: _expression GTE _expression
|
||||
|
||||
_expression_core: _atomic_expr | group_curly_parentheses
|
||||
|
||||
add: _expression ADD _expression_mul
|
||||
| ADD _expression_mul
|
||||
sub: _expression SUB _expression_mul
|
||||
| SUB _expression_mul
|
||||
mul: _expression_mul MUL_SYMBOL _expression_power
|
||||
div: _expression_mul DIV_SYMBOL _expression_power
|
||||
|
||||
adjacent_expressions: (_one_letter_symbol | number) _expression_mul
|
||||
| group_round_parentheses (group_round_parentheses | _one_letter_symbol)
|
||||
| _function _function
|
||||
| fraction _expression_mul
|
||||
|
||||
_expression_func: _expression_core
|
||||
| group_round_parentheses
|
||||
| fraction
|
||||
| binomial
|
||||
| _function
|
||||
| _integral// | derivative
|
||||
| limit
|
||||
| matrix
|
||||
|
||||
_expression_power: _expression_func | superscript | matrix_prime | symbol_prime
|
||||
|
||||
_expression_mul: _expression_power
|
||||
| mul | div | adjacent_expressions
|
||||
| summation | product
|
||||
|
||||
_expression: _expression_mul | add | sub
|
||||
|
||||
_limit_dir: "+" | "-" | L_BRACE ("+" | "-") R_BRACE
|
||||
|
||||
limit_dir_expr: _expression CARET _limit_dir
|
||||
|
||||
group_curly_parentheses_lim: L_BRACE _expression LIM_APPROACH_SYM (limit_dir_expr | _expression) R_BRACE
|
||||
|
||||
limit: FUNC_LIM UNDERSCORE group_curly_parentheses_lim _expression
|
||||
|
||||
differential: DIFFERENTIAL_SYMBOL _one_letter_symbol
|
||||
|
||||
//_derivative_operator: CMD_FRAC L_BRACE DIFFERENTIAL_SYMBOL R_BRACE L_BRACE differential R_BRACE
|
||||
|
||||
//derivative: _derivative_operator _expression
|
||||
|
||||
_integral: normal_integral | integral_with_special_fraction
|
||||
|
||||
normal_integral: FUNC_INT _expression DIFFERENTIAL_SYMBOL _one_letter_symbol
|
||||
| FUNC_INT (CARET _expression_core UNDERSCORE _expression_core)? _expression? DIFFERENTIAL_SYMBOL _one_letter_symbol
|
||||
| FUNC_INT (UNDERSCORE _expression_core CARET _expression_core)? _expression? DIFFERENTIAL_SYMBOL _one_letter_symbol
|
||||
|
||||
group_curly_parentheses_int: L_BRACE _expression? differential R_BRACE
|
||||
|
||||
special_fraction: CMD_FRAC group_curly_parentheses_int group_curly_parentheses
|
||||
|
||||
integral_with_special_fraction: FUNC_INT special_fraction
|
||||
| FUNC_INT (CARET _expression_core UNDERSCORE _expression_core)? special_fraction
|
||||
| FUNC_INT (UNDERSCORE _expression_core CARET _expression_core)? special_fraction
|
||||
|
||||
group_curly_parentheses_special: UNDERSCORE L_BRACE _atomic_expr EQUAL _atomic_expr R_BRACE CARET _expression_core
|
||||
| CARET _expression_core UNDERSCORE L_BRACE _atomic_expr EQUAL _atomic_expr R_BRACE
|
||||
|
||||
summation: FUNC_SUM group_curly_parentheses_special _expression
|
||||
| FUNC_SUM group_curly_parentheses_special _expression
|
||||
|
||||
product: FUNC_PROD group_curly_parentheses_special _expression
|
||||
| FUNC_PROD group_curly_parentheses_special _expression
|
||||
|
||||
superscript: _expression_func CARET (_expression_power | CMD_PRIME | CMD_ASTERISK)
|
||||
| _expression_func CARET L_BRACE (PRIMES | STARS | PRIMES_VIA_CMD | STARS_VIA_CMD) R_BRACE
|
||||
|
||||
matrix_prime: (matrix | group_round_parentheses) PRIMES
|
||||
|
||||
symbol_prime: (LATIN_SYMBOL_WITH_LATIN_SUBSCRIPT
|
||||
| LATIN_SYMBOL_WITH_GREEK_SUBSCRIPT
|
||||
| GREEK_SYMBOL_WITH_LATIN_SUBSCRIPT
|
||||
| GREEK_SYMBOL_WITH_GREEK_SUBSCRIPT) PRIMES
|
||||
|
||||
fraction: _basic_fraction
|
||||
| _simple_fraction
|
||||
| _general_fraction
|
||||
|
||||
_basic_fraction: CMD_FRAC DIGIT (DIGIT | SYMBOL | GREEK_SYMBOL_WITH_PRIMES)
|
||||
|
||||
_simple_fraction: CMD_FRAC DIGIT group_curly_parentheses
|
||||
| CMD_FRAC group_curly_parentheses (DIGIT | SYMBOL | GREEK_SYMBOL_WITH_PRIMES)
|
||||
|
||||
_general_fraction: CMD_FRAC group_curly_parentheses group_curly_parentheses
|
||||
|
||||
binomial: _basic_binomial
|
||||
| _simple_binomial
|
||||
| _general_binomial
|
||||
|
||||
_basic_binomial: CMD_BINOM DIGIT (DIGIT | SYMBOL | GREEK_SYMBOL_WITH_PRIMES)
|
||||
|
||||
_simple_binomial: CMD_BINOM DIGIT group_curly_parentheses
|
||||
| CMD_BINOM group_curly_parentheses (DIGIT | SYMBOL | GREEK_SYMBOL_WITH_PRIMES)
|
||||
|
||||
_general_binomial: CMD_BINOM group_curly_parentheses group_curly_parentheses
|
||||
|
||||
list_of_expressions: _expression ("," _expression)*
|
||||
|
||||
function_applied: _one_letter_symbol L_PAREN list_of_expressions R_PAREN
|
||||
|
||||
min: FUNC_MIN L_PAREN list_of_expressions R_PAREN
|
||||
|
||||
max: FUNC_MAX L_PAREN list_of_expressions R_PAREN
|
||||
|
||||
bra: CMD_LANGLE _expression BAR
|
||||
|
||||
ket: BAR _expression CMD_RANGLE
|
||||
|
||||
inner_product: CMD_LANGLE _expression BAR _expression CMD_RANGLE
|
||||
|
||||
_function: function_applied
|
||||
| abs | floor | ceil
|
||||
| _trigonometric_function | _inverse_trigonometric_function
|
||||
| _trigonometric_function_power
|
||||
| _hyperbolic_trigonometric_function | _inverse_hyperbolic_trigonometric_function
|
||||
| exponential
|
||||
| log
|
||||
| square_root
|
||||
| factorial
|
||||
| conjugate
|
||||
| max | min
|
||||
| bra | ket | inner_product
|
||||
| determinant
|
||||
| trace
|
||||
| adjugate
|
||||
|
||||
exponential: FUNC_EXP _expression
|
||||
|
||||
log: FUNC_LOG _expression
|
||||
| FUNC_LN _expression
|
||||
| FUNC_LG _expression
|
||||
| FUNC_LOG UNDERSCORE (DIGIT | _one_letter_symbol) _expression
|
||||
| FUNC_LOG UNDERSCORE group_curly_parentheses _expression
|
||||
|
||||
square_root: FUNC_SQRT group_curly_parentheses
|
||||
| FUNC_SQRT group_square_brackets group_curly_parentheses
|
||||
|
||||
factorial: _expression_func BANG
|
||||
|
||||
conjugate: CMD_OVERLINE group_curly_parentheses
|
||||
| CMD_OVERLINE DIGIT
|
||||
|
||||
_trigonometric_function: sin | cos | tan | csc | sec | cot
|
||||
|
||||
sin: FUNC_SIN _expression
|
||||
cos: FUNC_COS _expression
|
||||
tan: FUNC_TAN _expression
|
||||
csc: FUNC_CSC _expression
|
||||
sec: FUNC_SEC _expression
|
||||
cot: FUNC_COT _expression
|
||||
|
||||
_trigonometric_function_power: sin_power | cos_power | tan_power | csc_power | sec_power | cot_power
|
||||
|
||||
sin_power: FUNC_SIN CARET _expression_core _expression
|
||||
cos_power: FUNC_COS CARET _expression_core _expression
|
||||
tan_power: FUNC_TAN CARET _expression_core _expression
|
||||
csc_power: FUNC_CSC CARET _expression_core _expression
|
||||
sec_power: FUNC_SEC CARET _expression_core _expression
|
||||
cot_power: FUNC_COT CARET _expression_core _expression
|
||||
|
||||
_hyperbolic_trigonometric_function: sinh | cosh | tanh
|
||||
|
||||
sinh: FUNC_SINH _expression
|
||||
cosh: FUNC_COSH _expression
|
||||
tanh: FUNC_TANH _expression
|
||||
|
||||
_inverse_trigonometric_function: arcsin | arccos | arctan | arccsc | arcsec | arccot
|
||||
|
||||
arcsin: FUNC_ARCSIN _expression
|
||||
arccos: FUNC_ARCCOS _expression
|
||||
arctan: FUNC_ARCTAN _expression
|
||||
arccsc: FUNC_ARCCSC _expression
|
||||
arcsec: FUNC_ARCSEC _expression
|
||||
arccot: FUNC_ARCCOT _expression
|
||||
|
||||
_inverse_hyperbolic_trigonometric_function: asinh | acosh | atanh
|
||||
|
||||
asinh: FUNC_ARSINH _expression
|
||||
acosh: FUNC_ARCOSH _expression
|
||||
atanh: FUNC_ARTANH _expression
|
||||
|
||||
abs: BAR _expression BAR
|
||||
floor: L_FLOOR _expression R_FLOOR
|
||||
ceil: L_CEIL _expression R_CEIL
|
||||
|
||||
matrix: CMD_MATRIX_BEGIN matrix_body CMD_MATRIX_END
|
||||
matrix_body: matrix_row (MATRIX_ROW_DELIM matrix_row)* (MATRIX_ROW_DELIM)?
|
||||
matrix_row: _expression (MATRIX_COL_DELIM _expression)*
|
||||
determinant: (CMD_DETERMINANT_BEGIN matrix_body CMD_DETERMINANT_END)
|
||||
| FUNC_DETERMINANT _expression
|
||||
trace: FUNC_MATRIX_TRACE _expression
|
||||
adjugate: FUNC_MATRIX_ADJUGATE _expression
|
||||
@@ -0,0 +1,145 @@
|
||||
import os
|
||||
import logging
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from sympy.external import import_module
|
||||
from sympy.parsing.latex.lark.transformer import TransformToSymPyExpr
|
||||
|
||||
_lark = import_module("lark")
|
||||
|
||||
|
||||
class LarkLaTeXParser:
|
||||
r"""Class for converting input `\mathrm{\LaTeX}` strings into SymPy Expressions.
|
||||
It holds all the necessary internal data for doing so, and exposes hooks for
|
||||
customizing its behavior.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
print_debug_output : bool, optional
|
||||
|
||||
If set to ``True``, prints debug output to the logger. Defaults to ``False``.
|
||||
|
||||
transform : bool, optional
|
||||
|
||||
If set to ``True``, the class runs the Transformer class on the parse tree
|
||||
generated by running ``Lark.parse`` on the input string. Defaults to ``True``.
|
||||
|
||||
Setting it to ``False`` can help with debugging the `\mathrm{\LaTeX}` grammar.
|
||||
|
||||
grammar_file : str, optional
|
||||
|
||||
The path to the grammar file that the parser should use. If set to ``None``,
|
||||
it uses the default grammar, which is in ``grammar/latex.lark``, relative to
|
||||
the ``sympy/parsing/latex/lark/`` directory.
|
||||
|
||||
transformer : str, optional
|
||||
|
||||
The name of the Transformer class to use. If set to ``None``, it uses the
|
||||
default transformer class, which is :py:func:`TransformToSymPyExpr`.
|
||||
|
||||
"""
|
||||
def __init__(self, print_debug_output=False, transform=True, grammar_file=None, transformer=None):
|
||||
grammar_dir_path = os.path.join(os.path.dirname(__file__), "grammar/")
|
||||
|
||||
if grammar_file is None:
|
||||
latex_grammar = Path(os.path.join(grammar_dir_path, "latex.lark")).read_text(encoding="utf-8")
|
||||
else:
|
||||
latex_grammar = Path(grammar_file).read_text(encoding="utf-8")
|
||||
|
||||
self.parser = _lark.Lark(
|
||||
latex_grammar,
|
||||
source_path=grammar_dir_path,
|
||||
parser="earley",
|
||||
start="latex_string",
|
||||
lexer="auto",
|
||||
ambiguity="explicit",
|
||||
propagate_positions=False,
|
||||
maybe_placeholders=False,
|
||||
keep_all_tokens=True)
|
||||
|
||||
self.print_debug_output = print_debug_output
|
||||
self.transform_expr = transform
|
||||
|
||||
if transformer is None:
|
||||
self.transformer = TransformToSymPyExpr()
|
||||
else:
|
||||
self.transformer = transformer()
|
||||
|
||||
def doparse(self, s: str):
|
||||
if self.print_debug_output:
|
||||
_lark.logger.setLevel(logging.DEBUG)
|
||||
|
||||
parse_tree = self.parser.parse(s)
|
||||
|
||||
if not self.transform_expr:
|
||||
# exit early and return the parse tree
|
||||
_lark.logger.debug("expression = %s", s)
|
||||
_lark.logger.debug(parse_tree)
|
||||
_lark.logger.debug(parse_tree.pretty())
|
||||
return parse_tree
|
||||
|
||||
if self.print_debug_output:
|
||||
# print this stuff before attempting to run the transformer
|
||||
_lark.logger.debug("expression = %s", s)
|
||||
# print the `parse_tree` variable
|
||||
_lark.logger.debug(parse_tree.pretty())
|
||||
|
||||
sympy_expression = self.transformer.transform(parse_tree)
|
||||
|
||||
if self.print_debug_output:
|
||||
_lark.logger.debug("SymPy expression = %s", sympy_expression)
|
||||
|
||||
return sympy_expression
|
||||
|
||||
|
||||
if _lark is not None:
|
||||
_lark_latex_parser = LarkLaTeXParser()
|
||||
|
||||
|
||||
def parse_latex_lark(s: str):
|
||||
"""
|
||||
Experimental LaTeX parser using Lark.
|
||||
|
||||
This function is still under development and its API may change with the
|
||||
next releases of SymPy.
|
||||
"""
|
||||
if _lark is None:
|
||||
raise ImportError("Lark is probably not installed")
|
||||
return _lark_latex_parser.doparse(s)
|
||||
|
||||
|
||||
def _pretty_print_lark_trees(tree, indent=0, show_expr=True):
|
||||
if isinstance(tree, _lark.Token):
|
||||
return tree.value
|
||||
|
||||
data = str(tree.data)
|
||||
|
||||
is_expr = data.startswith("expression")
|
||||
|
||||
if is_expr:
|
||||
data = re.sub(r"^expression", "E", data)
|
||||
|
||||
is_ambig = (data == "_ambig")
|
||||
|
||||
if is_ambig:
|
||||
new_indent = indent + 2
|
||||
else:
|
||||
new_indent = indent
|
||||
|
||||
output = ""
|
||||
show_node = not is_expr or show_expr
|
||||
|
||||
if show_node:
|
||||
output += str(data) + "("
|
||||
|
||||
if is_ambig:
|
||||
output += "\n" + "\n".join([" " * new_indent + _pretty_print_lark_trees(i, new_indent, show_expr) for i in tree.children])
|
||||
else:
|
||||
output += ",".join([_pretty_print_lark_trees(i, new_indent, show_expr) for i in tree.children])
|
||||
|
||||
if show_node:
|
||||
output += ")"
|
||||
|
||||
return output
|
||||
@@ -0,0 +1,730 @@
|
||||
import re
|
||||
|
||||
import sympy
|
||||
from sympy.external import import_module
|
||||
from sympy.parsing.latex.errors import LaTeXParsingError
|
||||
|
||||
lark = import_module("lark")
|
||||
|
||||
if lark:
|
||||
from lark import Transformer, Token, Tree # type: ignore
|
||||
else:
|
||||
class Transformer: # type: ignore
|
||||
def transform(self, *args):
|
||||
pass
|
||||
|
||||
|
||||
class Token: # type: ignore
|
||||
pass
|
||||
|
||||
|
||||
class Tree: # type: ignore
|
||||
pass
|
||||
|
||||
|
||||
# noinspection PyPep8Naming,PyMethodMayBeStatic
|
||||
class TransformToSymPyExpr(Transformer):
|
||||
"""Returns a SymPy expression that is generated by traversing the ``lark.Tree``
|
||||
passed to the ``.transform()`` function.
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
**This class is never supposed to be used directly.**
|
||||
|
||||
In order to tweak the behavior of this class, it has to be subclassed and then after
|
||||
the required modifications are made, the name of the new class should be passed to
|
||||
the :py:class:`LarkLaTeXParser` class by using the ``transformer`` argument in the
|
||||
constructor.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
visit_tokens : bool, optional
|
||||
For information about what this option does, see `here
|
||||
<https://lark-parser.readthedocs.io/en/latest/visitors.html#lark.visitors.Transformer>`_.
|
||||
|
||||
Note that the option must be set to ``True`` for the default parser to work.
|
||||
"""
|
||||
|
||||
SYMBOL = sympy.Symbol
|
||||
DIGIT = sympy.core.numbers.Integer
|
||||
|
||||
def CMD_INFTY(self, tokens):
|
||||
return sympy.oo
|
||||
|
||||
def GREEK_SYMBOL_WITH_PRIMES(self, tokens):
|
||||
# we omit the first character because it is a backslash. Also, if the variable name has "var" in it,
|
||||
# like "varphi" or "varepsilon", we remove that too
|
||||
variable_name = re.sub("var", "", tokens[1:])
|
||||
|
||||
return sympy.Symbol(variable_name)
|
||||
|
||||
def LATIN_SYMBOL_WITH_LATIN_SUBSCRIPT(self, tokens):
|
||||
base, sub = tokens.value.split("_")
|
||||
if sub.startswith("{"):
|
||||
return sympy.Symbol("%s_{%s}" % (base, sub[1:-1]))
|
||||
else:
|
||||
return sympy.Symbol("%s_{%s}" % (base, sub))
|
||||
|
||||
def GREEK_SYMBOL_WITH_LATIN_SUBSCRIPT(self, tokens):
|
||||
base, sub = tokens.value.split("_")
|
||||
greek_letter = re.sub("var", "", base[1:])
|
||||
|
||||
if sub.startswith("{"):
|
||||
return sympy.Symbol("%s_{%s}" % (greek_letter, sub[1:-1]))
|
||||
else:
|
||||
return sympy.Symbol("%s_{%s}" % (greek_letter, sub))
|
||||
|
||||
def LATIN_SYMBOL_WITH_GREEK_SUBSCRIPT(self, tokens):
|
||||
base, sub = tokens.value.split("_")
|
||||
if sub.startswith("{"):
|
||||
greek_letter = sub[2:-1]
|
||||
else:
|
||||
greek_letter = sub[1:]
|
||||
|
||||
greek_letter = re.sub("var", "", greek_letter)
|
||||
return sympy.Symbol("%s_{%s}" % (base, greek_letter))
|
||||
|
||||
|
||||
def GREEK_SYMBOL_WITH_GREEK_SUBSCRIPT(self, tokens):
|
||||
base, sub = tokens.value.split("_")
|
||||
greek_base = re.sub("var", "", base[1:])
|
||||
|
||||
if sub.startswith("{"):
|
||||
greek_sub = sub[2:-1]
|
||||
else:
|
||||
greek_sub = sub[1:]
|
||||
|
||||
greek_sub = re.sub("var", "", greek_sub)
|
||||
return sympy.Symbol("%s_{%s}" % (greek_base, greek_sub))
|
||||
|
||||
def multi_letter_symbol(self, tokens):
|
||||
if len(tokens) == 4: # no primes (single quotes) on symbol
|
||||
return sympy.Symbol(tokens[2])
|
||||
if len(tokens) == 5: # there are primes on the symbol
|
||||
return sympy.Symbol(tokens[2] + tokens[4])
|
||||
|
||||
def number(self, tokens):
|
||||
if tokens[0].type == "CMD_IMAGINARY_UNIT":
|
||||
return sympy.I
|
||||
|
||||
if "." in tokens[0]:
|
||||
return sympy.core.numbers.Float(tokens[0])
|
||||
else:
|
||||
return sympy.core.numbers.Integer(tokens[0])
|
||||
|
||||
def latex_string(self, tokens):
|
||||
return tokens[0]
|
||||
|
||||
def group_round_parentheses(self, tokens):
|
||||
return tokens[1]
|
||||
|
||||
def group_square_brackets(self, tokens):
|
||||
return tokens[1]
|
||||
|
||||
def group_curly_parentheses(self, tokens):
|
||||
return tokens[1]
|
||||
|
||||
def eq(self, tokens):
|
||||
return sympy.Eq(tokens[0], tokens[2])
|
||||
|
||||
def ne(self, tokens):
|
||||
return sympy.Ne(tokens[0], tokens[2])
|
||||
|
||||
def lt(self, tokens):
|
||||
return sympy.Lt(tokens[0], tokens[2])
|
||||
|
||||
def lte(self, tokens):
|
||||
return sympy.Le(tokens[0], tokens[2])
|
||||
|
||||
def gt(self, tokens):
|
||||
return sympy.Gt(tokens[0], tokens[2])
|
||||
|
||||
def gte(self, tokens):
|
||||
return sympy.Ge(tokens[0], tokens[2])
|
||||
|
||||
def add(self, tokens):
|
||||
if len(tokens) == 2: # +a
|
||||
return tokens[1]
|
||||
if len(tokens) == 3: # a + b
|
||||
lh = tokens[0]
|
||||
rh = tokens[2]
|
||||
|
||||
if self._obj_is_sympy_Matrix(lh) or self._obj_is_sympy_Matrix(rh):
|
||||
return sympy.MatAdd(lh, rh)
|
||||
|
||||
return sympy.Add(lh, rh)
|
||||
|
||||
def sub(self, tokens):
|
||||
if len(tokens) == 2: # -a
|
||||
x = tokens[1]
|
||||
|
||||
if self._obj_is_sympy_Matrix(x):
|
||||
return sympy.MatMul(-1, x)
|
||||
|
||||
return -x
|
||||
if len(tokens) == 3: # a - b
|
||||
lh = tokens[0]
|
||||
rh = tokens[2]
|
||||
|
||||
if self._obj_is_sympy_Matrix(lh) or self._obj_is_sympy_Matrix(rh):
|
||||
return sympy.MatAdd(lh, sympy.MatMul(-1, rh))
|
||||
|
||||
return sympy.Add(lh, -rh)
|
||||
|
||||
def mul(self, tokens):
|
||||
lh = tokens[0]
|
||||
rh = tokens[2]
|
||||
|
||||
if self._obj_is_sympy_Matrix(lh) or self._obj_is_sympy_Matrix(rh):
|
||||
return sympy.MatMul(lh, rh)
|
||||
|
||||
return sympy.Mul(lh, rh)
|
||||
|
||||
def div(self, tokens):
|
||||
return self._handle_division(tokens[0], tokens[2])
|
||||
|
||||
def adjacent_expressions(self, tokens):
|
||||
# Most of the time, if two expressions are next to each other, it means implicit multiplication,
|
||||
# but not always
|
||||
from sympy.physics.quantum import Bra, Ket
|
||||
if isinstance(tokens[0], Ket) and isinstance(tokens[1], Bra):
|
||||
from sympy.physics.quantum import OuterProduct
|
||||
return OuterProduct(tokens[0], tokens[1])
|
||||
elif tokens[0] == sympy.Symbol("d"):
|
||||
# If the leftmost token is a "d", then it is highly likely that this is a differential
|
||||
return tokens[0], tokens[1]
|
||||
elif isinstance(tokens[0], tuple):
|
||||
# then we have a derivative
|
||||
return sympy.Derivative(tokens[1], tokens[0][1])
|
||||
else:
|
||||
return sympy.Mul(tokens[0], tokens[1])
|
||||
|
||||
def superscript(self, tokens):
|
||||
def isprime(x):
|
||||
return isinstance(x, Token) and x.type == "PRIMES"
|
||||
|
||||
def iscmdprime(x):
|
||||
return isinstance(x, Token) and (x.type == "PRIMES_VIA_CMD"
|
||||
or x.type == "CMD_PRIME")
|
||||
|
||||
def isstar(x):
|
||||
return isinstance(x, Token) and x.type == "STARS"
|
||||
|
||||
def iscmdstar(x):
|
||||
return isinstance(x, Token) and (x.type == "STARS_VIA_CMD"
|
||||
or x.type == "CMD_ASTERISK")
|
||||
|
||||
base = tokens[0]
|
||||
if len(tokens) == 3: # a^b OR a^\prime OR a^\ast
|
||||
sup = tokens[2]
|
||||
if len(tokens) == 5:
|
||||
# a^{'}, a^{''}, ... OR
|
||||
# a^{*}, a^{**}, ... OR
|
||||
# a^{\prime}, a^{\prime\prime}, ... OR
|
||||
# a^{\ast}, a^{\ast\ast}, ...
|
||||
sup = tokens[3]
|
||||
|
||||
if self._obj_is_sympy_Matrix(base):
|
||||
if sup == sympy.Symbol("T"):
|
||||
return sympy.Transpose(base)
|
||||
if sup == sympy.Symbol("H"):
|
||||
return sympy.adjoint(base)
|
||||
if isprime(sup):
|
||||
sup = sup.value
|
||||
if len(sup) % 2 == 0:
|
||||
return base
|
||||
return sympy.Transpose(base)
|
||||
if iscmdprime(sup):
|
||||
sup = sup.value
|
||||
if (len(sup)/len(r"\prime")) % 2 == 0:
|
||||
return base
|
||||
return sympy.Transpose(base)
|
||||
if isstar(sup):
|
||||
sup = sup.value
|
||||
# need .doit() in order to be consistent with
|
||||
# sympy.adjoint() which returns the evaluated adjoint
|
||||
# of a matrix
|
||||
if len(sup) % 2 == 0:
|
||||
return base.doit()
|
||||
return sympy.adjoint(base)
|
||||
if iscmdstar(sup):
|
||||
sup = sup.value
|
||||
# need .doit() for same reason as above
|
||||
if (len(sup)/len(r"\ast")) % 2 == 0:
|
||||
return base.doit()
|
||||
return sympy.adjoint(base)
|
||||
|
||||
if isprime(sup) or iscmdprime(sup) or isstar(sup) or iscmdstar(sup):
|
||||
raise LaTeXParsingError(f"{base} with superscript {sup} is not understood.")
|
||||
|
||||
return sympy.Pow(base, sup)
|
||||
|
||||
def matrix_prime(self, tokens):
|
||||
base = tokens[0]
|
||||
primes = tokens[1].value
|
||||
|
||||
if not self._obj_is_sympy_Matrix(base):
|
||||
raise LaTeXParsingError(f"({base}){primes} is not understood.")
|
||||
|
||||
if len(primes) % 2 == 0:
|
||||
return base
|
||||
|
||||
return sympy.Transpose(base)
|
||||
|
||||
def symbol_prime(self, tokens):
|
||||
base = tokens[0]
|
||||
primes = tokens[1].value
|
||||
|
||||
return sympy.Symbol(f"{base.name}{primes}")
|
||||
|
||||
def fraction(self, tokens):
|
||||
numerator = tokens[1]
|
||||
if isinstance(tokens[2], tuple):
|
||||
# we only need the variable w.r.t. which we are differentiating
|
||||
_, variable = tokens[2]
|
||||
|
||||
# we will pass this information upwards
|
||||
return "derivative", variable
|
||||
else:
|
||||
denominator = tokens[2]
|
||||
return self._handle_division(numerator, denominator)
|
||||
|
||||
def binomial(self, tokens):
|
||||
return sympy.binomial(tokens[1], tokens[2])
|
||||
|
||||
def normal_integral(self, tokens):
|
||||
underscore_index = None
|
||||
caret_index = None
|
||||
|
||||
if "_" in tokens:
|
||||
# we need to know the index because the next item in the list is the
|
||||
# arguments for the lower bound of the integral
|
||||
underscore_index = tokens.index("_")
|
||||
|
||||
if "^" in tokens:
|
||||
# we need to know the index because the next item in the list is the
|
||||
# arguments for the upper bound of the integral
|
||||
caret_index = tokens.index("^")
|
||||
|
||||
lower_bound = tokens[underscore_index + 1] if underscore_index else None
|
||||
upper_bound = tokens[caret_index + 1] if caret_index else None
|
||||
|
||||
differential_symbol = self._extract_differential_symbol(tokens)
|
||||
|
||||
if differential_symbol is None:
|
||||
raise LaTeXParsingError("Differential symbol was not found in the expression."
|
||||
"Valid differential symbols are \"d\", \"\\text{d}, and \"\\mathrm{d}\".")
|
||||
|
||||
# else we can assume that a differential symbol was found
|
||||
differential_variable_index = tokens.index(differential_symbol) + 1
|
||||
differential_variable = tokens[differential_variable_index]
|
||||
|
||||
# we can't simply do something like `if (lower_bound and not upper_bound) ...` because this would
|
||||
# evaluate to `True` if the `lower_bound` is 0 and upper bound is non-zero
|
||||
if lower_bound is not None and upper_bound is None:
|
||||
# then one was given and the other wasn't
|
||||
raise LaTeXParsingError("Lower bound for the integral was found, but upper bound was not found.")
|
||||
|
||||
if upper_bound is not None and lower_bound is None:
|
||||
# then one was given and the other wasn't
|
||||
raise LaTeXParsingError("Upper bound for the integral was found, but lower bound was not found.")
|
||||
|
||||
# check if any expression was given or not. If it wasn't, then set the integrand to 1.
|
||||
if underscore_index is not None and underscore_index == differential_variable_index - 3:
|
||||
# The Token at differential_variable_index - 2 should be the integrand. However, if going one more step
|
||||
# backwards after that gives us the underscore, then that means that there _was_ no integrand.
|
||||
# Example: \int^7_0 dx
|
||||
integrand = 1
|
||||
elif caret_index is not None and caret_index == differential_variable_index - 3:
|
||||
# The Token at differential_variable_index - 2 should be the integrand. However, if going one more step
|
||||
# backwards after that gives us the caret, then that means that there _was_ no integrand.
|
||||
# Example: \int_0^7 dx
|
||||
integrand = 1
|
||||
elif differential_variable_index == 2:
|
||||
# this means we have something like "\int dx", because the "\int" symbol will always be
|
||||
# at index 0 in `tokens`
|
||||
integrand = 1
|
||||
else:
|
||||
# The Token at differential_variable_index - 1 is the differential symbol itself, so we need to go one
|
||||
# more step before that.
|
||||
integrand = tokens[differential_variable_index - 2]
|
||||
|
||||
if lower_bound is not None:
|
||||
# then we have a definite integral
|
||||
|
||||
# we can assume that either both the lower and upper bounds are given, or
|
||||
# neither of them are
|
||||
return sympy.Integral(integrand, (differential_variable, lower_bound, upper_bound))
|
||||
else:
|
||||
# we have an indefinite integral
|
||||
return sympy.Integral(integrand, differential_variable)
|
||||
|
||||
def group_curly_parentheses_int(self, tokens):
|
||||
# return signature is a tuple consisting of the expression in the numerator, along with the variable of
|
||||
# integration
|
||||
if len(tokens) == 3:
|
||||
return 1, tokens[1]
|
||||
elif len(tokens) == 4:
|
||||
return tokens[1], tokens[2]
|
||||
# there are no other possibilities
|
||||
|
||||
def special_fraction(self, tokens):
|
||||
numerator, variable = tokens[1]
|
||||
denominator = tokens[2]
|
||||
|
||||
# We pass the integrand, along with information about the variable of integration, upw
|
||||
return sympy.Mul(numerator, sympy.Pow(denominator, -1)), variable
|
||||
|
||||
def integral_with_special_fraction(self, tokens):
|
||||
underscore_index = None
|
||||
caret_index = None
|
||||
|
||||
if "_" in tokens:
|
||||
# we need to know the index because the next item in the list is the
|
||||
# arguments for the lower bound of the integral
|
||||
underscore_index = tokens.index("_")
|
||||
|
||||
if "^" in tokens:
|
||||
# we need to know the index because the next item in the list is the
|
||||
# arguments for the upper bound of the integral
|
||||
caret_index = tokens.index("^")
|
||||
|
||||
lower_bound = tokens[underscore_index + 1] if underscore_index else None
|
||||
upper_bound = tokens[caret_index + 1] if caret_index else None
|
||||
|
||||
# we can't simply do something like `if (lower_bound and not upper_bound) ...` because this would
|
||||
# evaluate to `True` if the `lower_bound` is 0 and upper bound is non-zero
|
||||
if lower_bound is not None and upper_bound is None:
|
||||
# then one was given and the other wasn't
|
||||
raise LaTeXParsingError("Lower bound for the integral was found, but upper bound was not found.")
|
||||
|
||||
if upper_bound is not None and lower_bound is None:
|
||||
# then one was given and the other wasn't
|
||||
raise LaTeXParsingError("Upper bound for the integral was found, but lower bound was not found.")
|
||||
|
||||
integrand, differential_variable = tokens[-1]
|
||||
|
||||
if lower_bound is not None:
|
||||
# then we have a definite integral
|
||||
|
||||
# we can assume that either both the lower and upper bounds are given, or
|
||||
# neither of them are
|
||||
return sympy.Integral(integrand, (differential_variable, lower_bound, upper_bound))
|
||||
else:
|
||||
# we have an indefinite integral
|
||||
return sympy.Integral(integrand, differential_variable)
|
||||
|
||||
def group_curly_parentheses_special(self, tokens):
|
||||
underscore_index = tokens.index("_")
|
||||
caret_index = tokens.index("^")
|
||||
|
||||
# given the type of expressions we are parsing, we can assume that the lower limit
|
||||
# will always use braces around its arguments. This is because we don't support
|
||||
# converting unconstrained sums into SymPy expressions.
|
||||
|
||||
# first we isolate the bottom limit
|
||||
left_brace_index = tokens.index("{", underscore_index)
|
||||
right_brace_index = tokens.index("}", underscore_index)
|
||||
|
||||
bottom_limit = tokens[left_brace_index + 1: right_brace_index]
|
||||
|
||||
# next, we isolate the upper limit
|
||||
top_limit = tokens[caret_index + 1:]
|
||||
|
||||
# the code below will be useful for supporting things like `\sum_{n = 0}^{n = 5} n^2`
|
||||
# if "{" in top_limit:
|
||||
# left_brace_index = tokens.index("{", caret_index)
|
||||
# if left_brace_index != -1:
|
||||
# # then there's a left brace in the string, and we need to find the closing right brace
|
||||
# right_brace_index = tokens.index("}", caret_index)
|
||||
# top_limit = tokens[left_brace_index + 1: right_brace_index]
|
||||
|
||||
# print(f"top limit = {top_limit}")
|
||||
|
||||
index_variable = bottom_limit[0]
|
||||
lower_limit = bottom_limit[-1]
|
||||
upper_limit = top_limit[0] # for now, the index will always be 0
|
||||
|
||||
# print(f"return value = ({index_variable}, {lower_limit}, {upper_limit})")
|
||||
|
||||
return index_variable, lower_limit, upper_limit
|
||||
|
||||
def summation(self, tokens):
|
||||
return sympy.Sum(tokens[2], tokens[1])
|
||||
|
||||
def product(self, tokens):
|
||||
return sympy.Product(tokens[2], tokens[1])
|
||||
|
||||
def limit_dir_expr(self, tokens):
|
||||
caret_index = tokens.index("^")
|
||||
|
||||
if "{" in tokens:
|
||||
left_curly_brace_index = tokens.index("{", caret_index)
|
||||
direction = tokens[left_curly_brace_index + 1]
|
||||
else:
|
||||
direction = tokens[caret_index + 1]
|
||||
|
||||
if direction == "+":
|
||||
return tokens[0], "+"
|
||||
elif direction == "-":
|
||||
return tokens[0], "-"
|
||||
else:
|
||||
return tokens[0], "+-"
|
||||
|
||||
def group_curly_parentheses_lim(self, tokens):
|
||||
limit_variable = tokens[1]
|
||||
if isinstance(tokens[3], tuple):
|
||||
destination, direction = tokens[3]
|
||||
else:
|
||||
destination = tokens[3]
|
||||
direction = "+-"
|
||||
|
||||
return limit_variable, destination, direction
|
||||
|
||||
def limit(self, tokens):
|
||||
limit_variable, destination, direction = tokens[2]
|
||||
|
||||
return sympy.Limit(tokens[-1], limit_variable, destination, direction)
|
||||
|
||||
def differential(self, tokens):
|
||||
return tokens[1]
|
||||
|
||||
def derivative(self, tokens):
|
||||
return sympy.Derivative(tokens[-1], tokens[5])
|
||||
|
||||
def list_of_expressions(self, tokens):
|
||||
if len(tokens) == 1:
|
||||
# we return it verbatim because the function_applied node expects
|
||||
# a list
|
||||
return tokens
|
||||
else:
|
||||
def remove_tokens(args):
|
||||
if isinstance(args, Token):
|
||||
if args.type != "COMMA":
|
||||
# An unexpected token was encountered
|
||||
raise LaTeXParsingError("A comma token was expected, but some other token was encountered.")
|
||||
return False
|
||||
return True
|
||||
|
||||
return filter(remove_tokens, tokens)
|
||||
|
||||
def function_applied(self, tokens):
|
||||
return sympy.Function(tokens[0])(*tokens[2])
|
||||
|
||||
def min(self, tokens):
|
||||
return sympy.Min(*tokens[2])
|
||||
|
||||
def max(self, tokens):
|
||||
return sympy.Max(*tokens[2])
|
||||
|
||||
def bra(self, tokens):
|
||||
from sympy.physics.quantum import Bra
|
||||
return Bra(tokens[1])
|
||||
|
||||
def ket(self, tokens):
|
||||
from sympy.physics.quantum import Ket
|
||||
return Ket(tokens[1])
|
||||
|
||||
def inner_product(self, tokens):
|
||||
from sympy.physics.quantum import Bra, Ket, InnerProduct
|
||||
return InnerProduct(Bra(tokens[1]), Ket(tokens[3]))
|
||||
|
||||
def sin(self, tokens):
|
||||
return sympy.sin(tokens[1])
|
||||
|
||||
def cos(self, tokens):
|
||||
return sympy.cos(tokens[1])
|
||||
|
||||
def tan(self, tokens):
|
||||
return sympy.tan(tokens[1])
|
||||
|
||||
def csc(self, tokens):
|
||||
return sympy.csc(tokens[1])
|
||||
|
||||
def sec(self, tokens):
|
||||
return sympy.sec(tokens[1])
|
||||
|
||||
def cot(self, tokens):
|
||||
return sympy.cot(tokens[1])
|
||||
|
||||
def sin_power(self, tokens):
|
||||
exponent = tokens[2]
|
||||
if exponent == -1:
|
||||
return sympy.asin(tokens[-1])
|
||||
else:
|
||||
return sympy.Pow(sympy.sin(tokens[-1]), exponent)
|
||||
|
||||
def cos_power(self, tokens):
|
||||
exponent = tokens[2]
|
||||
if exponent == -1:
|
||||
return sympy.acos(tokens[-1])
|
||||
else:
|
||||
return sympy.Pow(sympy.cos(tokens[-1]), exponent)
|
||||
|
||||
def tan_power(self, tokens):
|
||||
exponent = tokens[2]
|
||||
if exponent == -1:
|
||||
return sympy.atan(tokens[-1])
|
||||
else:
|
||||
return sympy.Pow(sympy.tan(tokens[-1]), exponent)
|
||||
|
||||
def csc_power(self, tokens):
|
||||
exponent = tokens[2]
|
||||
if exponent == -1:
|
||||
return sympy.acsc(tokens[-1])
|
||||
else:
|
||||
return sympy.Pow(sympy.csc(tokens[-1]), exponent)
|
||||
|
||||
def sec_power(self, tokens):
|
||||
exponent = tokens[2]
|
||||
if exponent == -1:
|
||||
return sympy.asec(tokens[-1])
|
||||
else:
|
||||
return sympy.Pow(sympy.sec(tokens[-1]), exponent)
|
||||
|
||||
def cot_power(self, tokens):
|
||||
exponent = tokens[2]
|
||||
if exponent == -1:
|
||||
return sympy.acot(tokens[-1])
|
||||
else:
|
||||
return sympy.Pow(sympy.cot(tokens[-1]), exponent)
|
||||
|
||||
def arcsin(self, tokens):
|
||||
return sympy.asin(tokens[1])
|
||||
|
||||
def arccos(self, tokens):
|
||||
return sympy.acos(tokens[1])
|
||||
|
||||
def arctan(self, tokens):
|
||||
return sympy.atan(tokens[1])
|
||||
|
||||
def arccsc(self, tokens):
|
||||
return sympy.acsc(tokens[1])
|
||||
|
||||
def arcsec(self, tokens):
|
||||
return sympy.asec(tokens[1])
|
||||
|
||||
def arccot(self, tokens):
|
||||
return sympy.acot(tokens[1])
|
||||
|
||||
def sinh(self, tokens):
|
||||
return sympy.sinh(tokens[1])
|
||||
|
||||
def cosh(self, tokens):
|
||||
return sympy.cosh(tokens[1])
|
||||
|
||||
def tanh(self, tokens):
|
||||
return sympy.tanh(tokens[1])
|
||||
|
||||
def asinh(self, tokens):
|
||||
return sympy.asinh(tokens[1])
|
||||
|
||||
def acosh(self, tokens):
|
||||
return sympy.acosh(tokens[1])
|
||||
|
||||
def atanh(self, tokens):
|
||||
return sympy.atanh(tokens[1])
|
||||
|
||||
def abs(self, tokens):
|
||||
return sympy.Abs(tokens[1])
|
||||
|
||||
def floor(self, tokens):
|
||||
return sympy.floor(tokens[1])
|
||||
|
||||
def ceil(self, tokens):
|
||||
return sympy.ceiling(tokens[1])
|
||||
|
||||
def factorial(self, tokens):
|
||||
return sympy.factorial(tokens[0])
|
||||
|
||||
def conjugate(self, tokens):
|
||||
return sympy.conjugate(tokens[1])
|
||||
|
||||
def square_root(self, tokens):
|
||||
if len(tokens) == 2:
|
||||
# then there was no square bracket argument
|
||||
return sympy.sqrt(tokens[1])
|
||||
elif len(tokens) == 3:
|
||||
# then there _was_ a square bracket argument
|
||||
return sympy.root(tokens[2], tokens[1])
|
||||
|
||||
def exponential(self, tokens):
|
||||
return sympy.exp(tokens[1])
|
||||
|
||||
def log(self, tokens):
|
||||
if tokens[0].type == "FUNC_LG":
|
||||
# we don't need to check if there's an underscore or not because having one
|
||||
# in this case would be meaningless
|
||||
# TODO: ANTLR refers to ISO 80000-2:2019. should we keep base 10 or base 2?
|
||||
return sympy.log(tokens[1], 10)
|
||||
elif tokens[0].type == "FUNC_LN":
|
||||
return sympy.log(tokens[1])
|
||||
elif tokens[0].type == "FUNC_LOG":
|
||||
# we check if a base was specified or not
|
||||
if "_" in tokens:
|
||||
# then a base was specified
|
||||
return sympy.log(tokens[3], tokens[2])
|
||||
else:
|
||||
# a base was not specified
|
||||
return sympy.log(tokens[1])
|
||||
|
||||
def _extract_differential_symbol(self, s: str):
|
||||
differential_symbols = {"d", r"\text{d}", r"\mathrm{d}"}
|
||||
|
||||
differential_symbol = next((symbol for symbol in differential_symbols if symbol in s), None)
|
||||
|
||||
return differential_symbol
|
||||
|
||||
def matrix(self, tokens):
|
||||
def is_matrix_row(x):
|
||||
return (isinstance(x, Tree) and x.data == "matrix_row")
|
||||
|
||||
def is_not_col_delim(y):
|
||||
return (not isinstance(y, Token) or y.type != "MATRIX_COL_DELIM")
|
||||
|
||||
matrix_body = tokens[1].children
|
||||
return sympy.Matrix([[y for y in x.children if is_not_col_delim(y)]
|
||||
for x in matrix_body if is_matrix_row(x)])
|
||||
|
||||
def determinant(self, tokens):
|
||||
if len(tokens) == 2: # \det A
|
||||
if not self._obj_is_sympy_Matrix(tokens[1]):
|
||||
raise LaTeXParsingError("Cannot take determinant of non-matrix.")
|
||||
|
||||
return tokens[1].det()
|
||||
|
||||
if len(tokens) == 3: # | A |
|
||||
return self.matrix(tokens).det()
|
||||
|
||||
def trace(self, tokens):
|
||||
if not self._obj_is_sympy_Matrix(tokens[1]):
|
||||
raise LaTeXParsingError("Cannot take trace of non-matrix.")
|
||||
|
||||
return sympy.Trace(tokens[1])
|
||||
|
||||
def adjugate(self, tokens):
|
||||
if not self._obj_is_sympy_Matrix(tokens[1]):
|
||||
raise LaTeXParsingError("Cannot take adjugate of non-matrix.")
|
||||
|
||||
# need .doit() since MatAdd does not support .adjugate() method
|
||||
return tokens[1].doit().adjugate()
|
||||
|
||||
def _obj_is_sympy_Matrix(self, obj):
|
||||
if hasattr(obj, "is_Matrix"):
|
||||
return obj.is_Matrix
|
||||
|
||||
return isinstance(obj, sympy.Matrix)
|
||||
|
||||
def _handle_division(self, numerator, denominator):
|
||||
if self._obj_is_sympy_Matrix(denominator):
|
||||
raise LaTeXParsingError("Cannot divide by matrices like this since "
|
||||
"it is not clear if left or right multiplication "
|
||||
"by the inverse is intended. Try explicitly "
|
||||
"multiplying by the inverse instead.")
|
||||
|
||||
if self._obj_is_sympy_Matrix(numerator):
|
||||
return sympy.MatMul(numerator, sympy.Pow(denominator, -1))
|
||||
|
||||
return sympy.Mul(numerator, sympy.Pow(denominator, -1))
|
||||
Reference in New Issue
Block a user