Files
2026-07-13 12:32:53 +08:00

91 lines
3.0 KiB
Python

import ast
import io
import re
import tokenize
class IntegerDivisionWrapper(ast.NodeTransformer):
"""Wrap all int divisions in a call to :class:`~fractions.Fraction`."""
def visit_BinOp(self, node):
def is_integer(x):
if isinstance(x, ast.Constant) and isinstance(x.value, int):
return True
if isinstance(x, ast.UnaryOp) and isinstance(x.op, (ast.USub,
ast.UAdd)):
return is_integer(x.operand)
if isinstance(x, ast.BinOp) and isinstance(x.op, (ast.Add,
ast.Sub,
ast.Mult,
ast.Pow)):
return is_integer(x.left) and is_integer(x.right)
return False
if isinstance(node.op, ast.Div) and all(map(is_integer,
[node.left, node.right])):
return ast.Call(ast.Name('Fraction', ast.Load()),
[node.left, node.right], [])
return self.generic_visit(node)
class _WrapFloats(ast.NodeTransformer):
"""Wrap float literals by calls to specified type."""
def __init__(self, lines, type):
super().__init__()
self.lines = lines
self.type = type
def visit_Constant(self, node):
if isinstance(node.value, (float, complex)):
line = self.lines[node.lineno - 1]
value = line[node.col_offset:node.end_col_offset]
is_complex = value.endswith(('j', 'J'))
if is_complex:
value = value[:-1]
value = ast.Constant(value)
value = ast.Call(ast.Name(self.type, ast.Load()), [value], [])
if is_complex:
value = ast.BinOp(left=value, op=ast.Mult(),
right=ast.Constant(1j))
return value
return node
def wrap_float_literals(lines):
"""Wraps all float/complex literals with mpmath classes."""
source = ''.join(lines)
tree = ast.parse(source)
tree = _WrapFloats(lines, 'mpf').visit(tree)
ast.fix_missing_locations(tree)
source = ast.unparse(tree)
return source.splitlines(keepends=True)
_HEXFLT_MATCHER = re.compile(r"""
(?: [^"' ]|^)[ ]*(?P<hexflt>
0x
[0-9a-z]+
(?: \.[0-9a-z]*)?
p(?:[+-])?[0-9]+
)
""", re.VERBOSE | re.IGNORECASE)
_BINFLT_MATCHER = re.compile(r"""
(?: [^"' ]|^)[ ]*(?P<binflt>
0b
[01]+
(?: \.[01]*)?
p(?:[+-])?[0-9]+
)
""", re.VERBOSE | re.IGNORECASE)
def wrap_hexbinfloats(lines):
new_lines = []
for line in lines:
for r in _HEXFLT_MATCHER.findall(line):
line = line.replace(r, 'mpf("' + r + '", base=16)')
for r in _BINFLT_MATCHER.findall(line):
line = line.replace(r, 'mpf("' + r + '", base=2)')
new_lines.append(line)
return new_lines