Reputation: 3256
For the following 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
It gives following error
Failed to match sequence (OPEN_TAG NAME CLOSE_TAG) at line 1 char 6.
`- Expected "]", but got "_" at line 1 char 6.
If I change data rule
from
rule(:data) {str('name') | str('name_id') }
to
rule(:data) {str('name_id') | str('name') }
then it works as expected.
But, I am generating rules dynamically based on user input. So this solution wont work for me.
Thanks in advance.
Upvotes: 2
Views: 162
Reputation: 21548
As mudasobwa says... name will match, so it doesn't get a chance to try name_id. You either need to change the order so name_id is tried first, or you need to make name fail to match. How you do this depends on your grammar.
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
I think I would instead let the parser break the text up for me, then inspect the structure afterwards.. e.g.
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 is intended to work in two phases.. the first converts your doc to a tree. The second converts your tree to a data representation that you want.
In this case the first parse pulls out the structure. The seconds pass could check that "name_id" is valid. etc.
Upvotes: 1
Reputation: 121010
Rule :data
is being built and then checked in the order the items were provided. To enforce longer matchers occur before the shorter ones, one might simply sort them:
data = %w|name name_id|
data = data.sort { |a, b| b <=> a }
rule(:data) { data.map(&method(:str)).reduce(:|) }
Upvotes: 2