Почему 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, либо вообще ничего, а не пустой массив. Но имейте это по-своему!