pycparser ParseError с typedef

Я использую pycparser для синтаксического анализа кода C, который не могу скомпилировать с помощью cpp перед анализом, поэтому я вручную удаляю все комментарии и директивы препроцессора с помощью следующей функции:

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?

2 ответа

Я полагаю, что был typedef unsigned long ulong; в каком-то включенном файле. Без этого заявления ulong не может появиться там, где грамматика требует имени типа.

Попробуйте добавить объявление ulong где-то до его первого использования.


Чтобы более конкретно о вопросе: "Что не так с pycparser?":

Цель pycparser - анализ программ на C. Это не приблизительный парсер; фактически он нацелен на создание полного и точного анализа любой действительной программы C99.

К сожалению, невозможно точно проанализировать программу на C, не зная, какие идентификаторы являются типичными. Нет необходимости знать точный тип идентификатора, поэтому pycparser не требует доступа ко всем прототипам и глобальным определениям; однако он требует доступа ко всем соответствующим typedef s.

Это описано в разделе 3.2 файла readme для pycparser, который указывает на более длительное обсуждение на веб-сайте автора:

Ключевой момент, который необходимо понять, заключается в том, что pycparser на самом деле не заботится о семантике типов. Нужно только знать, относится ли какой-либо токен, встречающийся в источнике, к ранее определенному типу. Это важно для правильного анализа C.

По предложению Eli, вам лучше всего собрать только определения типов, используемые кодом, который вы хотите проанализировать, и вставить их в начале кода. Вероятно, их не так уж много.

Эссе Эли Бендерского отлично, и его стоит прочитать. Позвольте мне привести пару примеров кода на C, который нельзя проанализировать, не зная, является ли имя typedef или нет.

Классический пример, я думаю, хорошо известен:

(foo) - 3 * 4

Это выражение имеет два возможных анализа, только один из которых может применяться в любой данной программе. Слева разбор если foo переменная; справа разбор если foo это тип:

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

Другими словами, если foo является переменной, выражение вычитает 3*4 от foo, Но если foo это тип, выражение бросает -3 печатать foo а затем умножает результат на 4`.

Очевидно, что конкретное приложение, из которого был составлен этот вопрос, на самом деле не требует детальных знаний о разборе каждого выражения. Но нет способа сообщить об этом факте Pycparser; Pycparser's предназначен для обеспечения полного анализа.

В любом случае, возможно построить более подходящий пример. Рассмотрим два утверждения (которые не могут появляться в одной и той же единице перевода):

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;                        | }
}                                  |

Препроцессор C не только удаляет комментарии. Он также обрабатывает все макросы, в том числе #include, который тянет в заголовочных файлах. В вашем случае он пытается #include "memmgr.h", который имеет это typedef:

typedef unsigned long ulong;

Наряду с некоторыми другими.

Суть в том, что вы должны вызывать препроцессор перед pycparser. Если у вас есть действительный C-код, нет никаких причин, по которым препроцессор не должен работать. Это может быть тема для отдельного вопроса SO в C тег.

Другие вопросы по тегам