2013-07-18 26 views
5

我正在尋找匹配多行Parslet的方法。 的代碼看起來是這樣的:Ruby parslet:解析多行

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

然而,lines總是在無限循環中這是因爲match('$')會無休止地重複匹配字符串的結尾收場。

是否可以匹配多行可以爲空的行?

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

a 
multiline 

string^D 

應該匹配成功。我錯過了什麼嗎?我也試過(match('$').absent? >> any.maybe).repeat(1) >> match('$'),但那不符合空行。

Regards,
Danyel。

回答

3

我認爲你有兩個相關的,問題你的匹配:

  • 僞字符匹配$不消耗任何真實人物。你仍然需要以某種方式消費換行符。

  • Parslet被改寫(munging)以某種方式輸入,使得$比賽中你可能沒有想到的地方。我能用$得到的最好結果是匹配每個單獨的角色。

更安全地使用\n作爲行尾字符。我得到了以下的工作(我與Parslet自己初學者有點的,所以如果道歉它可能是更清晰):

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) 

直線的規則是因爲需要匹配空行和複雜沒有\n可能的最後一行。

您不必使用.as(:line)語法 - 我只是添加它來清楚地顯示:line規則是單獨匹配每條線,而不是簡單地消耗整個輸入。

+0

這看起來像一個不錯的解決方案。我的解決方法是與'\ n'一起工作,併爲傳入的字符串添加一個換行符,以防止匹配失敗。不過,這看起來更乾淨。謝謝! – Danyel

6

我通常爲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都有消耗某物的物體。我們現在是金色的。

+0

這對我來說會變成一個無限循環。 – Danyel

+0

oops。是的,我會解決這個問題。 –

+0

當您擁有可以匹配而不消耗任何輸入的規則時,就會發生無限循環。這裏'line'匹配一個空行,後面跟着'any.absent?'版本的'eol',它也不會消耗任何東西,所以它可以保持匹配。 –