Dan*_*yel 5 ruby lines parslet
我正在寻找一种匹配多行Parslet的方法.代码如下所示:
rule(:line) { (match('$').absent? >> any).repeat >> match('$') }
rule(:lines) { line.repeat }
Run Code Online (Sandbox Code Playgroud)
但是,lines总是会以无限循环结束,因为match('$')它将无休止地重复以匹配字符串的结尾.
是否可以匹配多个可以为空的行?
irb(main)> lines.parse($stdin.read)
This
is
a
multiline
string^D
Run Code Online (Sandbox Code Playgroud)
应该匹配成功.我错过了什么吗?我也试过,(match('$').absent? >> any.maybe).repeat(1) >> match('$')但这与空行不匹配.
此致,
Danyel.
我通常为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
Run Code Online (Sandbox Code Playgroud)
显然,如果你想用解析器做的比用String :: split("\n")实现的更多,你将用line_body有用的东西替换:)
我快速回答了这个问题并将其搞砸了.我只是想解释我犯的错误,并告诉你如何避免这种错误.
这是我的第一个答案.
rule(:eol) { str('\n') | any.absent? }
rule(:line) { (eol.absent? >> any).repeat >> eol }
rule(:lines) { line.as(:line).repeat }
Run Code Online (Sandbox Code Playgroud)
我没有遵循我通常的规则:
所以让我们应用这些......
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`!
Run Code Online (Sandbox Code Playgroud)
这里看看为什么我们得到一个无限循环.当输入被消耗时,你最终得到的是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) }
Run Code Online (Sandbox Code Playgroud)
现在line必须匹配一些东西,a cr(对于空行),或者至少一个字符后跟可选项eol?.所有人repeat都有消耗某些东西的身体.我们现在是金色的.
我认为您的匹配有两个相关的问题:
伪字符匹配$不消耗任何真实字符。您仍然需要以某种方式消耗换行符。
Parslet 以某种方式修改输入,$在您可能意想不到的地方进行匹配。我能得到的最好结果是$匹配每个单独的字符。
用作\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 )
Run Code Online (Sandbox Code Playgroud)
该行的规则很复杂,因为需要匹配空行和可能的没有\n.
您不必使用该.as(:line)语法 - 我只是添加它以清楚地表明该:line规则单独匹配每一行,而不是简单地消耗整个输入。