views:

222

answers:

3

I'm writing a calculator with an ability to accept new function definitions. Being aware of the need of newbies to try recursive functions such as Fibonacci, I would like my calculator to be able to recognize Tail-recursive functions with Flex + Bison and convert code to an Iterative form. I'm using Flex & Bison to do the job. If you have any hints or ideas, I welcome them warmly. Thanks!

EDIT:

Let's not worry about C or C++ output from Flex & Bison. Mainly I want an idea or a hint. Thanks.

+2  A: 

As I suggested in my comment, this is not an issue at all for the lexer, and possibly only slightly so for the parser. If you have a function something like this:

func f( a ) 
    if ( a == 0 )  
       return a
    return f( a - 1 )

then for a typical C compiler it would be up to the optimiser/code generator to convert that recursive call to a loop. Of course, in an interpretive calculator, you can be much more flexible, but I'd suggest that it's still one of the last processes that should perform the tail call removal, not the first ones.

anon
Thanks for attempting. Lexer can do nothing about it and there is little the parser can do. Could you please suggest if we have an AST, how we can recognize it given we have Bison generated an AST? Thanks.
Viet
@Vliet I don't really believe in ASTs - overkill for most small languages. My hacky approach (if I cared about TR optimisation) would (I think) be to ALWAYS write the function as a loop, which in the non-TR (i.e. truly recursive) cases executes only once. But as I say, I've never tried it.
anon
@Vliet s/write/transform into/ in my previous comment.
anon
Hi Neil, Thanks again. My aim is to produce a calculator which allows function declaration, at the same time being able to transform tail recursions to loops, so that users can benefit from the speed up. Moreover, it's quite an interesting challenge, isn't it?
Viet
+1  A: 

The advice I've always heard is: Don't implement tail recursion detection if you can instead implement tail call detection. A lot more methods end with a call to another method than a call to themselves.

If your call stack is not important to the end user/developer (ie, tail call elimination in Java would negate much of the value of the Stack Traces, unless handled very cleverly), then you should look at this route instead.

Jason
+1  A: 

Suppose you have a function...

def func(args)
   #stuff
return func(otherargs)

then note that the AST will have something like return -> func -> otherargs, with some annotations about types and whatevers. When you walk it and note that there exists return F where F is the current function frame, you can transform that into PUSH ARGS, GOTO F, instead of fully forming the stack frame and so forth. You'll have to fiddle the return values yourself.

Also note this will be substantially harder if you want to walk-and-execute, instead of having a multipass system. My instinct suggests that walk-and-execute will require a lookahead.

And, no, I don't think bison will do this for you without chaining parsers. You are analyzing semantics in a context-sensitive fashion.

Paul Nathan
Thanks Nathan. Could you please elaborate more on how you did with flex + bison? Many thanks!
Viet
What I did was have a abstract base class TreeNode, which each production created in some derived class and passed it around. In this particular application(compiler), one mistake I did was to add too much information instead of creating a new tree. A buddy of mine was working on the assignment as well and used a struct with a union - that turned out to be a more flexible way to do it. (half the LoC and worked better). Anyway, it's a matter of setting the node properties and semantically analyzing that. You should get the O'reilly book on flex/bison at any rate.
Paul Nathan
+1 Thanks Paul! That would be very helpful.
Viet