使用 Flex+Bison 识别尾递归函数并将代码转换为迭代形式
我正在编写一个能够接受新函数定义的计算器。意识到新手需要尝试斐波那契等递归函数,我希望我的计算器能够使用 Flex + Bison 识别尾递归函数,并将代码转换为迭代形式。我正在使用 Flex &野牛来完成这项工作。如果您有任何提示或想法,我热烈欢迎。谢谢!
编辑:
我们不用担心 Flex & 的 C 或 C++ 输出。野牛。主要是我想要一个想法或提示。谢谢。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
正如我在评论中所建议的,这对于词法分析器来说根本不是问题,对于解析器来说可能只是轻微的问题。如果您有一个类似这样的函数:
那么对于典型的 C 编译器来说,将由优化器/代码生成器将该递归调用转换为循环。当然,在解释性计算器中,您可以更加灵活,但我建议它仍然是应该执行尾部调用删除的最后一个进程之一,而不是第一个进程。
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:
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.
假设你有一个函数...
然后注意 AST 将具有类似 return ->; 的内容。功能-> otherargs,带有一些关于类型和其他内容的注释。当您遍历它并注意到存在 return F(其中 F 是当前函数框架)时,您可以将其转换为 PUSH ARGS、GOTO F,而不是完全形成堆栈框架等。您必须自己修改返回值。
另请注意,如果您想要边走边执行,而不是使用多通道系统,这将变得更加困难。我的直觉表明,步行并执行需要先行。
而且,不,我认为如果没有链接解析器,bison 不会为你做这件事。您正在以上下文敏感的方式分析语义。
Suppose you have a function...
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.
我经常听到的建议是:如果可以实现尾调用检测,就不要实现尾递归检测。更多的方法以调用另一个方法结束,而不是调用自身。
如果您的调用堆栈对最终用户/开发人员来说并不重要(即,Java 中的尾部调用消除将抵消堆栈跟踪的大部分价值,除非处理得非常巧妙),那么您应该改为查看此路线。
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.