Skip to content

Commit 05383ca

Browse files
ZibingZhangZibing Zhang
and
Zibing Zhang
authored
Factor out expression codegen from function codegen (#155)
* please work * moved back * same trick * move back * factor expr codegen out * add visit_, remove expr tests from func * suggestiosn * codegen test * using generic visit * extract method * only stmt codegen * reduce stop param * fix compatability issues * abc * Visit an XYZ node. * standard test method names * suggestions Co-authored-by: Zibing Zhang <zizhang@hubspot.com>
1 parent 9e0f77f commit 05383ca

14 files changed

+1735
-1717
lines changed

src/latexify/analyzers.py

+41
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import ast
66
import dataclasses
7+
import sys
78

89
from latexify import ast_utils, exceptions
910

@@ -62,3 +63,43 @@ def analyze_range(node: ast.Call) -> RangeInfo:
6263
stop_int=ast_utils.extract_int_or_none(stop),
6364
step_int=ast_utils.extract_int_or_none(step),
6465
)
66+
67+
68+
def reduce_stop_parameter(node: ast.expr) -> ast.expr:
69+
"""Adjusts the stop expression of the range.
70+
71+
This function tries to convert the syntax as follows:
72+
* n + 1 --> n
73+
* n + 2 --> n + 1
74+
* n - 1 --> n - 2
75+
76+
Args:
77+
node: The target expression.
78+
79+
Returns:
80+
Converted expression.
81+
"""
82+
if not (isinstance(node, ast.BinOp) and isinstance(node.op, (ast.Add, ast.Sub))):
83+
return ast.BinOp(left=node, op=ast.Sub(), right=ast_utils.make_constant(1))
84+
85+
# Treatment for Python 3.7.
86+
rhs = (
87+
ast.Constant(value=node.right.n)
88+
if sys.version_info.minor < 8 and isinstance(node.right, ast.Num)
89+
else node.right
90+
)
91+
92+
if not isinstance(rhs, ast.Constant):
93+
return ast.BinOp(left=node, op=ast.Sub(), right=ast_utils.make_constant(1))
94+
95+
shift = 1 if isinstance(node.op, ast.Add) else -1
96+
97+
return (
98+
node.left
99+
if rhs.value == shift
100+
else ast.BinOp(
101+
left=node.left,
102+
op=node.op,
103+
right=ast_utils.make_constant(value=rhs.value - shift),
104+
)
105+
)

src/latexify/analyzers_test.py

+17
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,20 @@ def test_analyze_range_invalid(code: str) -> None:
150150
exceptions.LatexifySyntaxError, match=r"^Unsupported AST for analyze_range\.$"
151151
):
152152
analyzers.analyze_range(node)
153+
154+
155+
@pytest.mark.parametrize(
156+
"before,after",
157+
[
158+
("n + 1", "n"),
159+
("n + 2", "n + 1"),
160+
("n - (-1)", "n - (-1) - 1"),
161+
("n - 1", "n - 2"),
162+
("1 * 2", "1 * 2 - 1"),
163+
],
164+
)
165+
def test_reduce_stop_parameter(before: str, after: str) -> None:
166+
test_utils.assert_ast_equal(
167+
analyzers.reduce_stop_parameter(ast_utils.parse_expr(before)),
168+
ast_utils.parse_expr(after),
169+
)

src/latexify/codegen/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Package latexify.codegen."""
22

3-
from latexify.codegen import function_codegen
3+
from latexify.codegen import expression_codegen, function_codegen
44

5+
ExpressionCodegen = expression_codegen.ExpressionCodegen
56
FunctionCodegen = function_codegen.FunctionCodegen

src/latexify/codegen/codegen_utils.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from typing import Any
2+
3+
from latexify import exceptions
4+
5+
6+
def convert_constant(value: Any) -> str:
7+
"""Helper to convert constant values to LaTeX.
8+
9+
Args:
10+
value: A constant value.
11+
12+
Returns:
13+
The LaTeX representation of `value`.
14+
"""
15+
if value is None or isinstance(value, bool):
16+
return r"\mathrm{" + str(value) + "}"
17+
if isinstance(value, (int, float, complex)):
18+
# TODO(odashi): Support other symbols for the imaginary unit than j.
19+
return str(value)
20+
if isinstance(value, str):
21+
return r'\textrm{"' + value + '"}'
22+
if isinstance(value, bytes):
23+
return r"\textrm{" + str(value) + "}"
24+
if value is ...:
25+
return r"\cdots"
26+
raise exceptions.LatexifyNotSupportedError(
27+
f"Unrecognized constant: {type(value).__name__}"
28+
)
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""Tests for latexify.codegen.codegen_utils."""
2+
3+
from __future__ import annotations
4+
5+
from typing import Any
6+
7+
import pytest
8+
9+
from latexify import exceptions
10+
from latexify.codegen.codegen_utils import convert_constant
11+
12+
13+
@pytest.mark.parametrize(
14+
"constant,latex",
15+
[
16+
(None, r"\mathrm{None}"),
17+
(True, r"\mathrm{True}"),
18+
(False, r"\mathrm{False}"),
19+
(123, "123"),
20+
(456.789, "456.789"),
21+
(-3 + 4j, "(-3+4j)"),
22+
("string", r'\textrm{"string"}'),
23+
(..., r"\cdots"),
24+
],
25+
)
26+
def test_convert_constant(constant: Any, latex: str) -> None:
27+
assert convert_constant(constant) == latex
28+
29+
30+
def test_convert_constant_unsupported_constant() -> None:
31+
with pytest.raises(
32+
exceptions.LatexifyNotSupportedError, match="^Unrecognized constant: "
33+
):
34+
convert_constant({})

0 commit comments

Comments
 (0)