math2tex 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,67 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ *.manifest
31
+ *.spec
32
+
33
+ # Installer logs
34
+ pip-log.txt
35
+ pip-delete-this-directory.txt
36
+
37
+ # Unit test / coverage reports
38
+ htmlcov/
39
+ .tox/
40
+ .nox/
41
+ .coverage
42
+ .coverage.*
43
+ .cache
44
+ nosetests.xml
45
+ coverage.xml
46
+ *.cover
47
+ *.pycover
48
+ .hypothesis/
49
+ .pytest_cache/
50
+ cover/
51
+
52
+ # Virtual environments
53
+ .venv
54
+ venv/
55
+ ENV/
56
+ env/
57
+ env.bak/
58
+ venv.bak/
59
+
60
+ # IDEs
61
+ .idea/
62
+ .vscode/
63
+ *.swp
64
+ *.swo
65
+
66
+ legacy_out.txt
67
+ test.in
math2tex-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 NicknamedTwice
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,135 @@
1
+ Metadata-Version: 2.4
2
+ Name: math2tex
3
+ Version: 1.0.0
4
+ Summary: A high-performance compiler for translating plaintext mathematics into LaTeX.
5
+ Author: NicknamedTwice
6
+ License: MIT License
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+
11
+ # math2tex
12
+
13
+ A compiler that converts plaintext mathematical notation into LaTeX.
14
+
15
+ `math2tex` is a recursive-descent parser written in Python. It translates mathematical expressions written in plain text into valid LaTeX while preserving operator precedence and mathematical structure. It supports everything from basic algebra to calculus, complex analysis, matrices, piecewise functions, and user-defined macros.
16
+
17
+ **Note:** The generated LaTeX uses commands provided by the `physics` package (for example `\dd{x}` and `\abs{x}`). Add the following to your document preamble:
18
+
19
+ ```latex
20
+ \usepackage{physics}
21
+ ```
22
+
23
+ ## Features
24
+
25
+ - Correct handling of operator precedence and associativity.
26
+ - User-defined macros.
27
+ - Support for fractions, powers, roots, sums, products, limits, derivatives, and integrals.
28
+ - Support for matrices and piecewise functions.
29
+ - Recursive-descent parser with an Abstract Syntax Tree (AST).
30
+ - Comprehensive automated test suite.
31
+
32
+ ## Installation
33
+
34
+ Install from a local clone:
35
+
36
+ ```bash
37
+ pip install .
38
+ ```
39
+
40
+ Or install from PyPI:
41
+
42
+ ```bash
43
+ pip install math2tex
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ Start the interactive command-line interface:
49
+
50
+ ```bash
51
+ math2tex
52
+ ```
53
+
54
+ You can then enter mathematical expressions and receive the corresponding LaTeX output.
55
+
56
+ ## Examples
57
+
58
+ ### Abel–Plana Formula
59
+
60
+ **Input**
61
+
62
+ ```text
63
+ sum_(n=0)^inf f(n) = int_0^inf f(x) dx + 1/2 f(0) + i int_0^inf (f(i y) - f(-i y)) / (e^(2 pi y) - 1) dy
64
+ ```
65
+
66
+ **Output**
67
+
68
+ ```latex
69
+ \sum_{n=0}^{\infty} f(n)=\int_{0}^{\infty} f(x)\dd{x}+\frac{1}{2}f(0)+i\int_{0}^{\infty}\frac{f(iy)-f(-iy)}{e^{2\pi y}-1}\dd{y}
70
+ ```
71
+
72
+ ### Contour Integral
73
+
74
+ **Input**
75
+
76
+ ```text
77
+ oint_C f(z) dz = 2 pi i sum Res(f, a_k)
78
+ ```
79
+
80
+ **Output**
81
+
82
+ ```latex
83
+ \oint_{C}f(z)\dd{z}=2\pi i\sum \operatorname{Res}(f,a_{k})
84
+ ```
85
+
86
+ ### Nested Fractions
87
+
88
+ **Input**
89
+
90
+ ```text
91
+ 1 / (1 + 1 / (1 + x))
92
+ ```
93
+
94
+ **Output**
95
+
96
+ ```latex
97
+ \frac{1}{1+\frac{1}{1+x}}
98
+ ```
99
+
100
+ ### Matrix
101
+
102
+ **Input**
103
+
104
+ ```text
105
+ [[1, 2], [3, 4]]
106
+ ```
107
+
108
+ **Output**
109
+
110
+ ```latex
111
+ \begin{pmatrix}
112
+ 1 & 2 \\
113
+ 3 & 4
114
+ \end{pmatrix}
115
+ ```
116
+
117
+ ## Project Structure
118
+
119
+ - **lexer.py** – Converts input text into a stream of tokens.
120
+ - **parser.py** – Builds an Abstract Syntax Tree (AST) from the token stream.
121
+ - **ast_nodes.py** – Defines the AST node types.
122
+ - **macros.py** – Handles user-defined macro expansion.
123
+ - **formatter.py** – Converts the AST into LaTeX.
124
+
125
+ ## Testing
126
+
127
+ Run the test suite with:
128
+
129
+ ```bash
130
+ pytest
131
+ ```
132
+
133
+ ## License
134
+
135
+ This project is licensed under the MIT License. See `LICENSE` for details.
@@ -0,0 +1,125 @@
1
+ # math2tex
2
+
3
+ A compiler that converts plaintext mathematical notation into LaTeX.
4
+
5
+ `math2tex` is a recursive-descent parser written in Python. It translates mathematical expressions written in plain text into valid LaTeX while preserving operator precedence and mathematical structure. It supports everything from basic algebra to calculus, complex analysis, matrices, piecewise functions, and user-defined macros.
6
+
7
+ **Note:** The generated LaTeX uses commands provided by the `physics` package (for example `\dd{x}` and `\abs{x}`). Add the following to your document preamble:
8
+
9
+ ```latex
10
+ \usepackage{physics}
11
+ ```
12
+
13
+ ## Features
14
+
15
+ - Correct handling of operator precedence and associativity.
16
+ - User-defined macros.
17
+ - Support for fractions, powers, roots, sums, products, limits, derivatives, and integrals.
18
+ - Support for matrices and piecewise functions.
19
+ - Recursive-descent parser with an Abstract Syntax Tree (AST).
20
+ - Comprehensive automated test suite.
21
+
22
+ ## Installation
23
+
24
+ Install from a local clone:
25
+
26
+ ```bash
27
+ pip install .
28
+ ```
29
+
30
+ Or install from PyPI:
31
+
32
+ ```bash
33
+ pip install math2tex
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ Start the interactive command-line interface:
39
+
40
+ ```bash
41
+ math2tex
42
+ ```
43
+
44
+ You can then enter mathematical expressions and receive the corresponding LaTeX output.
45
+
46
+ ## Examples
47
+
48
+ ### Abel–Plana Formula
49
+
50
+ **Input**
51
+
52
+ ```text
53
+ sum_(n=0)^inf f(n) = int_0^inf f(x) dx + 1/2 f(0) + i int_0^inf (f(i y) - f(-i y)) / (e^(2 pi y) - 1) dy
54
+ ```
55
+
56
+ **Output**
57
+
58
+ ```latex
59
+ \sum_{n=0}^{\infty} f(n)=\int_{0}^{\infty} f(x)\dd{x}+\frac{1}{2}f(0)+i\int_{0}^{\infty}\frac{f(iy)-f(-iy)}{e^{2\pi y}-1}\dd{y}
60
+ ```
61
+
62
+ ### Contour Integral
63
+
64
+ **Input**
65
+
66
+ ```text
67
+ oint_C f(z) dz = 2 pi i sum Res(f, a_k)
68
+ ```
69
+
70
+ **Output**
71
+
72
+ ```latex
73
+ \oint_{C}f(z)\dd{z}=2\pi i\sum \operatorname{Res}(f,a_{k})
74
+ ```
75
+
76
+ ### Nested Fractions
77
+
78
+ **Input**
79
+
80
+ ```text
81
+ 1 / (1 + 1 / (1 + x))
82
+ ```
83
+
84
+ **Output**
85
+
86
+ ```latex
87
+ \frac{1}{1+\frac{1}{1+x}}
88
+ ```
89
+
90
+ ### Matrix
91
+
92
+ **Input**
93
+
94
+ ```text
95
+ [[1, 2], [3, 4]]
96
+ ```
97
+
98
+ **Output**
99
+
100
+ ```latex
101
+ \begin{pmatrix}
102
+ 1 & 2 \\
103
+ 3 & 4
104
+ \end{pmatrix}
105
+ ```
106
+
107
+ ## Project Structure
108
+
109
+ - **lexer.py** – Converts input text into a stream of tokens.
110
+ - **parser.py** – Builds an Abstract Syntax Tree (AST) from the token stream.
111
+ - **ast_nodes.py** – Defines the AST node types.
112
+ - **macros.py** – Handles user-defined macro expansion.
113
+ - **formatter.py** – Converts the AST into LaTeX.
114
+
115
+ ## Testing
116
+
117
+ Run the test suite with:
118
+
119
+ ```bash
120
+ pytest
121
+ ```
122
+
123
+ ## License
124
+
125
+ This project is licensed under the MIT License. See `LICENSE` for details.
@@ -0,0 +1,130 @@
1
+ import subprocess
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ GREEN = "\033[92m"
6
+ RED = "\033[91m"
7
+ YELLOW = "\033[93m"
8
+ RESET = "\033[0m"
9
+ BOLD = "\033[1m"
10
+
11
+
12
+ def load_tests(path):
13
+ tests = []
14
+
15
+ for line in path.read_text(encoding="utf8").splitlines():
16
+ line=line.strip()
17
+
18
+ if not line or line.startswith("#"):
19
+ continue
20
+
21
+ if "=" not in line:
22
+ continue
23
+
24
+ expr, expected = line.rsplit("=",1)
25
+
26
+ tests.append(
27
+ (
28
+ expr.strip(),
29
+ expected.strip()
30
+ )
31
+ )
32
+
33
+ return tests
34
+
35
+
36
+ def run_math2tex(expr):
37
+ code = f"""
38
+ from math2tex import math2tex
39
+ print(math2tex({expr!r}))
40
+ """
41
+
42
+ result = subprocess.run(
43
+ [
44
+ sys.executable,
45
+ "-c",
46
+ code
47
+ ],
48
+ capture_output=True,
49
+ text=True
50
+ )
51
+
52
+ return result.stdout.strip()
53
+
54
+
55
+ def main():
56
+
57
+ tests = load_tests(
58
+ Path(__file__).parent / "tests.txt"
59
+ )
60
+
61
+ passed=0
62
+ failed=0
63
+
64
+
65
+ print(BOLD+"Running math2tex examples..."+RESET)
66
+ print()
67
+
68
+
69
+ for i,(expr,expected) in enumerate(tests,1):
70
+
71
+ actual = run_math2tex(expr)
72
+
73
+ if actual == expected:
74
+ print(
75
+ GREEN+
76
+ f"OK {i:03} PASS"+
77
+ RESET,
78
+ expr
79
+ )
80
+ passed+=1
81
+
82
+ else:
83
+ print(
84
+ RED+
85
+ f"ERR {i:03} FAIL"+
86
+ RESET,
87
+ expr
88
+ )
89
+
90
+ print(
91
+ YELLOW+
92
+ " expected:",
93
+ expected,
94
+ RESET
95
+ )
96
+
97
+ print(
98
+ YELLOW+
99
+ " actual: ",
100
+ actual,
101
+ RESET
102
+ )
103
+
104
+ print()
105
+
106
+ failed+=1
107
+
108
+
109
+ print("="*60)
110
+
111
+ print(
112
+ GREEN+
113
+ f"Passed: {passed}"+
114
+ RESET
115
+ )
116
+
117
+ print(
118
+ RED+
119
+ f"Failed: {failed}"+
120
+ RESET
121
+ )
122
+
123
+
124
+ if failed:
125
+ sys.exit(1)
126
+
127
+
128
+
129
+ if __name__=="__main__":
130
+ main()
@@ -0,0 +1,119 @@
1
+ # --- 1. Deep Nesting & Extreme Fractions ---
2
+ (((a))) = a
3
+ (a/b)/(c/d)/(e/f) = \frac{\frac{\frac{a}{b}}{\frac{c}{d}}}{\frac{e}{f}}
4
+ 1/(1+1/(1+1/x)) = \frac{1}{1+\frac{1}{1+\frac{1}{x}}}
5
+ sqrt(sqrt(sqrt(x))) = \sqrt{\sqrt{\sqrt{x}}}
6
+ root(3, root(4, x)) = \sqrt[3]{\sqrt[4]{x}}
7
+ (x+y)/(x-y) + (x-y)/(x+y) = \frac{x+y}{x-y}+\frac{x-y}{x+y}
8
+ ((x^2)^3)^4 = \left(\left(x^{2}\right)^{3}\right)^{4}
9
+ a / b / c / d = \frac{\frac{\frac{a}{b}}{c}}{d}
10
+ (a/(b/c))/d = \frac{\frac{a}{\frac{b}{c}}}{d}
11
+ 1/2 x = \frac{1}{2} x
12
+
13
+ # --- 2. Exponents, Subscripts, and Primes ---
14
+ x^y^z^w = x^{y^{z^{w}}}
15
+ a_b_c_d = a_{b_{c_{d}}}
16
+ x_i^2 = x_{i}^{2}
17
+ x^2_i = x^{2}_{i}
18
+ f'''(x) = f'''(x)
19
+ g''''(x) = g''''(x)
20
+ y'_n^2 = y'_{n}^{2}
21
+ (x_i^2)^3 = \left(x_{i}^{2}\right)^{3}
22
+ e^(-i pi) + 1 = e^{-i \pi}+1
23
+ e^(x^2 + y^2) = e^{x^{2}+y^{2}}
24
+
25
+ # --- 3. Trigonometric, Hyperbolic & Logarithmic ---
26
+ sin(cos(tan(x))) = \sin(\cos(\tan(x)))
27
+ arcsin(x) + arccos(x) = \arcsin(x)+\arccos(x)
28
+ sinh(x) cosh(y) = \sinh(x) \cosh(y)
29
+ tanh(x) coth(y) = \tanh(x) \coth(y)
30
+ ln(exp(x)) = \ln(e^{x})
31
+ log(x^2) = \log(x^{2})
32
+ sin^2(x) + cos^2(x) = \sin^{2}(x)+\cos^{2}(x)
33
+ sec(x) csc(x) = \sec(x) \csc(x)
34
+ arctan(y/x) = \arctan\left(\frac{y}{x}\right)
35
+ sin(x) / cos(x) = \frac{\sin(x)}{\cos(x)}
36
+
37
+ # --- 4. Complex Analysis & Improper Integrals ---
38
+ oint_C f(z)/(z-a) dz = \oint_{C}\frac{f(z)}{z-a}\dd{z}
39
+ integral_(-infty)^infty e^(-x^2) dx = \int_{-\infty}^{\infty} e^{(-x)^{2}}\dd{x}
40
+ iiint_V curl F cdot dS = \iiint_{V} \curl F \cdot \dd{S}
41
+ integral_0^infty x^(z-1) e^(-x) dx = \int_{0}^{\infty} x^{z-1} e^{-x}\dd{x}
42
+ Re(z) + i Im(z) = \Re(z)+i \Im(z)
43
+ norm(z_1 + z_2) = \norm{z_{1}+z_{2}}
44
+ abs(z_1 z_2) = \abs{z_{1} z_{2}}
45
+ integral_0^infty sin(x)/x dx = \int_{0}^{\infty}\frac{\sin(x)}{x}\dd{x}
46
+ oint_gamma (e^z)/z dz = \oint_{\gamma}\frac{e^{z}}{z}\dd{z}
47
+ oiint_S E cdot dA = \oiint_{S} E \cdot \dd{A}
48
+
49
+ # --- 5. Combinatorics, Factorials & Postfix ---
50
+ n! / (k! (n-k)!) = \frac{n!}{k!\left(n-k\right)!}
51
+ (2n)! / (n! n!) = \frac{\left(2n\right)!}{n! n!}
52
+ n choose k = \binom{n}{k}
53
+ binom(n+1, k+1) = \binom{n+1}{k+1}
54
+ n! ! = n!!
55
+ (x+1)! = \left(x+1\right)!
56
+ floor(x) + ceil(y) = \left\lfloor x\right\rfloor+\left\lceil y\right\rceil
57
+ abs(-x) = \abs{-x}
58
+ norm(x-y) = \norm{x-y}
59
+ (n-1) choose (k-1) = \binom{n-1}{k-1}
60
+
61
+ # --- 6. Vectors, Accents, and Fonts ---
62
+ vec(v) cdot vec(w) = \vec{v} \cdot \vec{w}
63
+ vec(v) cross vec(w) = \vec{v} \times \vec{w}
64
+ hat(i) + hat(j) + hat(k) = \hat{i}+\hat{j}+\hat{k}
65
+ ddot(x) + omega^2 x = \ddot{x}+\omega^{2} x
66
+ bar(z_1 + z_2) = \bar{z_{1}+z_{2}}
67
+ tilde(f)(xi) = \tilde{f}(\xi)
68
+ mathbb(R) subset mathbb(C) = \mathbb{R} \subset \mathbb{C}
69
+ mathcal(O)(n log n) = \mathcal{O}(n \log n)
70
+ mathbf(A) mathbf(x) = \mathbf{A} \mathbf{x}
71
+ mathfrak(g) oplus mathfrak(h) = \mathfrak{g} \oplus \mathfrak{h}
72
+
73
+ # --- 7. Big Operators (Without Equals Signs) ---
74
+ sum_(i>0)^n i^2 = \sum_{i>0}^{n} i^{2}
75
+ prod_(p in P) (1 - 1/p^s) = \prod_{p \in P}\left(1-\frac{1}{p^{s}}\right)
76
+ lim_(x -> 0) sin(x)/x = \lim_{x\to 0}\frac{\sin(x)}{x}
77
+ limsup_(n -> infty) x_n = \limsup_{n\to \infty} x_{n}
78
+ liminf_(n -> 0) y_n = \liminf_{n\to 0} y_{n}
79
+ sup_(x in S) f(x) = \sup_{x \in S} f(x)
80
+ inf_(x > 0) g(x) = \inf_{x>0} g(x)
81
+ sum_(n>0)^infty f(n) = \sum_{n>0}^{\infty} f(n)
82
+ prod_(k>1)^N (1-1/k) = \prod_{k>1}^{N}\left(1-\frac{1}{k}\right)
83
+ lim_(h -> 0) (f(x+h)-f(x))/h = \lim_{h\to 0}\frac{f\left(x+h\right)-f(x)}{h}
84
+
85
+ # --- 8. Derivatives (Partial, Mixed, High-order) ---
86
+ d/dx (u v) = \dv{u v}{x}
87
+ d^2/dt^2 x(t) = \dv[2]{x(t)}{t}
88
+ partial/partial x f(x,y) = \pdv{f(x,y)}{x}
89
+ partial^2 f / partial x partial y = \pdv{f}{x}{y}
90
+ partial^3 f / partial x^3 = \pdv[3]{f}{x}
91
+ nabla f = \nabla f
92
+ nabla^2 phi = \nabla^{2} \phi
93
+ laplacian u = \laplacian u
94
+ curl vec(F) = \curl \vec{F}
95
+ grad f = \grad f
96
+
97
+ # --- 9. Logical, Relational, and Set Theory ---
98
+ A cup B cup C = A \cup B \cup C
99
+ X cap Y cap Z = X \cap Y \cap Z
100
+ x in A implies x in B = x \in A \implies x \in B
101
+ P iff Q = P \iff Q
102
+ A setminus B subset C = A \setminus B \subset C
103
+ forall x exists y = \forall x \exists y
104
+ nexists z = \nexists z
105
+ a approx b approx c = a \approx b \approx c
106
+ x notin emptyset = x \notin \emptyset
107
+ A subseteq B supseteq C = A \subseteq B \supseteq C
108
+
109
+ # --- 10. Adversarial / Edge Cases / Shorthand ---
110
+ -x^2 = (-x)^{2}
111
+ -(x^2) = -x^{2}
112
+ --x = --x
113
+ +-x -+ y = \pm x \mp y
114
+ a +- b -+ c = a \pm b \mp c
115
+ 2x 3y = 2x 3y
116
+ sin x cos y = \sin x \cos y
117
+ ln x exp y = \ln x \exp y
118
+ a...b = a\ldots b
119
+ a cdots b vdots c ddots d = a \cdots b \vdots c \ddots d
@@ -0,0 +1,84 @@
1
+ """
2
+ math2tex — A professional plaintext-to-LaTeX mathematical expression engine.
3
+
4
+ Converts informal math notation into high-quality, publication-ready LaTeX.
5
+ Designed for use with the LaTeX ``physics`` package.
6
+
7
+ Public API
8
+ ----------
9
+ .. autofunction:: math2tex
10
+ .. autofunction:: math2tex_multiline
11
+ .. autofunction:: parse_macro_def
12
+ """
13
+
14
+ from typing import List, Optional
15
+
16
+ from .config import Config
17
+ from .lexer import ParseError, _preprocess, tokenize
18
+ from .parser import Parser
19
+ from .formatter import LatexFormatter
20
+ from .macros import parse_macro_def, Macro, substitute
21
+
22
+ __all__ = [
23
+ 'math2tex',
24
+ 'math2tex_multiline',
25
+ 'parse_macro_def',
26
+ 'Config',
27
+ 'ParseError',
28
+ 'Macro',
29
+ ]
30
+
31
+
32
+ def math2tex(expression: str,
33
+ macro_env: Optional[dict] = None) -> str:
34
+ try:
35
+ if macro_env is None:
36
+ macro_env = {}
37
+
38
+ # Expand zero-argument macros before lexing
39
+ macro = macro_env.get(expression.strip())
40
+ if macro is not None and not macro.args:
41
+ formatter = LatexFormatter({})
42
+ return formatter.format(macro.body, is_root=True)
43
+
44
+ preprocessed, text_map = _preprocess(expression)
45
+ tokens = tokenize(preprocessed)
46
+ parser = Parser(tokens, text_map=text_map, macro_env=macro_env)
47
+ ast = parser.parse()
48
+ formatter = LatexFormatter(text_map)
49
+ return formatter.format(ast, is_root=True)
50
+
51
+ except Exception as e:
52
+ return f'% Parse Error: {e}'
53
+
54
+
55
+ def math2tex_multiline(expressions: List[str],
56
+ env: str = None,
57
+ macro_env: Optional[dict] = None) -> str:
58
+ if env is None:
59
+ env = Config.DEFAULT_ENV
60
+ if macro_env is None:
61
+ macro_env = {}
62
+
63
+ align_mode = (env == 'align*')
64
+
65
+ formatted_lines: List[str] = []
66
+ for expr in expressions:
67
+ if not expr.strip():
68
+ continue
69
+ try:
70
+ preprocessed, text_map = _preprocess(expr)
71
+ tokens = tokenize(preprocessed)
72
+ parser = Parser(tokens, text_map=text_map, macro_env=macro_env)
73
+ ast = parser.parse()
74
+ formatter = LatexFormatter(text_map, align_mode=align_mode)
75
+ formatted = formatter.format(ast, is_root=True)
76
+ formatted_lines.append(formatted)
77
+ except Exception as e:
78
+ formatted_lines.append(f'% Parse Error: {e}')
79
+
80
+ if env == 'none':
81
+ return '\n'.join(formatted_lines)
82
+
83
+ content = ' \\\\\n'.join(formatted_lines)
84
+ return f'\\begin{{{env}}}\n{content}\n\\end{{{env}}}'