Okay, sorry I don't have time/ability to create a minimal reproduction
case, but here's my whole thing.
1) The whole grammar that reveals the problem. 2) the string input that
will reveal the problem. 3) The rule change (adding an .as() ) that
cures it.
As an aside, I have gotten the feeling writing this code that I'm not
using .as() quite as you intended; but I couldn't quite wrap my head
around how you intended with my use case, and the way I'm doing is
working out well for my brain and use case.
And in fact I think the change I made _was_ neccesary for me to get
output I could deal with properly; but perhaps the pre-change version
still shouldn't have raised, or should have raised a more clear exception.
**
1. Original grammar revealing the problem
**
class AdvParser < Parslet::Parser
root :query
# query is actually a list of expressions.
rule :query do
(spacing? >> (expression | paren_unit ) >> spacing?).repeat
end
rule :paren_list do
(str('(') >> (spacing? >> unary_expression >> spacing?).repeat >>
str(')'))
end
rule :paren_unit do
(str('(') >> spacing? >> (expression ) >> spacing? >> str(')')) |
paren_list
end
# Note well: It was tricky to parse the thing we want where you can
# have a flat list with boolean operators, but where 'OR' takes
precedence.
# eg "A AND B OR C AND C" or "A OR B AND C OR D". Tricky to parse at all,
# tricky to make precedence work. Important things that seem to make
it work:
# and_list comes BEFORE or_list in :expression.
# and_list's operand can be an or_list, but NOT vice versa
rule :expression do
(and_list | or_list | unary_expression )
end
rule :and_list do
((or_list | unary_expression | paren_unit) >>
(spacing >> str("AND") >> spacing >> (or_list | unary_expression
| paren_unit)).repeat(1)).as(:and_list)
end
rule :or_list do
((unary_expression | paren_unit) >>
(spacing >> str("OR") >> spacing >> (unary_expression |
paren_unit)).repeat(1)).as(:or_list)
end
rule :unary_expression do
(str('+') >> (phrase | token)).as(:mandatory) |
(str('-') >> (phrase | token)).as(:excluded) |
(str('NOT') >> spacing? >> (unary_expression |
paren_unit)).as(:not_expression) |
(phrase | token)
end
rule :token do
match['^ ":)('].repeat(1).as(:token)
end
rule :phrase do
match('"') >> match['^"'].repeat(1).as(:phrase) >> match('"')
end
rule :spacing do
match[' '].repeat(1)
end
rule :spacing? do
spacing.maybe
end
end
**
2. Input string
**
(one two three) AND (four five six)
**
3. Change this rule adding an 'as', all is well again.
**
rule :paren_list do
(str('(') >> (spacing? >> unary_expression >> spacing?).repeat >>
str(')')).as(:list)
end
**
On 3/15/2011 5:11 AM, Kaspar Schiess wrote:
> On 15.03.11 00:37, Jonathan Rochkind wrote:
>> Hmm, weirdly that seemed to be caused not by the parse rules themselves,
>> but by the as(:label)s I added to them. Adding an _additional_
>> .as(:label) in a certain place somehow solved this, I can't explain it.
>> I could give you my entire complicated parser, but I don't expect anyone
>> else to try and understand it.
> Hei Jonathan,
>
> I would sure like to try and understand it ;). And I guess we might find
> the mindshare of others as well. Can you post a sample that exposes this
> error? Even if it is not minimal, there might be a bug lurking in there!
>
> kaspar
>
>
>