Trình phân tích cú pháp công thức Excel tốt nhất mà tôi biết là E. W. Bachtal's algorithm. Có một cổng Python của Robin Macharg; phiên bản mới nhất mà tôi biết là một phần của pycel project, nhưng nó có thể được sử dụng độc lập - tokenizer. Không có vấn đề gì khi phân tích cú pháp công thức của bạn:
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([<tokenizer.RangeNode object at 0x2b7b1f5d7850>, <tokenizer.FunctionNode object at 0x2b7b1f5d7950>, <tokenizer.ASTNode object at 0x2b7b1f5d7990>, <tokenizer.ASTNode object at 0x2b7b1f5d79d0>, <tokenizer.RangeNode object at 0x2b7b1f5d7a10>, <tokenizer.FunctionNode object at 0x2b7b1f5d7a50>, <tokenizer.RangeNode object at 0x2b7b1f5d7a90>, <tokenizer.ASTNode object at 0x2b7b1f5d7ad0>, <tokenizer.ASTNode object at 0x2b7b1f5d7b10>, <tokenizer.RangeNode object at 0x2b7b1f5d7b50>, <tokenizer.RangeNode object at 0x2b7b1f5d7b90>, <tokenizer.FunctionNode object at 0x2b7b1f5d7bd0>, <tokenizer.RangeNode object at 0x2b7b1f5d7c10>, <tokenizer.RangeNode object at 0x2b7b22efc450>, <tokenizer.RangeNode object at 0x2b7b22efc510>, <tokenizer.FunctionNode object at 0x2b7b22efc410>, <tokenizer.ASTNode object at 0x2b7b22eff110>, <tokenizer.FunctionNode object at 0x2b7b22eff150>])
Trình mã thông báo để lại cho bạn một ngăn xếp RPN; nếu bạn muốn thấy làm việc với một AST thuận tiện hơn, bạn có thể dễ dàng chuyển đổi sang một 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]
Sau đó, bạn có thể đi bộ AST để tìm nút WriteCurve, kiểm tra đối số của nó:
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