2012-12-03 32 views
6

Tôi đang cố viết một trình phân tích cú pháp cho phép tính mệnh đề bằng Parsec. Trình phân tích cú pháp sử dụng hàm buildExpressionParser từ Text.Parsec.Expr. Đây là mã mà tôi xác định các toán tử logic.Tại sao chỉ phân tích cú pháp toán tử đầu tiên được xác định khi sử dụng buildExpressionParser của Parsec?

operators = [ [Prefix (string "~" >> return Negation)] 
      , [binary "&" Conjunction] 
      , [binary "|" Disjunction] 
      , [binary "->" Conditional] 
      , [binary "<->" Biconditional] 
      ] 

binary n c = Infix (spaces >> string n >> spaces >> return c) AssocRight 

expr = buildExpressionParser operators term 
    <?> "compound expression" 

tôi đã bỏ qua các phân tích cú pháp cho các biến, từ ngữ mở ngoặc, nhưng nếu bạn nghĩ rằng họ có thể liên quan đến vấn đề bạn có thể đọc full source for the parser.

Trình phân tích cú pháp thành công cho các biểu thức chỉ sử dụng phủ định và kết hợp, tức là toán tử tiền tố duy nhất và toán tử kết xuất đầu tiên.

*Data.Logic.Propositional.Parser2> runPT expr() "" "p & ~q" 
Right (p ∧ ¬q) 

Expressions sử dụng bất kỳ nhà khai thác khác thất bại trên ký tự đầu tiên của các nhà điều hành, với một lỗi như sau:

*Data.Logic.Propositional.Parser2> runPT expr() "" "p | q" 
Left (line 1, column 3): 
unexpected "|" 
expecting space or "&" 

Nếu tôi nhận xét ra dòng định phân tích cú pháp cho liên từ, sau đó phân tích cú pháp cho sự tách rời sẽ hoạt động (nhưng phần còn lại sẽ vẫn thất bại). Đặt tất cả chúng vào một danh sách duy nhất (ví dụ như cùng một ưu tiên) cũng không hoạt động: cùng một vấn đề vẫn thể hiện chính nó.

Có ai có thể chỉ ra những gì tôi đang làm sai? Cảm ơn nhiều.


Cảm ơn Daniel Fischer vì đã có câu trả lời nhanh chóng và hữu ích.

Để hoàn thành công cụ phân tích cú pháp này hoạt động chính xác, tôi cũng cần xử lý các ứng dụng lặp lại của ký hiệu phủ định, ví dụ: ~~p sẽ phân tích cú pháp chính xác. This SO answer chỉ cho tôi cách thực hiện và thay đổi tôi đã thực hiện cho trình phân tích cú pháp có thể được tìm thấy here.

Trả lời

8

Vấn đề của bạn là

binary n c = Infix (spaces >> string n >> spaces >> return c) AssocRight 

là người đầu tiên cố gắng điều hành ghi vào tiêu thụ một không gian trước khi nó không thành công, vì vậy các khả năng sau không cố gắng. (Parsec ủng hộ tiêu thụ phân tích cú pháp, và <|> chỉ cố gắng để chạy các phân tích cú pháp thứ hai nếu là người đầu tiên đã thất bại mà không tốn bất kỳ đầu vào.)

Để có các nhà khai thác trung tố khác cố gắng nếu là người đầu tiên thất bại, bạn có thể quấn binary parsers trong một try

binary n c = Infix (try $ ...) AssocRight 

để khi phân tích cú pháp như thất bại, nó không tiêu thụ bất kỳ đầu vào, hoặc, tốt hơn, và các giải pháp thông thường để vấn đề đó, loại bỏ các đầu spaces từ nó,

binary n c = Infix (string n >> spaces >> return c) AssocRight 

và có tất cả phân tích cú pháp của bạn tiêu thụ không gian sau token họ phân tích cú pháp

variable = do c <- letter 
       spaces 
       return $ Variable (Var c) 
     <?> "variable" 

parens p = do char '(' 
       spaces 
       x <- p 
       char ')' 
       spaces 
       return x 
     <?> "parens" 

Tất nhiên, nếu bạn có phân tích cú pháp có thể phân tích các nhà khai thác với một tiền tố chung, bạn vẫn sẽ cần phải quấn những người trong một try để nếu phân tích cú pháp >= không thành công, vẫn có thể thử >>=.

Mocking lên một datatype cho mệnh đề và thay đổi hành vi không gian tốn như đã nêu ở trên,

*PropositionalParser Text.Parsec> head $ runPT expr() "" "p | q -> r & s" 
Right (Conditional (Disjunction (Variable (Var 'p')) (Variable (Var 'q'))) (Conjunction (Variable (Var 'r')) (Variable (Var 's')))) 

thậm chí là một biểu hiện phức tạp hơn được phân tách.

+0

Cảm ơn câu trả lời tuyệt vời, Daniel. Giải quyết vấn đề và giải thích nó. Tôi rất biết ơn. –

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