2012-04-16 12 views
8

Tôi đang cố gắng sử dụng pyparsing để phân tích chức năng gọi theo hình thức:Parsing chức năng lồng nhau gọi sử dụng pyparsing

f(x, y) 

Đó là dễ dàng. Nhưng kể từ đó là một phân tích cú pháp đệ quy-gốc, nó cũng sẽ dễ dàng để phân tích:

f(g(x), y) 

Đó là điều tôi không thể có được. Dưới đây là một ví dụ luộc xuống:

from pyparsing import Forward, Word, alphas, alphanums, nums, ZeroOrMore, Literal 

lparen = Literal("(") 
rparen = Literal(")") 

identifier = Word(alphas, alphanums + "_") 
integer = Word(nums) 

functor = identifier 

# allow expression to be used recursively 
expression = Forward() 

arg = identifier | integer | expression 
args = arg + ZeroOrMore("," + arg) 

expression << functor + lparen + args + rparen 

print expression.parseString("f(x, y)") 
print expression.parseString("f(g(x), y)") 

Và đây là kết quả:

['f', '(', 'x', ',', 'y', ')'] 
Traceback (most recent call last): 
    File "tmp.py", line 14, in <module> 
    print expression.parseString("f(g(x), y)") 
    File "/usr/local/lib/python2.6/dist-packages/pyparsing-1.5.6-py2.6.egg/pyparsing.py", line 1032, in parseString 
    raise exc 
pyparsing.ParseException: Expected ")" (at char 3), (line:1, col:4) 

Tại sao phân tích cú pháp của tôi giải thích các functor của biểu thức bên trong như một định danh độc lập?

Trả lời

4

Định nghĩa của arg nên được sắp xếp với mục bắt đầu với một ở bên trái, vì vậy nó phù hợp ưu tiên:

arg = expression | identifier | integer 
+0

đẹp bắt, @ Jason! – PaulMcG

11

đẹp bắt về tìm ra rằng identifier được che expression trong định nghĩa của bạn về arg . Dưới đây là một số mẹo khác trên phân tích cú pháp của bạn:

x + ZeroOrMore(',' + x) là một mô hình rất phổ biến trong phân tích cú pháp pyparsing, vì vậy pyparsing bao gồm một phương pháp helper delimitedList cho phép bạn thay thế biểu rằng với delimitedList(x). Trên thực tế, delimitedList thực hiện một điều khác - nó loại bỏ dấu phẩy phân tách (hoặc dấu phân cách khác nếu được sử dụng tham số delim tùy chọn), dựa trên khái niệm rằng dấu phân tách có ích khi phân tích cú pháp, nhưng chỉ là các thẻ lộn xộn khi cố gắng sàng lọc phân tích dữ liệu sau đó. Vì vậy, bạn có thể viết lại args là args = delimitedList(arg) và bạn sẽ chỉ nhận được các arg trong danh sách, không có dấu phẩy nào phải "bước qua".

Bạn có thể sử dụng lớp Group để tạo cấu trúc thực tế trong mã thông báo được phân tích cú pháp của mình. Điều này sẽ xây dựng hệ thống phân cấp làm tổ cho bạn, mà không cần phải đi bộ danh sách này tìm kiếm '(' và ')' để cho bạn biết khi bạn đã đi xuống một mức độ trong hàm làm tổ:

arg = Group(expression) | identifier | integer 
expression << functor + Group(lparen + args + rparen) 

Kể từ args của bạn đang được Group ed cho bạn, bạn có thể tiếp tục ngăn chặn các dấu ngoặc, vì như dấu phẩy phân chia ranh giới, họ làm công việc của họ trong phân tích cú pháp, nhưng với nhóm các thẻ của bạn, họ không còn cần thiết:

lparen = Literal("(").suppress() 
rparen = Literal(")").suppress() 

tôi giả ' h() 'là một cuộc gọi hàm hợp lệ, không có args. Bạn có thể cho phép args là tùy chọn sử dụng Optional:

expression << functor + Group(lparen + Optional(args) + rparen) 

Bây giờ bạn có thể phân tích cú pháp "f (g (x), y, h())".

Chào mừng bạn đến với trò chơi pyparsing!

+3

Cảm ơn tất cả các ý kiến ​​hữu ích!Ví dụ này đã thực sự được điều chỉnh từ tài liệu pyparsing; Tôi sử dụng hầu hết các kỹ thuật bạn mô tả trong trình phân tích cú pháp thực tế của tôi. (Và việc thực hiện ngôn ngữ bây giờ có thể sử dụng trong khoảng 6 giờ làm việc --- prototyping trong Python với pyparsing là đáng ngạc nhiên nhanh chóng.) – JasonFruit

+0

sự khác biệt giữa 'Suppress (" (")' và 'Literal (" (") là gì.) '? – dashesy

+1

Không có sự khác biệt nào' expr.suppress() 'trả về' Suppress (expr) ', và nếu một chuỗi được truyền làm initializer cho Suppress, chuỗi được đưa vào một Literal. – PaulMcG

0

Bài đăng của Paul đã giúp ích rất nhiều. Chỉ cần cho các tài liệu tham khảo của người khác, tương tự có thể được sử dụng để xác định for loops như sau (đơn giản hóa giả phân tích cú pháp ở đây, để hiển thị cấu trúc):

sep = Literal(';') 
if_ = Keyword('if') 
then_ = Keyword('then') 
elif_ = Keyword('elif') 
end_ = Keyword('end') 

if_block = Forward() 
do_block = Forward() 

stmt = other | if_block 
stmts = OneOrMore(stmt +sep) 

case = Group(guard +then_ +stmts) 
cases = case +OneOrMore(elif_ +case) 

if_block << if_ +cases +end_