在Python中使用正则表达式确定C++函数及其参数
我在这个Python脚本中做错了什么,但事情变得复杂,我开始搞不清楚自己哪里出错了。
我想写一个脚本,去读取一个文件,找到所有的函数定义,然后提取出函数的名称、返回类型和参数,最后输出一个“doxygen”风格的注释,像这样:
/******************************************************************************/
/*!
\brief
Main function for the file
\return
The exit code for the program
*/
/******************************************************************************/
但是我在用正则表达式解析参数时遇到了问题……到目前为止,脚本是这样的:
import re
import sys
f = open(sys.argv[1])
functions = []
for line in f:
match = re.search(r'([\w]+)\s+([\S]+)\(([\w+\s+\w+])+\)',line)
if line.find("\\fn") < 0:
if match:
returntype = match.group(1)
funcname = match.group(2)
print '/********************************************************************'
print " \\fn " + match.group()
print ''
print ' \\brief'
print ' Function description for ' + funcname
print ''
if len(match.groups()) > 2:
params = []
count = len(match.groups()) - 2
while count > 0:
matchingstring = match.group(count + 2)
if matchingstring.find("void") < 0:
params.append(matchingstring)
count -= 1
for parameter in params:
print " \\param " + parameter
print ' Description of ' + parameter
print ''
print ' \\return'
print ' ' + returntype
print '********************************************************************/'
print ''
任何帮助都非常感谢。谢谢!
3 个回答
C++ 语言的结构复杂得让人头疼,特别是当涉及到嵌套的时候,普通的正则表达式根本无法处理。
还有一个问题,就是要搞清楚什么时候需要解析代码,什么时候不需要。一个函数可以在以下地方声明:
- 在文件的全局范围内
- 在一个命名空间里
- 在一个类里面
而后两种情况可以嵌套得非常深。
我建议使用 CLang。它是一个真正的 C++ 前端,有一个功能齐全的解析器,并且提供了:
- 一个 C 语言的 API,特别是有一个用于索引库的 API
- 在 C API 基础上的 Python 绑定
虽然 C API 和 Python 绑定并没有完全展现底层的 C++ 模型,但对于像列出函数这样简单的任务来说,应该是足够的。
不过,我对这个项目的实用性表示怀疑:如果文档可以通过一个简单的解析器生成,那它和代码就重复了。而重复的东西,最多是没用,最糟糕的是会带来危险:可能会导致不同步的问题……
如果一个函数复杂到需要文档来解释,那么开发者就应该自己写这份文档,因为他们了解这个函数的局限性等等。
我用过一种叫做PEG解析器的工具,它在处理简单格式解析时效果很好。pyPeg是一个用Python写的非常简单的这种解析器的实现。
下面是一个用Python写的解析C++函数的示例代码:
编辑:处理模板参数。用SK-logic的输入测试过,输出结果是正确的。
import pyPEG
from pyPEG import parseLine
import re
def symbol(): return re.compile(r"[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ&*][\w:]+")
def type(): return symbol
def functionName(): return symbol
def templatedType(): return symbol, "<", -1, [templatedType, symbol, ","], ">"
def parameter(): return [templatedType, type], symbol
def template(): return "<", -1, [symbol, template], ">"
def function(): return [type, templatedType], functionName, -1, template, "(", -1, [",", parameter], ")" # -1 -> zero or more repetitions.
sourceCode = "std::string foobar(std::vector<int> &A, std::map<std::string, std::vector<std::string> > &B)"
results = parseLine(sourceCode, function(), [], packrat=True)
当执行这个代码时,结果是:
([(u'type', [(u'symbol', 'std::string')]), (u'functionName', [(u'symbol', 'foobar')]), (u'parameter', [(u'templatedType', [(u'symbol', 'std::vector'), (u'symbol', 'int')]), (u'symbol', '&A')]), (u'parameter', [(u'templatedType', [(u'symbol', 'std::map'), (u'symbol', 'std::string'), (u'templatedType', [(u'symbol', 'std::vector'), (u'symbol', 'std::string')])]), (u'symbol', '&B')])], '')
C++的语法太复杂,简单的正则表达式根本无法处理。你至少需要一个基本的解析器。我发现对于一些特定的情况,如果我只关注自己的编程风格,而不考虑C++的整体语法,使用基于flex的词法分析器和一个简单的状态机通常就够用了。不过,这种方法在很多合法的C++代码中会出问题——比如,如果有人使用预处理器来修改语法;还有就是,<
这个符号的意思会根据前面的内容不同而有所变化,它可能表示一个模板。尽管如此,这种方法在特定的任务中通常是足够的。