Почему Parslet (в Ruby) возвращает пустой массив при разборе пустого строкового литерала?

Я играю с петрушкой. Это тривиальный парсер, который показывает мне неочевидное поведение.

require 'parslet'

class Parser < Parslet::Parser
  rule(:quote) { str('"') }
  rule(:escape_char) { str('\\') }
  def quoted(term)
    quote >> term >> quote
  end
  rule(:string) {
    quoted( (escape_char >> any | quote.absent? >> any).repeat.as(:string) )
  }
end

Очевидно, что он должен разобрать строку в двойных кавычках. И это так. Но следующий результат мне кажется странным.

Parser.new.string.parse '""'

Этот код возвращает {:string=>[]}, Зачем empty array там, но не empty string? Что мне не хватает?

я использую ruby 2.1.1 а также parslet 1.6.1

1 ответ

Решение

TL;DR; - Как правило, Парслет as применительно к repeat захватывает массив совпадений; за исключением особого случая, когда все совпадения являются необработанными строками, и в этом случае он присоединяется к ним и возвращает полученную строку.

В вашем коде repeat не знает типы, которые он будет захватывать, поскольку их нет, поэтому он возвращает пустой массив.

В этом примере... пустой массив кажется правильным выбором.

require 'parslet'

class Parser < Parslet::Parser
  rule(:quote) { str('"') }
  rule(:escape_char) { str('\\') }
  def quoted(term)
    quote >> term >> quote
  end
  rule(:string) {
    quoted( (escape_char >> any | quote.absent? >> any).as(:char).repeat.as(:string) )
  }
end

puts Parser.new.string.parse('""').inspect # => {:string=>[]}
puts Parser.new.string.parse('"test"').inspect 
    # =>  {:string=>[{:char=>"t"@1}, {:char=>"e"@2}, {:char=>"s"@3}, {:char=>"t"@4}]}

Когда дочерние узлы являются просто строками, Parslet объединяется в одну строку. Если в коллекции нет элементов, по умолчанию вместо пустой строки используется пустая коллекция.

maybe это отличается.

От http://kschiess.github.io/parslet/parser.html # Повторение и его особые случаи

Все они отображаются на Parslet::Atoms::Repetition. Пожалуйста, обратите внимание на этот маленький поворот к #maybe:

str ('foo'). Maybe.as (: f).parse ('') # => {: f => nil}
str ('foo'). repeat (0,1).as (: f).parse ('') # => {: f => []}

'nil'-значение #maybe равно nil. Это удовлетворяет интуиции, что foo.maybe либо дает мне foo, либо вообще ничего, а не пустой массив. Но имейте это по-своему!

Другие вопросы по тегам