What is the best way of preventing memory leaks in a yacc-based parser?

不羁的心 提交于 2019-12-07 07:49:40

问题


Yacc does not permit objects to be passed around. Because the %union can only contain POD types, complex objects must be new'd and passed around by pointer. If a syntax error occurs, the yacc parser just stops running, and references to all of those created objects are lost.

The only solution I've come up with is that all new'd object inherit a particular base class, be added to a container when allocated, and if there is an error everything in that container can be deleted.

Does anyone know of any better yacc tricks to solve this problem?

Please don't tell me to choose a different parser.


回答1:


I love Yacc, but the discriminating union stack does present a challenge.

I don't know whether you are using C or C++. I've modified Yacc to generate C++ for my own purposes, but this solution can be adapted to C.

My preferred solution is to pass an interface to the owner down the parse tree, rather than constructed objects up the stack. Do this by creating your own stack outside of Yacc's. Before you invoke a non-terminal that allocates an object, push the owner of that object to this stack.

For example:

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;

    ...
};

Everything that wants an expression has to implement the IExpressionOwner interface and push itself to the stack before invoking the expression non-terminal. It's a lot of extra code, but it controls object lifetime.

Update

The expression example is a bad one, since you don't know the operation until after you've reduced the left operand. Still, this technique works in many cases, and requires just a little tweaking for expressions.




回答2:


If it suits your project, consider using the Boehm Garbage collector. That way you can freely allocate new objects and let the collector handle the deletes. Of course there are tradeoffs involved in using a garbage collector. You would have to weigh the costs and benefits.




回答3:


Use smart pointers!

Or, if you're uncomfortable depending on yet another library, you can always use auto_ptr from the C++ standard library.




回答4:


Why is using a different parser such a problem? Bison is readily available, and (at least on linux) yacc is usually implemented as bison. You shouldn't need any changes to your grammar to use it (except for adding %destructor to solve your issue).



来源:https://stackoverflow.com/questions/64958/what-is-the-best-way-of-preventing-memory-leaks-in-a-yacc-based-parser

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!