análisis de fórmula de estilo excel

Estoy trabajando en crear un mapa de referencia de fórmulas a partir de una hoja de cálculo xml usando Python. la fórmula es como

=IF(AND(LEN(R[-2]C[-1])>0,R[-1]C),WriteCurve(OFFSET(R16C6, 0,0,R9C7,R10C7),R15C6,R10C3, R8C3),"NONE") 

Sólo estoy interesado en obtener el enésimo argumento de la función de escritura de la curva. Aquí surge un progtwig de estilo muy C, básicamente, contando coma, que no está dentro del corchete. hay mucha formula anidada

 def parseArguments(t, func, n): start=t.find(func)+len(func)+1 bracket = 0 ss = t[start:] lastcomma = 0 for i, a in enumerate(ss): if a=="(": bracket +=1 elif a==")": if bracket==0: break bracket-=1 elif a == ",": if bracket==0 and n==0: break elif bracket ==0: if n-1==0: lastcomma = i n-=1 if lastcomma == 0: return ss[:i] else: return ss[lastcomma+1:i] 

¿Hay alguna forma pythonica de hacer esto? ¿O hay una forma recursiva mejor para analizar toda la fórmula? Muchas gracias

El mejor analizador de fórmulas de Excel que conozco es el algoritmo de EW Bachtal . Hay un puerto de Python por Robin Macharg; La versión más reciente que conozco es parte del proyecto pycel , pero se puede usar de forma independiente: tokenizer . No tiene ningún problema en analizar tu fórmula:

 from tokenizer import shunting_yard rpn = shunting_yard('=IF(AND(LEN(R[-2]C[-1])>0,R[-1]C),WriteCurve(OFFSET(R16C6, 0,0,R9C7,R10C7),R15C6,R10C3, R8C3),"NONE")') print(rpn) deque([, , , , , , , , , , , , , , , , , ]) 

El tokenizer te deja con una stack RPN; Si te resulta más conveniente trabajar con un AST, puedes convertir fácilmente a un AST:

 def rpn_to_ast(rpn): stack = [] for n in rpn: num_args = (2 if n.token.ttype == "operator-infix" else 1 if n.token.ttype.startswith('operator') else n.num_args if n.token.ttype == 'function' else 0) n.args = [stack.pop() for _ in range(num_args)][::-1] stack.append(n) return stack[0] 

Luego puede recorrer el AST para encontrar el nodo WriteCurve y examinar sus argumentos:

 def walk(ast): yield ast for arg in getattr(ast, 'args', []): for node in walk(arg): yield node write_curve = next(node for node in walk(rpn_to_ast(rpn)) if node.token.ttype == 'function' and node.token.tvalue == 'WriteCurve') print(write_curve.args[2].token.tvalue) R10C3