2013-04-16 17 views
6

Tôi mới đến Parsec (và để phân tích cú pháp nói chung), và tôi đang gặp một số rắc rối với phân tích cú pháp này tôi đã viết:khó nhận được một phân tích cú pháp Parsec để bỏ qua không gian một cách chính xác

list = char '(' *> many (spaces *> some letter) <* spaces <* char ')' 

Ý tưởng là để phân tích danh sách ở định dạng này (tôi đang làm việc lên đến s-biểu thức):

(firstElement secondElement thirdElement and so on) 

tôi đã viết mã này để kiểm tra nó:

import Control.Applicative 
import Text.ParserCombinators.Parsec hiding (many) 

list = char '(' *> many (spaces *> some letter) <* spaces <* char ')' 

test s = do 
    putStrLn $ "Testing " ++ show s ++ ":" 
    parseTest list s 
    putStrLn "" 

main = do 
    test "()" 
    test "(hello)" 
    test "(hello world)" 
    test "(hello world)" 
    test "(hello world)" 
    test "()" 

T mình là đầu ra tôi nhận được:

Testing "()": 
[] 

Testing "(hello)": 
["hello"] 

Testing "(hello world)": 
["hello","world"] 

Testing "(hello world)": 
["hello","world"] 

Testing "(hello world)": 
parse error at (line 1, column 14): 
unexpected ")" 
expecting space or letter 

Testing "()": 
parse error at (line 1, column 3): 
unexpected ")" 
expecting space or letter 

Như bạn thấy, nó không thành công khi có khoảng trắng giữa các yếu tố cuối cùng của danh sách, và đóng cửa ). Tôi không hiểu tại sao không gian trắng không được tiêu thụ bởi spaces Tôi đã đặt ngay trước <* char ')'. Tôi đã phạm phải sai lầm ngớ ngẩn gì?

Trả lời

12

Vấn đề là các không gian chính thức được tiêu thụ bởi các spaces trong đối số để many,

list = char '(' *> many (spaces *> some letter) <* spaces <* char ')' 
        -- ^^^^^^ that one 

và sau đó là phân tích cú pháp dự đoán some letter nhưng tìm thấy một đóng ngoặc và do đó thất bại.

Để giải quyết nó, tiêu thụ không gian chỉ sau tokens,

list = char '(' *> spaces *> many (some letter <* spaces) <* char ')' 

mà làm việc như mong muốn:

$ runghc lisplists.hs 
Testing "()": 
[] 

Testing "(hello)": 
["hello"] 

Testing "(hello world)": 
["hello","world"] 

Testing "(hello world)": 
["hello","world"] 

Testing "(hello world)": 
["hello","world"] 

Testing "()": 
[] 
0

Đó là một chút khéo léo. Parsers theo mặc định là tham lam. Điều đó có nghĩa gì trong trường hợp của bạn? Khi bạn cố gắng phân tích cú pháp (hello world) bạn bắt đầu phân tích cú pháp (, thì bạn đang cố gắng khớp một số khoảng trắng và số nhận dạng. Vì vậy, chúng tôi làm điều đó. Không có dấu cách, nhưng có định danh. Chúng ta xong rồi. Chúng tôi thử lại với thế giới. Bây giờ chúng tôi đã có _) còn lại. Bạn hãy thử phân tích cú pháp (spaces *> some letter). Nó làm cho nó tham lam: vì vậy bạn phù hợp với không gian và bây giờ bạn mong đợi một số lá thư, nhưng bạn nhận được ) thay thế. Tại thời điểm này phân tích cú pháp không thành công, nhưng nó đã tiêu thụ không gian, vì vậy bạn đang phải chịu số phận. Bạn có thể thực hiện phân tích cú pháp này làm thụt lùi bằng cách sử dụng try combinator: try (many (spaces *> some letter))

3

Vấn đề là một khi phân tích cú pháp many (spaces *> some letter) thấy một không gian nó cam kết để phân tích mục khác, vì Parsec theo mặc định chỉ xem xét triển vọng một ký tự và không quay lại .

giải pháp

Các búa tạ là sử dụng try để cho phép quay lui, nhưng vấn đề như thế này được tốt nhất nên tránh bằng cách đơn giản phân tích tùy chọn khoảng trắng sau mỗi thẻ thay vào đó, như đã thấy trong Daniel's answer.

Các vấn đề liên quan