Пользовательская функция освобождения для деструктора токенов в Lemon

Я хочу, чтобы Lemon анализировал простое C-подобное выражение, поддерживающее целочисленное и строковое сравнение предопределенного набора переменных с известными именами. Для простоты предположим, что он поддерживает только сравнение строк. Итак, следующая строка является хорошим примером выражения, о котором я говорю:

a == "literal_1" || a == "literal_2"

Итак, мой лексер должен был бы передать анализатору значения в следующем порядке:

void *p = parserAlloc(malloc);
parser(p, TOK_VARIABLE_A, NULL);
parser(p, TOK_OPERATOR_EQ, NULL);
parser(p, TOK_LITERAL, strdup("literal_1"));
parser(p, TOK_OPERATOR_OR, NULL);
parser(p, TOK_VARIABLE_A, NULL);
parser(p, TOK_OPERATOR_EQ, NULL);
parser(p, TOK_LITERAL, strdup("literal_2"));
parserFree(p, free);

Я должен сделать дубликаты литеральных строк, переданных парсеру, потому что они могут содержать escape-последовательности, которые я должен сначала декодировать. Но кто отвечает за освобождение памяти после синтаксического анализа? К счастью, на помощь приходит Lemon со своей директивой %destructor, так что я могу написать:

%token_destructor TOK_LITERAL { free($$); }

Но на самом деле я не хочу жестко запрограммировать использование malloc, strdup и free в моем парсере и лексере. Я хочу иметь возможность передавать функции распределителя и освобождения в качестве параметров, но использовать их не только в parserInit и parserFree, но и для выделения и освобождения токенов.

Как я могу объявить дополнительный параметр для parserAlloc, чтобы одновременно передавать и malloc, и free? В Lemon есть директива %extra_argument, но она заставляет меня передавать свой параметр каждый раз, когда я подаю токен.


person firegurafiku    schedule 02.04.2016    source источник


Ответы (1)


Аргумент malloc для parserAlloc нигде не хранится, потому что созданный лимоном синтаксический анализатор никогда не выделяет память. [Примечание 1] И, конечно же, функция free тоже нигде не хранится, потому что она не предоставляется до тех пор, пока вы не вызовете parserFree.

Обычно вам также не нужна функция alloc внутри действия парсера, но если вы используете %destructor/%token-destructor, вам понадобится функция free. Единственным задокументированным механизмом для этого является функция дополнительных аргументов, которая, как вы говорите, требует предоставления аргумента при каждом вызове синтаксического анализатора. Это немного раздражает, особенно потому, что синтаксический анализатор немедленно сохраняет его в структуру состояния синтаксического анализатора (т.е. первый аргумент parse), но так оно и есть. Это было бы легко изменить, и Lemon не обременен, поэтому вы можете вносить изменения, которые хотите. Но, как предусмотрено, %extra-argument — единственный способ.

Если вам по какой-либо причине нужны функции alloc и free в ваших действиях, вы можете сделать %extra-argument указателем на структуру (что на самом деле является обычным случаем для %extra-argument); структура будет содержать указатели на обе функции. В качестве альтернативы вы можете использовать функцию со стандартным интерфейсом realloc: realloc(NULL, sz) эквивалентно malloc(sz), а realloc(p, 0) эквивалентно free(p) (пока p не равно NULL). Подробнее см. man realloc. Это не будет беспокоить анализатор лимона, потому что он никогда не использует ни malloc, ни free.

Примечания

  1. Это не совсем так. Существует, насколько мне известно, недокументированная функция: если вы установите %stack-size 0, то сгенерированный парсер перераспределит стек парсера до того, как он переполнится, а не выдаст ошибку. В этом случае синтаксический анализатор использует стандартную библиотеку realloc для выделения или перераспределения стека, а не функцию malloc, предоставленную parserAlloc, и parserFree освобождает стек с помощью стандартной библиотеки free, а не функции, переданной в качестве аргумента.
person rici    schedule 02.04.2016
comment
Только что проверил проблему %stack_size 0. Действительно, он генерировал вызовы realloc, что кажется мне таким странным. Это так несовместимо с parserAlloc и parserFree. - person firegurafiku; 02.04.2016