Qual é a melhor maneira de prevenir vazamentos de memória em um yacc baseado no analisador?
Pergunta
Yacc não permite que objetos a serem passados ao redor.Porque o %união só pode conter tipos de POD, objetos complexos devem ser novos e passada pelo ponteiro.Se ocorre um erro de sintaxe, o yacc analisador apenas interrompe a execução e referências para todos os objetos criados são perdidas.
A única solução que eu vim acima com é que todos os novos iria herdar do objecto de uma particular classe base, ser adicionado a um recipiente quando alocados, e se houver um erro em tudo em que o recipiente pode ser excluído.
Alguém sabe de melhor yacc truques para resolver este problema?
Por favor, não me diga para escolher um diferente do analisador.
Solução
Eu amo o Yacc, mas a discriminar união pilha apresenta um desafio.
Eu não sei se você estiver usando C ou C++.Que eu tenha modificado Yacc para gerar C++ para meus próprios fins, mas esta solução pode ser adaptada para C.
A minha solução preferida é passar uma interface para o proprietário até a árvore de análise, em vez de objetos construídos a pilha.Fazer isso através da criação de sua própria pilha fora do Yacc.Antes de você chamar um não-terminal que aloca um objeto, aperte o proprietário do objeto para esta pilha.
Por exemplo:
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;
...
};
Tudo o que quer uma expressão tem para implementar o IExpressionOwner interface e empurre próprio para a pilha antes de invocar a expressão não-terminal.É um monte de código extra, mas ele controla o tempo de vida do objeto.
Atualização
A expressão é um exemplo ruim, pois você não sabe a operação até que, depois de ter reduzido o operando esquerdo.Ainda assim, essa técnica funciona em muitos casos, e requer apenas um pouco de ajustes de expressões.
Outras dicas
Se ele se adapte ao seu projeto, considere o uso de Boehm coletor de Lixo.Dessa forma, você pode livremente alocar novos objetos e deixe o coletor de lidar com o exclui.É claro que existem tradeoffs envolvidos no uso de um coletor de lixo.Você teria que pesar os custos e benefícios.
Utilização ponteiros inteligentes!
Ou, se você está desconfortável, dependendo ainda de outra biblioteca, você sempre pode usar auto_ptr a partir da C++ standard library.
Por isso é utilizando um analisador de tal problema?Bison é prontamente disponível, e (pelo menos no linux) yacc é geralmente implementado como bison.Você não deve precisar de qualquer alteração à sua gramática de uso (exceto para a adição %destruidor para resolver o seu problema).