0

我正在使用 pycparser 解析一些在解析之前无法使用 cpp 编译的 C 代码,因此我使用以下函数手动剥离所有注释和预处理器指令:

def remove_comments(text):
    def replacer(match):
        s = match.group(0)
        if s.startswith('/') or s.startswith('#'):
            return ""
        else:
            return s

    pattern = re.compile(
        r'#.*?$|//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
        re.DOTALL | re.MULTILINE
    )
    return re.sub(pattern, replacer, text)

这是示例中memmgr.c文件中此函数的输出:

typedef ulong Align;

union mem_header_union
{
    struct 
    {


        union mem_header_union* next;



        ulong size; 
    } s;



    Align align_dummy;
};

typedef union mem_header_union mem_header_t;



static mem_header_t base;



static mem_header_t* freep = 0;



static byte pool[POOL_SIZE] = {0};
static ulong pool_free_pos = 0;


void memmgr_init()
{
    base.s.next = 0;
    base.s.size = 0;
    freep = 0;
    pool_free_pos = 0;
}


static mem_header_t* get_mem_from_pool(ulong nquantas)
{
    ulong total_req_size;

    mem_header_t* h;

    if (nquantas < MIN_POOL_ALLOC_QUANTAS)
        nquantas = MIN_POOL_ALLOC_QUANTAS;

    total_req_size = nquantas * sizeof(mem_header_t);

    if (pool_free_pos + total_req_size <= POOL_SIZE)
    {
        h = (mem_header_t*) (pool + pool_free_pos);
        h->s.size = nquantas;
        memmgr_free((void*) (h + 1));
        pool_free_pos += total_req_size;
    }
    else
    {
        return 0;
    }

    return freep;
}










void* memmgr_alloc(ulong nbytes)
{
    mem_header_t* p;
    mem_header_t* prevp;





    ulong nquantas = (nbytes + sizeof(mem_header_t) - 1) / sizeof(mem_header_t) + 1;




    if ((prevp = freep) == 0)
    {
        base.s.next = freep = prevp = &base;
        base.s.size = 0;
    }

    for (p = prevp->s.next; ; prevp = p, p = p->s.next)
    {

        if (p->s.size >= nquantas) 
        {

            if (p->s.size == nquantas)
            {



                prevp->s.next = p->s.next;
            }
            else 
            {
                p->s.size -= nquantas;
                p += p->s.size;
                p->s.size = nquantas;
            }

            freep = prevp;
            return (void*) (p + 1);
        }



        else if (p == freep)
        {
            if ((p = get_mem_from_pool(nquantas)) == 0)
            {

                printf("!! Memory allocation failed !!\n");

                return 0;
            }
        }
    }
}

但我得到了这个 ParseError:

pycparser.plyparser.ParseError: :1:15: before: Align

pycparser 有什么问题?

4

2 回答 2

1

C 预处理器不仅去除注释。它还处理所有宏,包括#include拉入头文件的宏。在你的情况下,它正在尝试#include "memmgr.h",它有这个typedef

typedef unsigned long ulong;

和其他一些人一起。

底线是 - 您应该在 pycparser 之前调用预处理器。如果您有有效的 C 代码,则预处理器没有理由不工作。C这可能是标签中单独的 SO 问题的主题。

于 2015-09-12T13:18:03.070 回答
1

我想typedef unsigned long ulong;在某些包含的文件中有一个。如果没有该声明,ulong则不能出现在语法需要类型名的地方。

尝试ulong在第一次使用之前添加 , 的声明。


更具体地说,关于这个问题:“pycparser有什么问题?”:

pycparser 的目标是解析 C 程序。它不是近似解析器;它实际上旨在生成对任何有效 C99 程序的完整、准确的解析。

不幸的是,如果不知道哪些标识符是类型名,就不可能准确地解析 C 程序。不需要知道标识符的确切类型,因此 pycparser 不需要访问所有原型和全局定义;但是,它确实需要访问所有相关typedef的 s。

这在pycparser 的自述文件的第 3.2 节中有记录,该文件指向作者网站上的更长讨论

这里要理解的关键点是 pycparser 并不真正关心类型的语义。它只需要知道源中遇到的某个token是否是先前定义的类型。为了能够正确解析 C,这是必不可少的。

正如 Eli 所建议的,最好的办法是只收集要分析的代码使用的 typedef,并将它们插入代码的开头。他们可能不会太多

Eli Bendersky 的文章非常出色,值得一读。让我只提供几个 C 代码示例,如果不知道名称是否为 typedef,就无法解析这些代码。

我认为经典的例子是众所周知的:

(foo) - 3 * 4

这个表达式有两种可能的解析,只有一种可以应用于任何给定的程序。左边,解析 iffoo是一个变量;在右边,解析 iffoo是一个类型:

    -                              *
   / \                            / \
  /   \                          /   \
foo    *                       cast   4
      / \                      /  \
     /   \                    /    \
    3     4                 foo     -   
                                    |
                                    |
                                    3

换句话说,如果foo是一个变量,则表达式从 中3*4减去foo。但如果foo是类型,则表达式转换-3为类型foo,然后将结果乘以 4`。

显然,这个问题所源自的特定应用实际上并不需要关于每个表达式的解析的详细知识。但是没有办法将这个事实传达给 pycparser;pycparser 旨在提供完整的解析。

在任何情况下,都可以构建一个可能更相关的例子。考虑两个语句(不能出现在同一个翻译单元中):

foo (*bar()) ();

foo (*bar()) ();

尽管它们相似(:-)),但这两个陈述完全不同。第一个声明了一个名为 的函数bar。第二个调用了一个名为 的函数foo,在调用了一个名为 的函数bar来计算 的参数之后foo

即使您只是收集声明,了解该声明是否是声明也很重要。(是的,这种结构非常罕见;它可能不会出现在正在分析的代码库中的任何地方。但是 pycparser 是如何知道的呢?)

以下是完整的上下文:

#include <stdio.h>                 | #include <stdio.h>
typedef int foo;                   |
int answer() { return 42; }        | int answer() { return 42; }
                                   |
                                   | int (*foo(int a)) () {
                                   |   printf("%d\n", a);
                                   |   return answer;
                                   | }
                                   |
                                   | static const int unused = 43;
int (*bar()) () { return answer; } | int const* bar() { return &unused; }
                                   |
int main() {                       | int main() {
  /* Declare bar */                |  /* Call foo */
  foo (*bar()) ();                 |  foo (*bar()) ();
  printf("%d\n", bar()());         |  return 0;
  return 0;                        | }
}                                  |

于 2015-09-11T19:45:18.360 回答