help-bison
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: How to correctly deallocate some token types ...


From: Hans Aberg
Subject: Re: How to correctly deallocate some token types ...
Date: Tue, 13 May 2003 11:20:25 +0200

At 09:47 +0800 2003/05/13, address@hidden wrote:
>I have the same trouble with deallocating C++ object in Bison. I think Ricardo
>just wants to confirm that if the codes he presented are enough to ensure that
>the object can be deallocated properly even when a parsing error occurs.

If you use the C stack, the %destructor is the best option that Bison has
for the cleanup. Without it, with the C stack, one has to try to keep track
of the dynamic data by other, complicated, hands on means.

> But I
>am still interested at the way Hans mentioned. Can you share an example of it,
>or some reference guides?

First, I use a C++ skeleton file that makes use of a proper C++ sequence
container (like std::vector, std::deque, etc.) for stack. I use a rewrite
of lalr1.cc plus some tweaks of Bison itself, so currently you cannot use
exactly what I have.

As a decided to use classes with non-trivial constructors, I cannot use the
%union feature. So instead I use a C++ polymorphic (virtual) class
hierarchy with a reference count. The setup is this:

class object_root {
  mutable unsigned count_;
public:
  object_root() : count_(1) {}
  virtual ~object_root() {}

  object_root* copy() const { ++count_; return
const_cast<object_root*>(this); }
  void shed() { if (--count_ == 0)  delete this; }

  virtual object_root* clone() const = 0;

};

class object {
protected:
  object_root* data_;

public:
  object_root* copy() const { return (data_ == 0)? 0 : data_->copy(); }
  void shed() { if (data_ != 0)  data_->shed(); }

  object() : data_(0) {}
  ~object() { shed(); }

  object(const object& x) : data_(x.copy()) {}
  object& operator=(const object& x) {
    if (data_ != x.data_) { shed(); data_ = x.copy(); }
    return *this;
  }

  object(object_root* rp) : data_(rp) {}

  object_root* data() { return data_; }
  const object_root* data() const { return data_; }
};

Add new classes T to the hierarchy by:

class T : public virtual object_root {
public:
  T() {}
  virtual object_root* clone() const { return new T(*this); }
};

The clone operator turns out to be quite unnecessary in a parser though, as
one usually is just building a single structure that never needs to be
cloned. It might be needed on occasion though.

I then use this is the parser as

class semantic_type {
public:
  object object_;

  semantic_type() {}
};

#define YYSTYPE semantic_type

Then the object_ can hold any object derived from the class object_root.
One can of course add more types to the semantic type, but they will be
duplicated through the stack. But std::string and a number (int) added is
often useful, because it is so frequent for the lexer to return such data.

The actions do not generally become very complicated, because one writes
functions that via a suitable dynamic_cast<T&>(...) extracts the desired
dynamic object. If there then is a type error, the parser will throw an
exception. SO one will have to wrap the parser in a clause
  try {
    // parser
  catch (...) {
    // Write out exception.
  }
If there are some type errors in the .y sources, one will find out when
running the parser. So, for now, in the wait for better Bison C++ typing
support, that seems to be an acceptable approach.

  Hans Aberg






reply via email to

[Prev in Thread] Current Thread [Next in Thread]