Парслет Не разбирая всю строку

Для следующего Parslet Parser

require 'parslet'
require 'parslet/convenience'

class Lines < Parslet::Parser
        rule(:open_tag) {str('[')}
    rule(:close_tag) {str(']')}
    rule(:data) {str('name') | str('name_id') }
    rule(:text) { open_tag >> data >> close_tag }
    root :text
end

begin
    p Lines.new.parse("[name_id]")    <---- It throws error
rescue Parslet::ParseFailed => failure
    Lines.new.parse_with_debug("[name_id]")
end

Это дает следующую ошибку

Failed to match sequence (OPEN_TAG NAME CLOSE_TAG) at line 1 char 6.
`- Expected "]", but got "_" at line 1 char 6.

Если я изменю data rule от

rule(:data) {str('name') | str('name_id') }

в

rule(:data) {str('name_id') | str('name') }

тогда это работает как ожидалось.

Но я генерирую правила динамически на основе пользовательского ввода. Так что это решение не будет работать для меня.

Заранее спасибо.

2 ответа

Правило :data строится, а затем проверяется в порядке предоставления предметов. Чтобы заставить более длинные совпадения встречаться раньше, чем более короткие, можно просто отсортировать их:

data = %w|name name_id|

data = data.sort { |a, b| b <=> a }

rule(:data) { data.map(&method(:str)).reduce(:|) }

Как говорит мудасобва... имя будет совпадать, поэтому у него нет возможности попробовать name_id. Вам либо нужно изменить порядок, чтобы сначала попробовать name_id, либо вам нужно, чтобы имя не совпадало. Как вы это сделаете, зависит от вашей грамматики.

require 'parslet'
require 'parslet/convenience'

class Lines < Parslet::Parser
    rule(:open_tag) {str('[')}
    rule(:close_tag) {str(']')}
    rule(:data) { str('name]') | str('name_id]')  } # <-- you can't let a matcher match unless it really is a match, so here it works because name] fails for name_id

    rule(:text) { open_tag >> data  }
    root :text
end

begin
    p Lines.new.parse("[name_id]")   
rescue Parslet::ParseFailed => failure
    Lines.new.parse_with_debug("[name_id]")
end

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

require 'parslet'
require 'parslet/convenience'

class Lines < Parslet::Parser
    rule(:open_tag) {str('[')}
    rule(:close_tag) {str(']')}
    rule(:data) { (close_tag.absnt? >> any).repeat(1).as(:data)  }
    rule(:text) { open_tag >> data >> close_tag }
    root :text
end

begin
    p Lines.new.parse("[name_id]")   # =>  {:data=>"name_id"@1}
rescue Parslet::ParseFailed => failure
    Lines.new.parse_with_debug("[name_id]")
end

Parslet предназначен для работы в два этапа. Первый преобразует документ в дерево. Второй преобразует ваше дерево в представление данных, которое вы хотите.

В этом случае первый разбор вытягивает структуру. Прохождение секунд может проверить, что "name_id" является действительным. и т.п.

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