Inheritance in Bison

Brad Vander Zanden

These notes give you some design hints for your parsing assignment.

I. There should be a class for each nonterminal and a subclass for
   each production. 

   Example: Given the productions P->PC | C, there should be a
      class for P and subclasses called P_PC and P_C. Given the
      production C -> x there should be a class for C and a subclass
      called C_x (although if there is only one production you might
      not define the subclass since the class can handle the production.

   Example: Given the input xxx, you would have the following parse
      tree:

                             P_PC
			      |
		   ----------------------
		   |                    |
		  P_PC			C
	           |                    |
	    -------------		x
	    |           |
	   P_C		C
	    |           |
            C           x
	    |
	    x
	 
II. Printing the Tree

   A. Each subclass should have its own version of print_production

      Example:  P_PC::print_production () {
                    print name
		    print '->'
		    print child1.get_name();
		    print child2.get_name();
		    child1.print_production();
		    child2.print_production();
		}

   B. print_production should be declared virtual because each subclass
      needs to provide its own customized version

   C. Each class should have a class variable that defines its name

      1. In C++, class variables are declared using the keyword static

         Example: class P {
	            static char *name;
		    ...
		  };

      2. Class variables are shared by all instances

      3. A class variable needs to be initialized outside the class
         declaration

	 Example: In the .cc file that defines the methods for class P, we
	 could include the statement:

	 static char *P::name = P::initialize_name("P");

      4. Methods like initialize_name can be declared as class methods using
         the static keyword:

	 Example: class P {
	            static char *initialize_name(char *);
		    ...
		  };

		  In the .cc file:
		  
		  char *P::initialize_name(char *value) {
		      // only allow the name to be initialized once
		      static initialized = false;

		      if (initialized == false) {
		        initialized = true;
		        name = new char[strlen(value) + 1];
			strcpy(name, value);
		      }
		      return name;
		  }

III. Creating the Parse Tree in Bison

   A. The nonterminals in a Bison production are labeled $$, $1, $2, ..., $n
      where $$ points to the non-terminal on the left side of the production
      and $1, $2, ..., $n point to the non-terminals on the right side of
      the production. 

      Example:   Given the production P : P C
		 $$ = the left hand side P
		 $1 = the right hand side P
		 $2 = C

      1. Each non-terminal is allowed to point to one object. Somewhere
         at the beginning of Bison you tell Bison what $$ points to. In
	 our assignment you would tell Bison that $$ points to a Node
	 class.

   B. To build the parse tree, each production should allocate the subclass
      that corresponds to that production and install the right side
      nonterminals as children. The right side nonterminals have already
      had their parse nodes created so it is a simple matter of passing
      them to the new object.

      Example: P : P C {
                $$ = new P_PC((P *) $1, (C *) $2);
	       }

	 1. The downcast is needed because the $'s have been declared
	    to point to a (Node *) but the constructor expects a (P *) and
	    a (C *). The downcasts tell the compiler that $1 points to
	    a (P *) and that $2 points to a (C *).

IV. Flattening the Tree: The productions P -> PC | C are meant to
    simulate the production P -> C+. I want your parser
    to print out P -> C+ rather than the productions 
    P -> PC and P -> C. I also want your parse tree to look like:

    this:     P                       and not this:        P
         -----------		                          / \
         |    |    |					 /   \
	 C    C    C					 P   C
	                                                / \
						       /   \
						       P   C
						       |
						       |
						       C
     
    A. This is called flattening the tree

    B. To do it, you need to define a class called P_CList that has a
       list of C's:

       class P_CList {
          CList *my_clist;
       }

       1. The P -> C production creates a P_CList object and inserts the
          first C onto the list:

	  P : C { $$ = new P_CList((C*) $1); }

       2. The P -> PC production adds its C object to the list and passes
          the P_CList object to its parent:

	  P : P C { $$ = $1;
	            ((P *)$$)->Append((C*)$2);
		  }

           a. The assignment "$$ = $1" passes the P_CList object from
	      the right side child to its left side parent.

       3. What these productions do is collect the leaves and insert them
          onto a list