【问题标题】:How to preprocess a C source code for pycparser如何预处理 pycparser 的 C 源代码
【发布时间】:2015-11-17 12:05:45
【问题描述】:

我需要使用 pycparser 解析我的 C 和 C++ 代码,但之前需要去除预处理器指令和 cmets。

那么,你知道有什么方法可以做到这一点吗?我找到了CPP 预处理器,但我不知道是否可以像那样使用它,而无需“完整”预处理。

我也找到了一个工具 unifdef,它似乎完全符合我的要求,但仅限于预处理器条件(例如 #ifdef)。

我不想自己编写这个工具,因为它要用于一个非常大的项目,所以我想使用一些非常复杂的东西。


我的尝试:

我试图找到地方,这段代码中调用的函数test在哪里:

#include <stdio.h>

// asdfdsa
/* sadfsd
 * 
 */

void test() {
    printf("asd");
}

int main() {

    test();

    test();

    return 0;
}

我使用命令 gcc -E -std=c99 test.c -o testP.c 预处理了这段代码,然后我尝试使用这段 Python 代码查找函数调用:

#-----------------------------------------------------------------
# pycparser: func_defs.py
#
# Using pycparser for printing out all the calls of some function
# in a C file.
#
# Copyright (C) 2008-2015, Eli Bendersky
# License: BSD
#-----------------------------------------------------------------
from __future__ import print_function
import sys

# This is not required if you've installed pycparser into
# your site-packages/ with setup.py
sys.path.extend(['.', '..'])

from pycparser import c_parser, c_ast, parse_file


# A visitor with some state information (the funcname it's
# looking for)
#
class FuncCallVisitor(c_ast.NodeVisitor):
    def __init__(self, funcname):
        self.funcname = funcname

    def visit_FuncCall(self, node):
        if node.name.name == self.funcname:
            print('%s called at %s' % (self.funcname, node.name.coord))


def show_func_calls(filename, funcname):
    ast = parse_file(filename, use_cpp=True)
    v = FuncCallVisitor(funcname)
    v.visit(ast)

if __name__ == "__main__":
    if len(sys.argv) > 2:
        filename = sys.argv[1]
        func = sys.argv[2]
    else:
        filename = 'test.c'
        func = 'test'

    show_func_calls(filename, func)

但是,我仍然收到此错误:

Traceback (most recent call last):
  File "func_calls.py", line 46, in <module>
    show_func_calls(filename, func)
  File "func_calls.py", line 33, in show_func_calls
    ast = parse_file(filename, use_cpp=True)
  File "/usr/local/lib/python2.7/dist-packages/pycparser/__init__.py", line 93, in parse_file
    return parser.parse(text, filename)
  File "/usr/local/lib/python2.7/dist-packages/pycparser/c_parser.py", line 146, in parse
    debug=debuglevel)
  File "/usr/local/lib/python2.7/dist-packages/pycparser/ply/yacc.py", line 265, in parse
    return self.parseopt_notrack(input,lexer,debug,tracking,tokenfunc)
  File "/usr/local/lib/python2.7/dist-packages/pycparser/ply/yacc.py", line 1047, in parseopt_notrack
    tok = self.errorfunc(errtoken)
  File "/usr/local/lib/python2.7/dist-packages/pycparser/c_parser.py", line 1691, in p_error
    column=self.clex.find_tok_column(p)))
  File "/usr/local/lib/python2.7/dist-packages/pycparser/plyparser.py", line 55, in _parse_error
    raise ParseError("%s: %s" % (coord, msg))
pycparser.plyparser.ParseError: /usr/lib/gcc/x86_64-linux-gnu/4.9/include/stdarg.h:40:27: before: __gnuc_va_list

【问题讨论】:

  • 所以基本上你希望所有以 # 或 /* 或 // 开头的行都被丢弃,对吗?为什么不使用 sed?
  • @schaiba 我什至想删除嵌套指令等。老实说,我以前从未使用过sed,所以我不太了解它的功能:-)
  • cpp 有什么问题?
  • 你想用这个完成什么?!预处理的源代码难以使用,因为它充斥着(通常是无礼的)宏扩展,例如 getchar() 以及由某些常量的杂乱常量表达式定义。
  • @vonbrand 看起来剥离和cpp 处理将是作为 pycparser 工具链的一部分生成交叉引用的中间步骤。

标签: c c++


【解决方案1】:

pycparser 文档特别是 says 你应该使用 cppgcc -E 来准备源代码进行解析。因此,“完整”预处理不是cpp问题,它是您在代码上运行pycparser 所需的功能。

如果您只是简单地去除所有预处理器指令的代码,解析将失败,因为未声明的类型(如int32_tsize_t)和库函数缺少原型。

编辑:pycparser 仅支持 C99 语法。如果gcc 出于某种原因将您的代码用于 C++,请像这样运行预处理器:

gcc -E -std=c99

EDIT2:您似乎遇到了更多与编译器特定符号相关的错误。尝试使用pycparser提供的"fake" headers

gcc -E -std=c99 -I/path/to/pycparser/utils/fake_libc_include

【讨论】:

  • 我已经尝试了gcc -Ecpp -E,但我收到了错误pycparser.plyparser.ParseError: /usr/include/stdio.h:29:8: before: "C"
  • 您的来源是否有.cpp 扩展名? gcc 似乎出于某种原因将您的代码视为 C++,而 pycparser 仅支持 C。
  • 我尝试用-std=c99 编译它,但结果还是一样...我的代码有.c 扩展。我在问题中更彻底地描述了我的尝试。
  • 不是相同的结果,你现在得到一个完全不同的错误信息。检查我的答案的更新。
  • 太棒了!非常感谢,“假头”终于解决了!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-26
  • 1970-01-01
  • 1970-01-01
  • 2012-12-11
  • 2011-07-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多