I had code like this:
Node *Term(CompileData &Data,bool Get){ Node *N=Prim(Data,Get); while(true) { switch(Data.Sc.Type) { case scMul: N=new BinaryNode(bnMul,N,Prim(Data,true)); break; case scDiv: N=new BinaryNode(bnDiv,N,Prim(Data,true)); break; default: return N; } }}
Obviously, Prim() can be the source of many exceptions, thrown by the scanner in the case of an illegal character, an undefined symbol thrown from the symbol table, a parse error thrown by Prim() itself, etc etc.
I put a debugging counter in that is incremented by Node's constructor and decremented by its destructor and sure enough, if I ran a script with errors in, at program exit there were a bunch of undeleted nodes.
Fine in the driver program, but in game, I may not necessarily want to terminate the whole game if a script contains errors.
Solutions has been to whack together a reference-counting smart pointer and use that throughout the expression parser:
ref_ptr Term(CompileData &Data,bool Get){ ref_ptr N=Prim(Data,Get); while(true) { switch(Data.Sc.Type) { case scMul: N=new BinaryNode(bnMul,N,Prim(Data,true)); break; case scDiv: N=new BinaryNode(bnDiv,N,Prim(Data,true)); break; default: return N; } }}
ref_ptr<> is just a little template class that is a bit less safe than boost::shared_ptr, but a bit more lax in syntax (e.g. can assign a new expression directly rather than through an explicit.
It might end up leaking if you run out of memory - still don't really understand all that properly, but whatever I throw at the compiler now, the NodeCounter is always zero after the call to Expr::Gen() or Expr::Calc().
I'll post a more detailed description of the scripting language soon - just want to get a few more fundamental bits working.