I am done

This commit is contained in:
2024-10-30 22:14:35 +01:00
parent 720dc28c09
commit 40e2a747cf
36901 changed files with 5011519 additions and 0 deletions

View File

@ -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.

View File

@ -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;

View File

@ -0,0 +1,66 @@
from sympy.external import import_module
from sympy.utilities.decorator import doctest_depends_on
from sympy.parsing.latex.lark import LarkLaTeXParser, TransformToSymPyExpr, parse_latex_lark # noqa
from .errors import LaTeXParsingError # noqa
__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
"""
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.")

View File

@ -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

View File

@ -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

View File

@ -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.readlines()]
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()

View File

@ -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

View File

@ -0,0 +1,2 @@
class LaTeXParsingError(Exception):
pass

View File

@ -0,0 +1,2 @@
from .latex_parser import parse_latex_lark, LarkLaTeXParser # noqa
from .transformer import TransformToSymPyExpr # noqa

View File

@ -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

View File

@ -0,0 +1,327 @@
%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]/
BASIC_SUBSCRIPTED_SYMBOL: /([a-zA-Z])_(([A-Za-z0-9]|[a-zA-Z]+)|\{([A-Za-z0-9]|[a-zA-Z]+)\})/
SYMBOL_WITH_GREEK_SUBSCRIPT: /([a-zA-Z])_/ GREEK_SYMBOL | /([a-zA-Z])_/ L_BRACE GREEK_SYMBOL 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_SUBSCRIPTED_SYMBOL: GREEK_SYMBOL /_(([A-Za-z0-9]|[a-zA-Z]+)|\{([A-Za-z0-9]|[a-zA-Z]+)\})/
%import common.DIGIT -> DIGIT
//////////////////// grammar //////////////////////
latex_string: _relation | _expression
_one_letter_symbol: SYMBOL
| BASIC_SUBSCRIPTED_SYMBOL
| SYMBOL_WITH_GREEK_SUBSCRIPT
| GREEK_SUBSCRIPTED_SYMBOL
| GREEK_SYMBOL
multi_letter_symbol: CMD_MATHIT L_BRACE /[a-zA-Z]+(\s+[a-zA-Z]+)*/ R_BRACE
number: /\d+(\.\d*)?/
_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
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
_expression_func: _expression_core
| group_round_parentheses
| fraction
| binomial
| _function
_expression_power: _expression_func | superscript
_expression_mul: _expression_power
| mul | div | adjacent_expressions
| _integral// | derivative
| summation | product
| limit
_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
fraction: _basic_fraction
| _simple_fraction
| _general_fraction
_basic_fraction: CMD_FRAC DIGIT (DIGIT | SYMBOL | GREEK_SYMBOL)
_simple_fraction: CMD_FRAC DIGIT group_curly_parentheses
| CMD_FRAC group_curly_parentheses (DIGIT | SYMBOL | GREEK_SYMBOL)
_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)
_simple_binomial: CMD_BINOM DIGIT group_curly_parentheses
| CMD_BINOM group_curly_parentheses (DIGIT | SYMBOL | GREEK_SYMBOL)
_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
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 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

View File

@ -0,0 +1,146 @@
import os
import logging
import re
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:
with open(os.path.join(grammar_dir_path, "latex.lark"), encoding="utf-8") as f:
latex_grammar = f.read()
else:
with open(grammar_file, encoding="utf-8") as f:
latex_grammar = f.read()
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

View File

@ -0,0 +1,557 @@
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 # type: ignore
else:
class Transformer: # type: ignore
def transform(self, *args):
pass
class Token: # 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(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 BASIC_SUBSCRIPTED_SYMBOL(self, tokens):
symbol, sub = tokens.value.split("_")
if sub.startswith("{"):
return sympy.Symbol("%s_{%s}" % (symbol, sub[1:-1]))
else:
return sympy.Symbol("%s_{%s}" % (symbol, sub))
def GREEK_SUBSCRIPTED_SYMBOL(self, tokens):
greek_letter, sub = tokens.value.split("_")
greek_letter = re.sub("var", "", greek_letter[1:])
if sub.startswith("{"):
return sympy.Symbol("%s_{%s}" % (greek_letter, sub[1:-1]))
else:
return sympy.Symbol("%s_{%s}" % (greek_letter, sub))
def SYMBOL_WITH_GREEK_SUBSCRIPT(self, tokens):
symbol, sub = tokens.value.split("_")
if sub.startswith("{"):
greek_letter = sub[2:-1]
greek_letter = re.sub("var", "", greek_letter)
return sympy.Symbol("%s_{%s}" % (symbol, greek_letter))
else:
greek_letter = sub[1:]
greek_letter = re.sub("var", "", greek_letter)
return sympy.Symbol("%s_{%s}" % (symbol, greek_letter))
def multi_letter_symbol(self, tokens):
return sympy.Symbol(tokens[2])
def number(self, tokens):
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):
return sympy.Add(tokens[0], tokens[2])
def sub(self, tokens):
if len(tokens) == 2:
return -tokens[1]
elif len(tokens) == 3:
return sympy.Add(tokens[0], -tokens[2])
def mul(self, tokens):
return sympy.Mul(tokens[0], tokens[2])
def div(self, tokens):
return sympy.Mul(tokens[0], sympy.Pow(tokens[2], -1))
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):
return sympy.Pow(tokens[0], tokens[2])
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 sympy.Mul(numerator, sympy.Pow(denominator, -1))
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