2013-07-18 68 views
5

Tôi đang tìm cách để khớp nhiều dòng Parslet. Mã này trông như thế này:Phân tích cú pháp Ruby: phân tích cú pháp nhiều dòng

rule(:line) { (match('$').absent? >> any).repeat >> match('$') } 
rule(:lines) { line.repeat } 

Tuy nhiên, lines sẽ luôn luôn kết thúc trong một vòng lặp vô hạn đó là vì match('$') không ngừng sẽ lặp lại để phù hợp với kết thúc chuỗi.

Có thể khớp nhiều dòng có thể trống không?

irb(main)> lines.parse($stdin.read) 
This 
is 

a 
multiline 

string^D 

phải khớp thành công. Tui bỏ lỡ điều gì vậy? Tôi cũng đã thử (match('$').absent? >> any.maybe).repeat(1) >> match('$') nhưng không khớp với các dòng trống.

Kính trọng,
Danyel.

Trả lời

3

Tôi nghĩ rằng bạn có hai, liên quan, các vấn đề với kết hợp của bạn:

  • Các giả nhân vật phù hợp với $ không tiêu thụ bất kỳ nhân vật thực sự. Bạn vẫn cần phải tiêu thụ các dòng mới bằng cách nào đó.

  • Parslet đang trộn đầu vào theo một cách nào đó, làm cho một kết quả phù hợp với một số địa điểm bạn có thể không mong đợi. Kết quả tốt nhất mà tôi có thể nhận được khi sử dụng $ đã kết thúc với từng ký tự riêng lẻ.

An toàn hơn để sử dụng \n làm ký tự cuối dòng. Tôi đã nhận như sau để làm việc (tôi hơi của một người mới bắt đầu với Parslet bản thân mình, vì vậy xin lỗi nếu nó có thể được rõ ràng hơn):

require 'parslet' 

class Lines < Parslet::Parser 
    rule(:text) { match("[^\n]") } 
    rule(:line) { (text.repeat(0) >> match("\n")) | text.repeat(1) } 
    rule(:lines) { line.as(:line).repeat } 
    root :lines 
end 

s = "This 
is 

a 
multiline 
string" 

p Lines.new.parse(s) 

Nguyên tắc cho dòng rất phức tạp vì sự cần thiết phải phù hợp với dòng trống và một dòng cuối cùng có thể không có \n.

Bạn không phải sử dụng cú pháp .as(:line) - Tôi vừa thêm nó để hiển thị rõ ràng rằng quy tắc :line khớp với từng dòng riêng lẻ và không chỉ đơn giản là tiêu thụ toàn bộ đầu vào.

+0

Điều này trông giống như một giải pháp tốt đẹp. Cách giải quyết của tôi là làm việc với '\ n', và thêm một dòng mới vào chuỗi đến để ngăn chặn lỗi kết hợp ở cuối. Điều này có vẻ sạch hơn, mặc dù. Cảm ơn! – Danyel

6

Tôi thường xác định quy tắc cho end_of_line. Điều này dựa trên mẹo trong http://kschiess.github.io/parslet/tricks.html để khớp với end_of_file.

class MyParser < Parslet::Parser 
    rule(:cr)   { str("\n") } 
    rule(:eol?)  { any.absent? | cr } 
    rule(:line_body) { (eol?.absent? >> any).repeat(1) } 
    rule(:line)  { cr | line_body >> eol? } 
    rule(:lines?)  { line.repeat (0)} 
    root(:lines?) 
end 

puts MyParser.new.parse(""" this is a line 
so is this 

that was too 
This ends""").inspect 

Rõ ràng nếu bạn muốn làm nhiều hơn với các phân tích cú pháp hơn bạn có thể đạt được với String :: split ("\ n"), bạn sẽ thay thế line_body với một cái gì đó hữu ích :)


tôi đã nhanh chóng trả lời câu hỏi này và nhét nó lên. Tôi chỉ mặc dù tôi sẽ giải thích sai lầm tôi đã thực hiện, và cho bạn thấy làm thế nào để tránh những sai lầm của loại đó.

Đây là câu trả lời đầu tiên của tôi.

rule(:eol) { str('\n') | any.absent? } 
rule(:line) { (eol.absent? >> any).repeat >> eol } 
rule(:lines) { line.as(:line).repeat } 

Tôi không theo quy tắc thông thường của tôi:

  • Luôn chắc đếm lặp lại rõ ràng
  • Bất kỳ quy tắc mà có thể phù hợp với không dây chiều dài, nên có tên kết thúc bằng một '?'

Vì vậy, cho phép áp dụng những ...

rule(:eol?) { str('\n') | any.absent? } 
# as the second option consumes nothing 

rule(:line?) { (eol.absent? >> any).repeat(0) >> eol? } 
# repeat(0) can consume nothing 

rule(:lines?) { line.as(:line?).repeat(0) } 
# We have a problem! We have a rule that can consume nothing inside a `repeat`! 

đây thấy lý do tại sao chúng tôi có được một vòng lặp vô hạn. Khi đầu vào được tiêu thụ, bạn chỉ kết thúc với số end of file, khớp với eol? và do đó line? (vì nội dung đường có thể trống). Ở bên trong lines 'repeat, nó giữ phù hợp mà không cần tốn bất cứ thứ gì và vòng lặp mãi mãi.

Chúng tôi cần thay đổi quy tắc đường để luôn luôn tiêu thụ thứ gì đó.

rule(:cr)   { str('\n') } 
rule(:eol?)  { cr | any.absent? } 
rule(:line_body) { (eol.absent? >> any).repeat(1) } 
rule(:line)  { cr | line_body >> eol? } 
rule(:lines?)  { line.as(:line).repeat(0) } 

Bây giờ line có để phù hợp với một cái gì đó, hoặc là một cr (đối với dòng trống), hoặc ít nhất một ký tự tiếp theo là tùy chọn eol?. Tất cả các repeat đều có các vật thể tiêu thụ một thứ gì đó. Chúng tôi bây giờ là vàng.

+0

Điều này biến thành một vòng lặp vô hạn đối với tôi. – Danyel

+0

oops. vâng tôi sẽ sửa nó. –

+0

Vòng lặp vô hạn xảy ra khi bạn có các quy tắc có thể khớp mà không tốn bất kỳ đầu vào nào. Ở đây 'dòng' khớp với một dòng trống, tiếp theo là phiên bản' any.absent? 'Của' eol' cũng không tiêu thụ bất cứ thứ gì, vì vậy nó có thể tiếp tục khớp. –

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