Каков наилучший способ предотвращения утечек памяти в парсере на основе yacc? - PullRequest
4 голосов
/ 15 сентября 2008

Yacc не разрешает передавать объекты. Поскольку% union может содержать только типы POD, сложные объекты должны быть новыми и передаваться по указателю. Если возникает синтаксическая ошибка, анализатор yacc просто прекращает работу, и ссылки на все эти созданные объекты теряются.

Единственное решение, которое я нашел, состоит в том, что все new'd-объект наследуют определенный базовый класс, добавляются в контейнер при выделении, и в случае ошибки все в этом контейнере можно удалить.

Кто-нибудь знает какие-нибудь лучшие трюки yacc для решения этой проблемы?

Пожалуйста, не говорите мне выбирать другой парсер.

Ответы [ 4 ]

2 голосов
/ 15 сентября 2008

Мне нравится Yacc, но стек различающихся объединений представляет собой проблему.

Я не знаю, используете ли вы C или C ++. Я изменил Yacc для генерации C ++ для своих собственных целей, но это решение можно адаптировать для C.

Мое предпочтительное решение - передавать интерфейс владельцу по дереву разбора, а не создавать объекты в стеке. Сделайте это, создав собственный стек вне Yacc. Прежде чем вызывать нетерминал, который выделяет объект, поместите владельца этого объекта в этот стек.

Например:

class IExpressionOwner
{
public:
    virtual ExpressionAdd *newExpressionAdd() = 0;
    virtual ExpressionSubstract *newExpressionSubtract() = 0;
    virtual ExpressionMultiply *newExpressionMultiply() = 0;
    virtual ExpressionDivide *newExpressionDivide() = 0;
};

class ExpressionAdd : public Expression, public IExpressionOwner
{
private:
    std::auto_ptr<Expression> left;
    std::auto_ptr<Expression> right;

public:
    ExpressionAdd *newExpressionAdd()
    {
        ExpressionAdd *newExpression = new ExpressionAdd();
        std::auto_ptr<Expression> autoPtr(newExpression);
        if (left.get() == NULL)
            left = autoPtr;
        else
            right = autoPtr;
        return newExpression;
    }

    ...
};

class Parser
{
private:
    std::stack<IExpressionOwner *> expressionOwner;

    ...
};

Все, что хочет выражение, должно реализовывать интерфейс IExpressionOwner и помещать себя в стек перед вызовом выражения нетерминально. Это много лишнего кода, но он управляет временем жизни объекта.

Обновление

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

1 голос
/ 15 сентября 2008

Если это подходит вашему проекту, рассмотрите возможность использования сборщика мусора Boehm. Таким образом, вы можете свободно размещать новые объекты и позволять сборщику обрабатывать удаления. Конечно, при использовании сборщика мусора существуют компромиссы. Вам придется взвесить затраты и выгоды.

0 голосов
/ 16 сентября 2008

Почему использование другого парсера является такой проблемой? Бизон легко доступен, и (по крайней мере, в Linux) yacc обычно реализован как бизон. Вам не нужно вносить изменения в свою грамматику, чтобы использовать ее (за исключением добавления% destructor для решения вашей проблемы).

0 голосов
/ 15 сентября 2008

Использование умных указателей !

Или, если вам неудобно зависеть от другой библиотеки, вы всегда можете использовать auto_ptr из стандартной библиотеки C ++.

...