Struggling with recursive rules causing Stack Level Too Deep
- From:
- Chris Corbyn
- Date:
- 2011-11-04 @ 09:29
Hi All,
I'm familiar with parsers like yacc, that allow a rule to refer back to
itself, which is really useful for cases where an expression, can in turn
be composed of itself, with the idea being that logically each
sub-expression reduces down to a single value at some point during
reduction and recursion is therefore finite.
I'm having difficulty getting my head around how Parslet wants such
expressions to be constructed, however. I've set myself a simple problem
in order to understand this and think I'm getting somewhere, but still not
quite there.
The grammar supports two operations:
1. Simple infix addition like 1 + 2
2. An add() function, that accepts a variable number of arguments, like
add(1, 2, 3)
Standing by themselves with integer operands, these work fine. But
anywhere an integer appears, you should be able to use another expression
in its place:
add(1, add(2, 3))
I got the above to work fine, which gave me hope.
Then I got this to work, provided "+" only accepted integers, not sub-expressions:
add(1 + 2, add(3, 4))
But I can't get this to work (which is confusing, since it's not the only
rule that includes recursion):
add(add(1, 2) + add(3, 4), add(5, 6))
Allowing add() to be nested didn't cause Stack Level too deep, but as soon
as I try to change "+" to use "expr" for its operands, Parslet can't
handle the recursion. How can I change this parser to avoid that
recursion?
https://gist.github.com/1338851
Code included here, though no doubt badly formatted too:
require "parslet"
class MiniP < Parslet::Parser
rule(:wsp) { match("\\s").repeat(1) }
rule(:wsp?) { wsp.maybe }
rule(:t_int) { match("[0-9]").repeat(1).as(:int) }
# this is causing Stack Level Too Deep
rule(:sum) { expr.as(:left) >> wsp? >> str("+") >> wsp? >> expr.as(:right) }
# but this is fine
rule(:arg_list) { expr >> wsp? >> (str(",") >> wsp? >> expr).repeat }
rule(:add_call) { str("add") >> wsp? >> str("(") >> wsp? >>
arg_list.as(:args) >> wsp? >> str(")") }
rule(:expr) { sum.as(:sum) | add_call.as(:add_call) | t_int }
root(:expr)
end
class MiniT < Parslet::Transform
rule(:int => simple(:int)) { |dict| Integer(dict[:int]) }
rule(:args => sequence(:args)) { |dict| dict[:args] }
rule(:sum => { :left => simple(:left), :right => simple(:right) }) {
|dict| dict[:left] + dict[:right] }
rule(:add_call => subtree(:args)) { |dict| dict[:args].reduce(&:+) }
end
expr = "add(add(4, 5), add(1, 1 + add(0, 1)))"
p MiniP.new.parse(expr) # debug
p MiniT.new.apply(MiniP.new.parse(expr))
Cheers,
Chris
Re: [ruby.parslet] Struggling with recursive rules causing Stack Level Too Deep
- From:
- Kaspar Schiess
- Date:
- 2011-11-04 @ 10:11
Hei Chris,
You have to think about what moves the parse forward, just as if you were
coding it by hand. The two relevant functions would look something like
this (in your grammar):
def parse_sum
parse_expr or return false
parse_wsp
parse('+') or return false
parse_wsp
parse_expr
end
def parse_expr
return true if parse_sum
return true if parse_add_call
parse_t_int
end
(Assuming that parse functions return a boolean indicating their success.)
So what you have there is direct mutual recursion between expr and sum.
And put like this (in code), it is easy to see, isn't it?
I guess your sum doesn't really allow any kind of expression in front, but
rather something that can be an element of a sum, like integers or
function calls. That avoids the recursion.
Here's something that works, maybe that helps you along as well:
https://gist.github.com/1339032
regards,
kaspar
Re: [ruby.parslet] Struggling with recursive rules causing Stack Level Too Deep
- From:
- Chris Corbyn
- Date:
- 2011-11-04 @ 10:43
Hi Kaspar,
Thanks for the quick response, splitting up expr into a rule to break the
recursion solves it. All clear now.
Vieni per caso dalla Svizzera? (Sorry if not, I just guessed Florian
knows you from Switzerland because of the name… I'm in Melbourne too).
Cheers,
Chris
On 04/11/2011, at 9:11 PM, Kaspar Schiess wrote:
> Hei Chris,
>
> You have to think about what moves the parse forward, just as if you
were coding it by hand. The two relevant functions would look something
like this (in your grammar):
>
> def parse_sum
> parse_expr or return false
> parse_wsp
> parse('+') or return false
> parse_wsp
> parse_expr
> end
>
> def parse_expr
> return true if parse_sum
> return true if parse_add_call
> parse_t_int
> end
>
> (Assuming that parse functions return a boolean indicating their
success.) So what you have there is direct mutual recursion between expr
and sum. And put like this (in code), it is easy to see, isn't it?
>
> I guess your sum doesn't really allow any kind of expression in front,
but rather something that can be an element of a sum, like integers or
function calls. That avoids the recursion.
>
> Here's something that works, maybe that helps you along as well:
https://gist.github.com/1339032
>
> regards,
> kaspar
>
Re: Struggling with recursive rules causing Stack Level Too Deep
- From:
- Kaspar Schiess
- Date:
- 2011-11-07 @ 08:05
On 04.11.11 11:43, Chris Corbyn wrote:
> Vieni per caso dalla Svizzera? (Sorry if not, I just guessed Florian
knows you from Switzerland because of the name… I'm in Melbourne too).
Yes, I live and work in Switzerland; but I only speak two of the other
three languages ;) I understand Italian though, so if you don't mind me
answering in French, we can still converse. ;)
kaspar