From: Balazs Endresz on
Recently I made a parser combinator lib, based on Parsec:
http://code.google.com/p/jsparsec/
and I started to rewrite it in trampolined style to avoid "too much
recursion". But above about 3000 "recursive" calls (in Firefox) it
doesn't work as expected. I might have messed something up, but I
can't seem to figure out what's happening. The parser operates on a
ParseState object, which indicates how much of the text has been
consumed. And it *does* seem to fully consume the input no matter how
deep the recursion is, but when it approaches to the last function
call, then it fails with "Internal error: too much recursion". I even
inserted timeouts between every few hundred calls but it fails then as
well, but with a slightly different error: "too much recursion (802
out of range 13)" - this is a bit strange but I don't think it's
related. If I stop it before the last computation and evaluate the
remaining thunks manually, one by one, then again: "Internal error:
too much recursion".

Here's the code I used to test it:
http://code.google.com/p/jsparsec/source/browse/branches/tailopt-test/test.js

So, what seems to be happening, is that it works properly until 3000
recursive calls but above that, when the last callback function should
receive the final result, it fails. But I can see that the input in
the ParseState object has been fully consumed. How could such thing
happen?
From: Ry Nohryb on
On May 2, 11:25 am, Balazs Endresz <balazs.endr...(a)gmail.com> wrote:
> Recently I made a parser combinator lib, based on Parsec:http://code.google.com/p/jsparsec/
> and I started to rewrite it in trampolined style to avoid "too much
> recursion". But above about 3000 "recursive" calls (in Firefox) it
> doesn't work as expected. I might have messed something up, but I
> can't seem to figure out what's happening. The parser operates on a
> ParseState object, which indicates how much of the text has been
> consumed. And it *does* seem to fully consume the input no matter how
> deep the recursion is, but when it approaches to the last function
> call, then it fails with "Internal error: too much recursion". I even
> inserted timeouts between every few hundred calls but it fails then as
> well, but with a slightly different error: "too much recursion (802
> out of range 13)" - this is a bit strange but I don't think it's
> related. If I stop it before the last computation and evaluate the
> remaining thunks manually, one by one, then again: "Internal error:
> too much recursion".
>
> Here's the code I used to test it:http://code.google.com/p/jsparsec/source/browse/branches/tailopt-test...
>
> So, what seems to be happening, is that it works properly until 3000
> recursive calls but above that, when the last callback function should
> receive the final result, it fails. But I can see that the input in
> the ParseState object has been fully consumed. How could such thing
> happen?

That's the limit of its call stack:

javascript:var n=0;(function f () {try {f(n++)} catch (e) {alert(n)}})
(0);

Safari:40328
Opera:16384
Chrome:6921
FF:3000
--
Jorge.
From: Balazs Endresz on
On May 2, 11:59 am, Ry Nohryb <jo...(a)jorgechamorro.com> wrote:
> On May 2, 11:25 am, Balazs Endresz <balazs.endr...(a)gmail.com> wrote:
>
>
>
>
>
> > Recently I made a parser combinator lib, based on Parsec:http://code.google.com/p/jsparsec/
> > and I started to rewrite it in trampolined style to avoid "too much
> > recursion". But above about 3000 "recursive" calls (in Firefox) it
> > doesn't work as expected. I might have messed something up, but I
> > can't seem to figure out what's happening. The parser operates on a
> > ParseState object, which indicates how much of the text has been
> > consumed. And it *does* seem to fully consume the input no matter how
> > deep the recursion is, but when it approaches to the last function
> > call, then it fails with "Internal error: too much recursion". I even
> > inserted timeouts between every few hundred calls but it fails then as
> > well, but with a slightly different error: "too much recursion (802
> > out of range 13)" - this is a bit strange but I don't think it's
> > related. If I stop it before the last computation and evaluate the
> > remaining thunks manually, one by one, then again: "Internal error:
> > too much recursion".
>
> > Here's the code I used to test it:http://code.google.com/p/jsparsec/source/browse/branches/tailopt-test...
>
> > So, what seems to be happening, is that it works properly until 3000
> > recursive calls but above that, when the last callback function should
> > receive the final result, it fails. But I can see that the input in
> > the ParseState object has been fully consumed. How could such thing
> > happen?
>
> That's the limit of its call stack:
>
> javascript:var n=0;(function f () {try {f(n++)} catch (e) {alert(n)}})
> (0);
>
> Safari:40328
> Opera:16384
> Chrome:6921
> FF:3000
> --
> Jorge.

I understand that this shouldn't work above those limits but writing
it in trampolined style *should* solve this issue. See the second
answer here:
http://stackoverflow.com/questions/189725/what-is-a-trampoline-function
From: Ry Nohryb on
On May 2, 12:32 pm, Balazs Endresz <balazs.endr...(a)gmail.com> wrote:
> On May 2, 11:59 am, Ry Nohryb <jo...(a)jorgechamorro.com> wrote:
>
>
>
>
>
> > On May 2, 11:25 am, Balazs Endresz <balazs.endr...(a)gmail.com> wrote:
>
> > > Recently I made a parser combinator lib, based on Parsec:http://code.google.com/p/jsparsec/
> > > and I started to rewrite it in trampolined style to avoid "too much
> > > recursion". But above about 3000 "recursive" calls (in Firefox) it
> > > doesn't work as expected. I might have messed something up, but I
> > > can't seem to figure out what's happening. The parser operates on a
> > > ParseState object, which indicates how much of the text has been
> > > consumed. And it *does* seem to fully consume the input no matter how
> > > deep the recursion is, but when it approaches to the last function
> > > call, then it fails with "Internal error: too much recursion". I even
> > > inserted timeouts between every few hundred calls but it fails then as
> > > well, but with a slightly different error: "too much recursion (802
> > > out of range 13)" - this is a bit strange but I don't think it's
> > > related. If I stop it before the last computation and evaluate the
> > > remaining thunks manually, one by one, then again: "Internal error:
> > > too much recursion".
>
> > > Here's the code I used to test it:http://code.google.com/p/jsparsec/source/browse/branches/tailopt-test...
>
> > > So, what seems to be happening, is that it works properly until 3000
> > > recursive calls but above that, when the last callback function should
> > > receive the final result, it fails. But I can see that the input in
> > > the ParseState object has been fully consumed. How could such thing
> > > happen?
>
> > That's the limit of its call stack:
>
> > javascript:var n=0;(function f () {if (n>5e4) return; try {setTimeout(function(){f(n++),0}} catch (e) {alert(n)}})
> > (0);
>
> > Safari:40328
> > Opera:16384
> > Chrome:6921
> > FF:3000
> > --
> > Jorge.
>
> I understand that this shouldn't work above those limits but writing
> it in trampolined style *should* solve this issue. See the second
> answer here:http://stackoverflow.com/questions/189725/what-is-a-trampoline-function

Can you post a short "trampolinized" snippet that fails ?
--
Jorge.
From: Balazs Endresz on
On May 2, 12:53 pm, Ry Nohryb <jo...(a)jorgechamorro.com> wrote:
> On May 2, 12:32 pm, Balazs Endresz <balazs.endr...(a)gmail.com> wrote:
>
>
>
>
>
> > On May 2, 11:59 am, Ry Nohryb <jo...(a)jorgechamorro.com> wrote:
>
> > > On May 2, 11:25 am, Balazs Endresz <balazs.endr...(a)gmail.com> wrote:
>
> > > > Recently I made a parser combinator lib, based on Parsec:http://code.google.com/p/jsparsec/
> > > > and I started to rewrite it in trampolined style to avoid "too much
> > > > recursion". But above about 3000 "recursive" calls (in Firefox) it
> > > > doesn't work as expected. I might have messed something up, but I
> > > > can't seem to figure out what's happening. The parser operates on a
> > > > ParseState object, which indicates how much of the text has been
> > > > consumed. And it *does* seem to fully consume the input no matter how
> > > > deep the recursion is, but when it approaches to the last function
> > > > call, then it fails with "Internal error: too much recursion". I even
> > > > inserted timeouts between every few hundred calls but it fails then as
> > > > well, but with a slightly different error: "too much recursion (802
> > > > out of range 13)" - this is a bit strange but I don't think it's
> > > > related. If I stop it before the last computation and evaluate the
> > > > remaining thunks manually, one by one, then again: "Internal error:
> > > > too much recursion".
>
> > > > Here's the code I used to test it:http://code.google.com/p/jsparsec/source/browse/branches/tailopt-test...
>
> > > > So, what seems to be happening, is that it works properly until 3000
> > > > recursive calls but above that, when the last callback function should
> > > > receive the final result, it fails. But I can see that the input in
> > > > the ParseState object has been fully consumed. How could such thing
> > > > happen?
>
> > > That's the limit of its call stack:
>
> > > javascript:var n=0;(function f () {if (n>5e4) return; try {setTimeout(function(){f(n++),0}} catch (e) {alert(n)}})
> > > (0);
>
> > > Safari:40328
> > > Opera:16384
> > > Chrome:6921
> > > FF:3000
> > > --
> > > Jorge.
>
> > I understand that this shouldn't work above those limits but writing
> > it in trampolined style *should* solve this issue. See the second
> > answer here:http://stackoverflow.com/questions/189725/what-is-a-trampoline-function
>
> Can you post a short "trampolinized" snippet that fails ?
> --
> Jorge.

Other than the one I already posted, if I could make a really simple
one, my problem would be solved :)
But here's one that works:

function trampoline(x) {
while (x && x.func) {
x = x.func.apply(null, x.args);
}
}

var n = 0;
function f(callback){
return n++ >50000 ? callback(n) : {func:f, args:[callback]};
}

trampoline({func:f, args:[alert]});