4

Yacc 不允许传递对象。因为 %union 只能包含 POD 类型,所以复杂对象必须是新的并通过指针传递。如果发生语法错误,yacc 解析器将停止运行,并且对所有这些已创建对象的引用都将丢失。

我想出的唯一解决方案是所有新的对象都继承一个特定的基类,在分配时添加到容器中,如果出现错误,可以删除该容器中的所有内容。

有谁知道任何更好的 yacc 技巧来解决这个问题?

请不要告诉我选择不同的解析器。

4

4 回答 4

2

我喜欢 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 接口并在调用非终端表达式之前将自身推入堆栈。这是很多额外的代码,但它控制着对象的生命周期。

更新

表达式示例是一个糟糕的示例,因为在您减少左操作数之前您不知道该操作。尽管如此,这种技术在许多情况下仍然有效,并且只需要对表达式进行一点调整。

于 2008-09-15T18:26:43.490 回答
1

如果它适合您的项目,请考虑使用 Boehm 垃圾收集器。这样您就可以自由分配新对象并让收集器处理删除。当然,使用垃圾收集器需要权衡取舍。您将不得不权衡成本和收益。

于 2008-09-15T17:50:39.533 回答
-1

使用智能指针

或者,如果您对依赖另一个库感到不舒服,您可以随时使用C++ 标准库中的auto_ptr

于 2008-09-15T17:50:46.727 回答
-1

为什么使用不同的解析器会出现这样的问题?Bison 很容易获得,并且(至少在 linux 上)yacc 通常实现为 bison。您不需要对语法进行任何更改即可使用它(除了添加 %destructor 来解决您的问题)。

于 2008-09-16T14:53:08.360 回答