Voici un peu méthode longue mais encore incomplète qui n'implique aucunement sympy. Il suffit de couvrir l'exemple de (-b-sqrt(b**2-4*a*c))/(2*a)
qui obtient traduit à \frac{- b - \sqrt{b^{2} - 4 \; a \; c}}{2 \; a}
et rend comme

Il crée essentiellement l'AST et marche il produire le calcul de latex les correspond aux nœuds AST. Ce qui est là devrait donner assez d'idée pour l'étendre dans les endroits qui lui manquent.
import ast
class LatexVisitor(ast.NodeVisitor):
def prec(self, n):
return getattr(self, 'prec_'+n.__class__.__name__, getattr(self, 'generic_prec'))(n)
def visit_Call(self, n):
func = self.visit(n.func)
args = ', '.join(map(self.visit, n.args))
if func == 'sqrt':
return '\sqrt{%s}' % args
else:
return r'\operatorname{%s}\left(%s\right)' % (func, args)
def prec_Call(self, n):
return 1000
def visit_Name(self, n):
return n.id
def prec_Name(self, n):
return 1000
def visit_UnaryOp(self, n):
if self.prec(n.op) > self.prec(n.operand):
return r'%s \left(%s\right)' % (self.visit(n.op), self.visit(n.operand))
else:
return r'%s %s' % (self.visit(n.op), self.visit(n.operand))
def prec_UnaryOp(self, n):
return self.prec(n.op)
def visit_BinOp(self, n):
if self.prec(n.op) > self.prec(n.left):
left = r'\left(%s\right)' % self.visit(n.left)
else:
left = self.visit(n.left)
if self.prec(n.op) > self.prec(n.right):
right = r'\left(%s\right)' % self.visit(n.right)
else:
right = self.visit(n.right)
if isinstance(n.op, ast.Div):
return r'\frac{%s}{%s}' % (self.visit(n.left), self.visit(n.right))
elif isinstance(n.op, ast.FloorDiv):
return r'\left\lfloor\frac{%s}{%s}\right\rfloor' % (self.visit(n.left), self.visit(n.right))
elif isinstance(n.op, ast.Pow):
return r'%s^{%s}' % (left, self.visit(n.right))
else:
return r'%s %s %s' % (left, self.visit(n.op), right)
def prec_BinOp(self, n):
return self.prec(n.op)
def visit_Sub(self, n):
return '-'
def prec_Sub(self, n):
return 300
def visit_Add(self, n):
return '+'
def prec_Add(self, n):
return 300
def visit_Mult(self, n):
return '\\;'
def prec_Mult(self, n):
return 400
def visit_Mod(self, n):
return '\\bmod'
def prec_Mod(self, n):
return 500
def prec_Pow(self, n):
return 700
def prec_Div(self, n):
return 400
def prec_FloorDiv(self, n):
return 400
def visit_LShift(self, n):
return '\\operatorname{shiftLeft}'
def visit_RShift(self, n):
return '\\operatorname{shiftRight}'
def visit_BitOr(self, n):
return '\\operatorname{or}'
def visit_BitXor(self, n):
return '\\operatorname{xor}'
def visit_BitAnd(self, n):
return '\\operatorname{and}'
def visit_Invert(self, n):
return '\\operatorname{invert}'
def prec_Invert(self, n):
return 800
def visit_Not(self, n):
return '\\neg'
def prec_Not(self, n):
return 800
def visit_UAdd(self, n):
return '+'
def prec_UAdd(self, n):
return 800
def visit_USub(self, n):
return '-'
def prec_USub(self, n):
return 800
def visit_Num(self, n):
return str(n.n)
def prec_Num(self, n):
return 1000
def generic_visit(self, n):
if isinstance(n, ast.AST):
return r'' % (n.__class__.__name__, ', '.join(map(self.visit, [getattr(n, f) for f in n._fields])))
else:
return str(n)
def generic_prec(self, n):
return 0
def py2tex(expr):
pt = ast.parse(expr)
return LatexVisitor().visit(pt.body[0].value)
Une différence importante est que vous analysez ces expressions pour ne pas les évaluer, mais pour faire la mise en page du document avec elles. C'est dire que vous avez su remplacer les parens autour de «x + y» par des accolades, mais il y a certainement d'autres endroits où les parens devraient être conservés. Je pense que vous allez devoir réfléchir sérieusement à ce que vous essayez d'analyser et à quoi cela devrait ressembler lorsque vous avez terminé. Ensuite, vous pouvez commencer à réfléchir à la façon dont cette transformation pourrait être réalisée avec un parseur approprié. Cela vous aidera également à formuler ce que l'analyseur devra faire. – PaulMcG