Parslet双重否定

时间:2015-06-12 07:43:08

标签: ruby parslet

在解析引号和转义时(参见Why does Parslet (in Ruby) return an empty array when parsing an empty string literal?)我在Parslet中遇到了一个奇怪的事:(escape_char.absent? >> str('"')).absent? >> any 看起来Parslet实际上解决了双重否定并期望转义字符在那里

require 'parslet'
require 'rspec'
require 'parslet/rig/rspec'
require 'parslet/convenience'

class Parser < Parslet::Parser
  root(:quote)

  rule :quote do
    quote >> text >> quote
  end

  rule :text do
    (quote.absent? >> any).repeat
  end

  rule :quote do
    escape_char.absent? >> str('"')
  end

  rule :escape_char do
    str('\\')
  end
end

describe Parser do
  it 'should parse text in quotes' do
    is_expected.to parse('"hello"')
  end

  it 'should parse text in quotes with escaped quote' do
    is_expected.to parse('"foo\"bar"')
  end

  it 'should parse text in quotes with trailing escaped quote' do
    is_expected.to parse('"text\""')
  end
end

我对如何解决这个问题并不感兴趣,因为它已在上面链接的帖子中描述过,但仅仅是好奇地理解这种行为。一开始似乎违反直觉,但我确信这背后有充分的理由。

1 个答案:

答案 0 :(得分:0)

Parslet通过编写较小的解析器来构建解析器......这就是PEG的美妙之处。

“缺席”是一个解析器,需要一个解析器。它尝试将输入流与包装的解析器进行匹配。如果包装的解析器匹配,则Absent报告“不匹配”。如果内部解析器无法匹配,则“Absent”解析器将通过。

所以,你提到的解析器: (escape_char.absent? >> str('"')).absent? >> any

将匹配单个字符,但仅当(escape_char.absent? >> str('"'))无法匹配相同的字符时才会匹配。

如果第一个字符是转义字符不是引号,

(escape_char.absent? >> str('"'))将无法匹配。

测试这个,结果证明是真的。

require 'parslet'
require 'rspec'
require 'parslet/rig/rspec'
require 'parslet/convenience'

class Parser < Parslet::Parser
  root(:x)

  rule :x do 
    (escape_char.absent? >> str('"')).absent? >> any 
  end

  rule :escape_char do
    str('\\')
  end
end

  begin
    Parser.new.parse('a') # passes
    Parser.new.parse('b') # passes
    Parser.new.parse('\\') # passes
    Parser.new.parse('"') # << this one fails
    puts "pass"
  rescue Parslet::ParseFailed => error
    puts error.cause.ascii_tree
  end