我通常爲end_of_line定義一條規則。這是基於http://kschiess.github.io/parslet/tricks.html中用於匹配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
顯然,如果你想用解析器比你可以用字符串實現::分裂(「\ n」)做的越多,你就會有一些有用的東西:)
我更換line_body
我很快就回答了這個問題,並將其解決了。我只是想解釋我犯的錯誤,並告訴你如何避免這種錯誤。
這是我的第一個答案。
rule(:eol) { str('\n') | any.absent? }
rule(:line) { (eol.absent? >> any).repeat >> eol }
rule(:lines) { line.as(:line).repeat }
我沒有按照我一貫的原則:
- 總是要重複計數明確
- ,可以匹配零個長度字符串的任何規則,應該有名字的結尾「?」
所以讓我們運用這些...
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`!
這裏看到爲什麼我們得到了一個無限循環。隨着輸入被消耗,最後只有end of file
,它匹配eol?
,因此line?
(因爲線體可以是空的)。在lines
'repeat
之內,它保持匹配而不消耗任何東西並永遠循環。
我們需要更改線條規則,以便它總是消耗一些東西。
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) }
現在line
必須匹配的東西,可以是cr
(空線),或至少一個字符,隨後可選eol?
。所有repeat
都有消耗某物的物體。我們現在是金色的。
這看起來像一個不錯的解決方案。我的解決方法是與'\ n'一起工作,併爲傳入的字符串添加一個換行符,以防止匹配失敗。不過,這看起來更乾淨。謝謝! – Danyel