2012-10-17 36 views
6

Tôi đang làm việc để xây dựng bản đồ tham khảo công thức từ bảng tính xml bằng cách sử dụng python. công thức là nhưphân tích cú pháp công thức kiểu excel

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

Tôi chỉ quan tâm đến việc tranh luận thứ n của hàm writecurve. ở đây tôi đi lên chương trình phong cách rất C về cơ bản đếm coma mà không phải là bên trong khung. có rất nhiều công thức lồng nhau

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] 

Có cách nào để làm điều này không? hoặc là có một cách đệ quy tốt hơn để phân tích toàn bộ công thức? Rất cám ơn

Trả lời

8

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 
Các vấn đề liên quan